feat(transport,tunnel): implement Wave 3 — QUIC transport + split-tunnel router
aura-transport: quinn 0.11 endpoint with HTTP/3 mimicry (ALPN h3/h3-29, Chrome-like transport params), outer-TLS accept-any (real auth is the inner Aura handshake), packet padding to HTTPS sizes; AuraServer/AuraClient drive the proto handshake over a QUIC bidi stream; AuraConnection impls aura_proto::PacketConnection (full-duplex via Session::split + per-half mutex). 14 tests incl. a real-QUIC loopback end-to-end (crypto+pki+proto+transport). aura-tunnel: RouteTable (longest-prefix split-tunnel classify), AuraDns (hickory) host-route registration, AuraRouter over a PacketIo TUN seam + Arc<dyn PacketConnection>, AuraTun (tun 0.8 unix; wintun cfg-gated Windows). 10 tests (route classify/priority, dst-IP parse, mock router). send_direct is a v1 stub. Whole workspace: tests green, clippy clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
//! End-to-end integration test over genuine QUIC on loopback.
|
||||
//!
|
||||
//! Proves that aura-crypto + aura-pki + aura-proto + aura-transport integrate: we mint a real CA,
|
||||
//! issue a server cert (SAN "localhost") and a client cert, bind an [`AuraServer`] on an
|
||||
//! OS-assigned loopback port, connect an [`AuraClient`] (with a camouflage SNI distinct from the
|
||||
//! verified server name), run accept + connect concurrently, then push packets both directions
|
||||
//! through the [`PacketConnection`] API and assert payload integrity.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use aura_pki::AuraCa;
|
||||
use aura_proto::{ClientConfig, PacketConnection, ServerConfig};
|
||||
use aura_transport::{AuraClient, AuraConnection, AuraServer};
|
||||
|
||||
/// The DNS name baked into the server cert SAN and verified by the inner Aura handshake.
|
||||
const SERVER_NAME: &str = "localhost";
|
||||
/// A deliberately different outer SNI, to prove the mimicry hostname is independent of the
|
||||
/// inner-verified server name.
|
||||
const CAMOUFLAGE_SNI: &str = "cdn.example.com";
|
||||
|
||||
#[tokio::test]
|
||||
async fn end_to_end_quic_loopback() {
|
||||
// --- PKI: CA + server cert + client cert ------------------------------------------------
|
||||
let ca = AuraCa::generate("Aura Test CA").expect("generate CA");
|
||||
let server_cert = ca
|
||||
.issue_server_cert(SERVER_NAME)
|
||||
.expect("issue server cert");
|
||||
let client_cert = ca
|
||||
.issue_client_cert("client-001")
|
||||
.expect("issue client cert");
|
||||
let ca_pem = ca.ca_cert_pem();
|
||||
|
||||
let server_cfg = ServerConfig {
|
||||
ca_cert_pem: ca_pem.clone(),
|
||||
server_cert_pem: server_cert.cert_pem.clone(),
|
||||
server_key_pem: server_cert.key_pem.clone(),
|
||||
};
|
||||
let client_cfg = ClientConfig {
|
||||
ca_cert_pem: ca_pem.clone(),
|
||||
client_cert_pem: client_cert.cert_pem.clone(),
|
||||
client_key_pem: client_cert.key_pem.clone(),
|
||||
server_name: SERVER_NAME.to_string(),
|
||||
};
|
||||
|
||||
// --- Bind the server on 127.0.0.1:0 and read the OS-assigned port -----------------------
|
||||
// The outer QUIC (mimicry) cert reuses the Aura server cert PEM, as the brief suggests.
|
||||
let server = AuraServer::bind(
|
||||
"127.0.0.1:0".parse().unwrap(),
|
||||
&server_cert.cert_pem,
|
||||
&server_cert.key_pem,
|
||||
server_cfg,
|
||||
)
|
||||
.expect("bind server");
|
||||
let server_addr = server.local_addr().expect("server local_addr");
|
||||
assert_ne!(server_addr.port(), 0, "OS should assign a real port");
|
||||
|
||||
// --- Run accept + connect concurrently --------------------------------------------------
|
||||
let accept_task = tokio::spawn(async move { server.accept().await });
|
||||
let connect_task =
|
||||
tokio::spawn(
|
||||
async move { AuraClient::connect(server_addr, CAMOUFLAGE_SNI, client_cfg).await },
|
||||
);
|
||||
|
||||
let server_conn: AuraConnection = accept_task
|
||||
.await
|
||||
.expect("accept task join")
|
||||
.expect("server accept");
|
||||
let client_conn: AuraConnection = connect_task
|
||||
.await
|
||||
.expect("connect task join")
|
||||
.expect("client connect");
|
||||
|
||||
// The mutual-auth handshake should have established peer identities both ways.
|
||||
assert_eq!(
|
||||
server_conn.peer_id(),
|
||||
Some("client-001"),
|
||||
"server should learn the client's verified CN"
|
||||
);
|
||||
|
||||
// Share both ends as trait objects, proving `Arc<dyn PacketConnection>` usability.
|
||||
let server_conn: Arc<dyn PacketConnection> = Arc::new(server_conn);
|
||||
let client_conn: Arc<dyn PacketConnection> = Arc::new(client_conn);
|
||||
|
||||
// --- Client -> Server: several packets, assert integrity --------------------------------
|
||||
let c2s: Vec<Vec<u8>> = vec![
|
||||
b"hello server".to_vec(),
|
||||
vec![0u8; 1500], // larger-than-bucket payload
|
||||
(0..=255u8).collect(), // every byte value
|
||||
b"".to_vec(), // empty packet
|
||||
];
|
||||
for pkt in &c2s {
|
||||
client_conn.send_packet(pkt).await.expect("client send");
|
||||
let got = server_conn.recv_packet().await.expect("server recv");
|
||||
assert_eq!(&got, pkt, "client->server payload mismatch");
|
||||
}
|
||||
|
||||
// --- Server -> Client: several packets, assert integrity --------------------------------
|
||||
let s2c: Vec<Vec<u8>> = vec![
|
||||
b"hello client".to_vec(),
|
||||
vec![0xABu8; 777],
|
||||
b"final packet".to_vec(),
|
||||
];
|
||||
for pkt in &s2c {
|
||||
server_conn.send_packet(pkt).await.expect("server send");
|
||||
let got = client_conn.recv_packet().await.expect("client recv");
|
||||
assert_eq!(&got, pkt, "server->client payload mismatch");
|
||||
}
|
||||
|
||||
// --- Concurrent full-duplex: both directions in flight at once --------------------------
|
||||
let s = server_conn.clone();
|
||||
let c = client_conn.clone();
|
||||
let dup_server = tokio::spawn(async move {
|
||||
s.send_packet(b"duplex-from-server").await.unwrap();
|
||||
s.recv_packet().await.unwrap()
|
||||
});
|
||||
let dup_client = tokio::spawn(async move {
|
||||
c.send_packet(b"duplex-from-client").await.unwrap();
|
||||
c.recv_packet().await.unwrap()
|
||||
});
|
||||
let server_got = dup_server.await.expect("dup server join");
|
||||
let client_got = dup_client.await.expect("dup client join");
|
||||
assert_eq!(server_got, b"duplex-from-client");
|
||||
assert_eq!(client_got, b"duplex-from-server");
|
||||
}
|
||||
Reference in New Issue
Block a user