Files
AuraVPN/crates/aura-transport/tests/tcp_loopback.rs
T
xah30 c95e1a482c feat(crypto,cli,transport): daily protocol-mask rotation at 05:00 MSK
Both server and client deterministically rotate the on-wire obfuscation mask
(SNI, HTTP Host/User-Agent/Server headers, UDP padding profile) at 05:00 Moscow
time (02:00 UTC) every day, derived from the CA fingerprint + UTC date — no
network coordination needed.

- aura-crypto::masks: MaskSet + 4 palettes (16 SNI, 10 UA, 5 Server, 4 padding
  profiles); derive_mask_for_msk_date via HKDF-SHA256(salt="aura-mask-v1-salt",
  ikm=ca_fp||"YYYY-MM-DD", info="aura-mask-v1"); ca_fingerprint with built-in
  base64 PEM decode (no new deps).
- aura-cli::masks: MaskRotator (Arc<RwLock<MaskSet>>) + Hinnant's civil_from_days
  for manual UTC date math; scheduler picks next 02:00 UTC strictly (avoids
  busy-loop at boundary); spawned at startup in server::run/client::run.
- aura-transport: PADDING_PROFILES + next_bucket_for_profile (profile 0 byte-for-
  byte equals legacy pad_to_https_size); TcpOpts gains user_agent/server_header;
  UdpOpts gains padding_profile; MultiServer holds Arc<UdpServer>/Arc<TcpServer>
  with set_udp_opts/set_tcp_opts so rotation propagates without restart.
- Backward-compatible: defaults preserve previous behavior; existing 97 tests
  unchanged. 17 new tests (derive determinism + date variation, civil-from-days
  known points incl. 1970-01-01/2000-02-29/2024->2025, next-rotation boundary,
  msk_today offset, profile equivalence, base64 round-trip, full mask-driven
  UDP loopback). Total: 114 passed, clippy/fmt clean. No new workspace deps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 01:11:45 +03:00

78 lines
2.7 KiB
Rust

//! End-to-end loopback test for the TCP fallback transport: real TCP on 127.0.0.1, full Aura
//! mutual-auth handshake, packet echo — with the HTTP masquerade both off and on.
use aura_pki::AuraCa;
use aura_proto::{ClientConfig, PacketConnection, ServerConfig};
use aura_transport::{TcpClient, TcpOpts, TcpServer};
/// Mint a fresh CA + server("localhost") + client("client-tcp") and build the proto configs.
fn make_configs() -> (ServerConfig, ClientConfig) {
let ca = AuraCa::generate("Aura Test CA").expect("generate CA");
let server = ca
.issue_server_cert("localhost")
.expect("issue server cert");
let client = ca
.issue_client_cert("client-tcp")
.expect("issue client cert");
let ca_pem = ca.ca_cert_pem();
let scfg = ServerConfig {
ca_cert_pem: ca_pem.clone(),
server_cert_pem: server.cert_pem,
server_key_pem: server.key_pem,
};
let ccfg = ClientConfig {
ca_cert_pem: ca_pem,
client_cert_pem: client.cert_pem,
client_key_pem: client.key_pem,
server_name: "localhost".to_string(),
};
(scfg, ccfg)
}
async fn run_case(opts: TcpOpts) {
let (scfg, ccfg) = make_configs();
let server = TcpServer::bind("127.0.0.1:0".parse().unwrap(), scfg, opts.clone())
.await
.expect("bind server");
let addr = server.local_addr().expect("local addr");
let server_task = tokio::spawn(async move {
let conn = server.accept().await.expect("server handshake");
assert_eq!(conn.peer_id(), Some("client-tcp"), "verified client id");
// Echo three packets back to the client.
for _ in 0..3 {
let pkt = conn.recv_packet().await.expect("server recv");
conn.send_packet(&pkt).await.expect("server echo");
}
});
let client = TcpClient::connect(addr, ccfg, opts)
.await
.expect("client handshake");
// Exchange packets of varying sizes (incl. a large one) and assert the echo matches.
for i in 0..3u16 {
let payload = vec![(i as u8).wrapping_add(1); 100 + (i as usize) * 600]; // 100, 700, 1300 bytes
client.send_packet(&payload).await.expect("client send");
let echoed = client.recv_packet().await.expect("client recv");
assert_eq!(echoed, payload, "round-trip payload mismatch");
}
server_task.await.expect("server task");
}
#[tokio::test]
async fn tcp_loopback_end_to_end_plain() {
run_case(TcpOpts::default()).await;
}
#[tokio::test]
async fn tcp_loopback_end_to_end_masquerade() {
run_case(TcpOpts {
masquerade: true,
host: "cdn.example.com".to_string(),
..TcpOpts::default()
})
.await;
}