//! aura-transport — the Aura VPN's QUIC transport with HTTP/3 traffic mimicry (project §7). //! //! This crate carries the Aura protocol over real QUIC and exposes an established connection as an //! [`aura_proto::PacketConnection`]. It has two layers, and which one is the security boundary is //! the key design point: //! //! * **Outer = QUIC/TLS mimicry.** The connection is dressed up to look like ordinary browser //! HTTP/3: ALPN `h3`/`h3-29` and Chrome-like transport parameters (see [`mimicry`]). The outer //! TLS is **not** the real authentication — so the QUIC client accepts *any* server certificate //! ([`quic::AcceptAnyServerCert`]). A passive observer sees what looks like a CDN connection. //! * **Inner = the Aura proto handshake.** Over a single bidirectional QUIC stream we run //! [`aura_proto::client_handshake`] / [`aura_proto::server_handshake`], which perform the hybrid //! post-quantum key agreement and **mutual X.509** verification against the Aura CA. *This* is the //! authentication and the source of the session keys. //! //! ## Layout (project §7) //! * [`quic`] — quinn endpoint/config setup and the dangerous outer-TLS verifier. //! * [`mimicry`] — ALPN/SNI constants and [`mimicry::chrome_quic_transport_config`]. //! * [`padding`] — [`padding::pad_to_https_size`] / [`padding::inject_padding_frames`] traffic shaping. //! * [`conn`] — [`AuraConnection`], the [`aura_proto::PacketConnection`] implementation. //! * [`udp`] — an alternative backend that carries Aura's *own* protocol over **plain UDP** //! (no QUIC, no outer TLS): [`UdpServer`] / [`UdpClient`] / [`UdpConnection`]. The Aura PQ //! handshake runs over a small DTLS-flight-style reliability adapter; application packets then ride //! as unreliable explicit-nonce AEAD datagrams. This is the security-equivalent of the QUIC path //! (the inner Aura handshake is the only authentication either way), minus the HTTP/3 disguise. //! //! ## Usage (Wave 4 / CLI) //! ```no_run //! # async fn demo( //! # ca_cert_pem: String, server_cert_pem: String, server_key_pem: String, //! # client_cert_pem: String, client_key_pem: String, server_name: String, //! # ) -> anyhow::Result<()> { //! use aura_transport::{AuraServer, AuraClient, PacketConnection}; //! use aura_proto::{ServerConfig, ClientConfig}; //! //! // Server: bind, then accept authenticated connections in a loop. //! let server = AuraServer::bind( //! "0.0.0.0:4433".parse()?, //! &server_cert_pem, // outer QUIC cert (may equal the Aura server cert) //! &server_key_pem, //! ServerConfig { //! ca_cert_pem: ca_cert_pem.clone(), //! server_cert_pem: server_cert_pem.clone(), //! server_key_pem: server_key_pem.clone(), //! }, //! )?; //! let server_conn = server.accept().await?; // -> AuraConnection //! //! // Client: connect to the server's address with a camouflage SNI. //! let client_conn = AuraClient::connect( //! "203.0.113.10:4433".parse()?, //! "cdn.example.com", // outer SNI (mimicry) //! ClientConfig { ca_cert_pem, client_cert_pem, client_key_pem, server_name }, //! ).await?; //! //! // Either side: use it as a packet pipe (also works behind Arc). //! client_conn.send_packet(b"\x45\x00 ...ip packet... ").await?; //! let pkt = server_conn.recv_packet().await?; //! # let _ = pkt; Ok(()) //! # } //! ``` #![forbid(unsafe_code)] #![warn(missing_docs)] pub mod conn; pub mod dial; pub mod mimicry; pub mod padding; pub mod quic; pub mod tcp; pub mod udp; pub use conn::AuraConnection; pub use dial::{ dial, Accepted, DialConfig, Endpoints, MultiServer, TransportMode, DEFAULT_PORT_SCAN_MAX, }; pub use mimicry::{alpn_protocols, chrome_quic_transport_config, ALPN_H3, DEFAULT_SNI}; pub use padding::{ inject_padding_frames, next_bucket_for_profile, pad_to_bucket, pad_to_https_size, HTTPS_SIZE_BUCKETS, PADDING_PROFILES, }; pub use quic::{client_endpoint, server_endpoint, AcceptAnyServerCert}; pub use tcp::{TcpClient, TcpConnection, TcpOpts, TcpServer, DEFAULT_TCP_ALPN}; pub use udp::{knock_for_minute, UdpClient, UdpConnection, UdpOpts, UdpServer, KNOCK_LEN}; // Re-export the inner proto trait so downstream crates (the CLI) can name the connection as // `Arc` without a separate `aura_proto` import. pub use aura_proto::PacketConnection; use std::net::SocketAddr; use std::sync::Arc; use aura_proto::{client_handshake, server_handshake, ClientConfig, ServerConfig}; use thiserror::Error; /// Errors produced by the Aura transport layer. #[derive(Debug, Error)] pub enum TransportError { /// A PEM blob (certificate or private key) could not be parsed. #[error("PEM parse error: {0}")] Pem(String), /// Building or converting a rustls/quic TLS configuration failed. #[error("TLS configuration error: {0}")] Tls(String), /// Binding, connecting, or operating the quinn endpoint failed (includes the QUIC handshake). #[error("QUIC transport error: {0}")] Quic(String), /// The inner Aura protocol handshake failed. #[error("Aura handshake error: {0}")] Handshake(#[from] aura_proto::ProtoError), /// A generic I/O error (e.g. binding the UDP socket). #[error("I/O error: {0}")] Io(#[from] std::io::Error), } // quinn's connect/handshake errors are distinct types; fold them into one transport error. impl From for TransportError { fn from(e: quinn::ConnectError) -> Self { TransportError::Quic(format!("connect: {e}")) } } impl From for TransportError { fn from(e: quinn::ConnectionError) -> Self { TransportError::Quic(format!("connection: {e}")) } } /// An Aura VPN server: a bound QUIC endpoint that accepts authenticated [`AuraConnection`]s. /// /// Each [`accept`](AuraServer::accept) performs the outer QUIC accept, opens the inner bidirectional /// stream, runs [`aura_proto::server_handshake`] (mutual auth against the CA), and returns a ready /// [`AuraConnection`]. pub struct AuraServer { endpoint: quinn::Endpoint, proto_cfg: Arc, } impl AuraServer { /// Bind a server on `addr`. /// /// * `addr` — UDP address to listen on; use `..:0` for an OS-assigned port and read it back with /// [`AuraServer::local_addr`]. /// * `outer_cert_pem` / `outer_key_pem` — the **outer** QUIC/TLS (mimicry) certificate and key. /// These may be the same PEM as the Aura server cert in `proto_cfg` (and typically are). /// * `proto_cfg` — the inner Aura handshake config (CA + server leaf cert/key) used to mutually /// authenticate each client. /// /// # Errors /// Returns [`TransportError`] if the certs/keys are unparsable or the UDP socket cannot bind. pub fn bind( addr: SocketAddr, outer_cert_pem: &str, outer_key_pem: &str, proto_cfg: ServerConfig, ) -> Result { let endpoint = quic::server_endpoint(addr, outer_cert_pem, outer_key_pem)?; Ok(Self { endpoint, proto_cfg: Arc::new(proto_cfg), }) } /// The local address (including the OS-assigned port) this server is bound to. /// /// # Errors /// Returns [`TransportError::Io`] if the underlying socket address cannot be read. pub fn local_addr(&self) -> Result { Ok(self.endpoint.local_addr()?) } /// Accept the next client: outer QUIC handshake, then the inner Aura mutual-auth handshake. /// /// Returns a ready [`AuraConnection`] whose [`peer_id`](AuraConnection::peer_id) is the verified /// client Common Name. Call this in a loop (optionally spawning a task per connection). /// /// # Errors /// Returns [`TransportError`] if the endpoint is closed, the QUIC handshake fails, or the inner /// Aura handshake fails (e.g. the client's certificate does not verify against the CA). pub async fn accept(&self) -> Result { let incoming = self .endpoint .accept() .await .ok_or_else(|| TransportError::Quic("endpoint closed".into()))?; let connection = incoming.await?; // The client opens the bidi stream by writing the first ClientHello byte; accept it. let (send, recv) = connection.accept_bi().await?; // proto reader = RecvStream, proto writer = SendStream. let session = server_handshake(recv, send, &self.proto_cfg).await?; Ok(AuraConnection::from_session(session)) } /// Access the underlying quinn endpoint (e.g. for graceful shutdown via `close`/`wait_idle`). #[must_use] pub fn endpoint(&self) -> &quinn::Endpoint { &self.endpoint } } /// An Aura VPN client entry point. pub struct AuraClient; impl AuraClient { /// Connect to an Aura server at `server_addr`, presenting `sni` as the outer (mimicry) hostname. /// /// Performs the outer QUIC connect (accepting any server cert — see crate docs), opens a single /// bidirectional stream, and runs [`aura_proto::client_handshake`] for hybrid-PQ key agreement /// and mutual X.509 auth using `proto_cfg`. /// /// * `server_addr` — the server's UDP socket address. /// * `sni` — the Server Name Indication to present on the outer TLS (camouflage, e.g. /// `"cdn.example.com"`); this is independent of `proto_cfg.server_name`, which is the name /// verified *inside* the Aura handshake against the server's real certificate. /// * `proto_cfg` — CA + client leaf cert/key + expected server name for the inner handshake. /// /// # Errors /// Returns [`TransportError`] if the QUIC connect/handshake fails or the inner Aura handshake /// fails (e.g. the server cert does not chain to the CA or its SAN does not match /// `proto_cfg.server_name`). pub async fn connect( server_addr: SocketAddr, sni: &str, proto_cfg: ClientConfig, ) -> Result { let endpoint = quic::client_endpoint()?; let connection = endpoint.connect(server_addr, sni)?.await?; // open_bi() reserves the stream; the first write (the ClientHello inside the handshake) // actually opens it on the wire. let (send, recv) = connection.open_bi().await?; let session = client_handshake(recv, send, &proto_cfg).await?; Ok(AuraConnection::from_session(session)) } }