//! End-to-end smoke test for the daily mask rotator: build a CA, derive today's [`MaskSet`], plug //! its `padding_profile_id` into the server / client `UdpOpts`, run a UDP loopback handshake, and //! exchange a packet. This proves: //! //! * The crypto-layer derivation produces values that the transport layer accepts. //! * The padding profile id derived from `MaskSet` is a valid argument for `pad_to_bucket` / //! `next_bucket_for_profile`. //! * Wire compatibility is preserved when both ends use the same mask. //! //! It does NOT exercise the time-based rotation (that runs at 05:00 MSK and would require freezing //! the clock); the algorithm itself is unit-tested in `aura_cli::masks::tests`. use std::sync::Arc; use aura_cli::masks::MaskRotator; use aura_crypto::derive_mask_for_msk_date; use aura_pki::AuraCa; use aura_proto::{ClientConfig, PacketConnection, ServerConfig}; use aura_transport::{UdpClient, UdpOpts, UdpServer}; const SERVER_NAME: &str = "localhost"; const CLIENT_ID: &str = "cli-mask-client"; fn make_configs(ca: &AuraCa) -> (ServerConfig, ClientConfig) { let server_cert = ca .issue_server_cert(SERVER_NAME) .expect("issue server cert"); let client_cert = ca.issue_client_cert(CLIENT_ID).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, client_cert_pem: client_cert.cert_pem, client_key_pem: client_cert.key_pem, server_name: SERVER_NAME.to_string(), }; (server_cfg, client_cfg) } #[tokio::test] async fn mask_drives_udp_loopback_with_obfuscation() { // Real CA → real CA PEM → real fingerprint → today's MaskSet, both sides. let ca = AuraCa::generate("aura-mask-loopback-ca").expect("CA"); let pem = ca.ca_cert_pem(); // Rotator drives the *current* mask (matches a direct derive for today). let rotator = MaskRotator::new(&pem).expect("rotator"); let current = rotator.current().await; // Cross-check against a direct crypto-layer derive for today's MSK day. let now = aura_cli::masks::unix_now_utc(); let (y, m, d) = aura_cli::masks::msk_today(now); let fp = aura_crypto::ca_fingerprint(&pem).expect("fp"); let direct = derive_mask_for_msk_date(&fp, y, m, d); assert_eq!(current, direct, "rotator should match direct derivation"); // Build UdpOpts with obfuscation on and the *mask's* padding profile id. Both sides agree // because they derived from the same CA and date. let opts = UdpOpts { obfuscate: true, padding_profile: current.padding_profile_id, ..UdpOpts::default() }; let (server_cfg, client_cfg) = make_configs(&ca); let server = UdpServer::bind("127.0.0.1:0".parse().unwrap(), server_cfg, opts).expect("bind udp server"); let server_addr = server.local_addr().expect("server addr"); let accept_task = tokio::spawn(async move { server.accept().await }); let connect_task = tokio::spawn(async move { UdpClient::connect(server_addr, client_cfg, opts).await }); let server_conn = accept_task.await.expect("accept join").expect("accept"); let client_conn = connect_task.await.expect("connect join").expect("connect"); assert_eq!(server_conn.peer_id(), Some(CLIENT_ID)); assert_eq!(client_conn.peer_id(), Some(SERVER_NAME)); let server_conn: Arc = Arc::new(server_conn); let client_conn: Arc = Arc::new(client_conn); for pkt in [ b"hello-mask".to_vec(), vec![0xA5u8; 1300], (0..200u8).collect::>(), ] { client_conn.send_packet(&pkt).await.expect("client send"); let got = server_conn.recv_packet().await.expect("server recv"); assert_eq!(got, pkt, "padded round trip preserves the payload"); } }