checkpoint: implement wgcloak v1 foundations
This commit is contained in:
commit
313ab00765
45 files changed
+12058
No files matched your search
+148
@@ -0,0 +1,148 @@
|
||||
#[path = "../src/config.rs"]
|
||||
mod config;
|
||||
#[path = "../src/datagram.rs"]
|
||||
mod datagram;
|
||||
#[path = "../src/protocol/mod.rs"]
|
||||
mod protocol;
|
||||
#[path = "../src/stats.rs"]
|
||||
mod stats;
|
||||
#[path = "../src/transport/mod.rs"]
|
||||
mod transport;
|
||||
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
|
||||
use base64::Engine;
|
||||
use bytes::Bytes;
|
||||
use quinn::{ClientConfig as QuinnClientConfig, Endpoint, ServerConfig as QuinnServerConfig};
|
||||
use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer};
|
||||
use tokio::time::timeout;
|
||||
use transport::{
|
||||
quic::{QuicClientConnector, QuicServer},
|
||||
TransportError, TransportSession,
|
||||
};
|
||||
|
||||
const SECRET: &str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
|
||||
// A test-only localhost certificate, valid for ten years. Keeping the DER
|
||||
// here avoids adding a certificate-generation crate to the application.
|
||||
const CERT_DER_B64: &str = "MIIDHDCCAgSgAwIBAgIUJnI/VlHMWu6j0dXr/t1OmMQrLgowDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDkxNDIwNDEyMFoXDTM2MDkxMTIwNDEyMFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm4d4r5Py4GdfQrLit2oytLv20L+y7oM/VTDzzJfFMPmewOh0XyS0BI4lyRbCgOuUh686UPvF/BU8uJq24OO7wt/2CrE3FfwYX7BDjIWMuFevHuv896imabbX4IJ7lzVugI3862QSW09QSEQk3ggYs3z/F16cnSRZm9aIURgdPQsLmMzOwcPtDGwxp2jf0Rky0kFRxhUnqfIWko4hny7GjWL8VG0WzYJY6aUevjPlN/Oq/akr9xUAr2QJenEesP77do4X31LIZmsvB6GqKwaSQ0AbwNHW7rDiaE4dkZMIk5l95YgYnrzyOC8IhUwaSZt0wODxk/euXPYr+RFJv2FuOQIDAQABo2YwZDAdBgNVHQ4EFgQUhViBEXLkp7YUVZaWzLi2SfRmfmYwHwYDVR0jBBgwFoAUhViBEXLkp7YUVZaWzLi2SfRmfmYwFAYDVR0RBA0wC4IJbG9jYWxob3N0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAEFC7da0bK78IUL0k6lZ82pqFImX2bNteMh/r1IJG0xqB/O2dOLUDHybpF7HToommALgRu6zhlT0d1eHg0cMYEgrKyq4BAAXGHqOt3OdKKL5nAWmI04dlWsrG9aTU3hTQhe0++hnTbVm+YJnYAp+ufOxx858FeothjLWu1Lr81Nm01FnmZbXcFno3nosyl1GPxSRVZl2XH9NsfMSZJUbSU6mHXTO1pEH6mjYmgD362CeJc+0DOzevdLsDozxUoWmdEuqlldauESW6XVK0WxRSJ1i2/B/mtYViHr5R/j5NYgG5STnNnwaNwjYK+0AhxvTtH3jAp0Bsluw4d+xKdsV/YI=";
|
||||
const KEY_DER_B64: &str = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbh3ivk/LgZ19CsuK3ajK0u/bQv7Lugz9VMPPMl8Uw+Z7A6HRfJLQEjiXJFsKA65SHrzpQ+8X8FTy4mrbg47vC3/YKsTcV/BhfsEOMhYy4V68e6/z3qKZpttfggnuXNW6AjfzrZBJbT1BIRCTeCBizfP8XXpydJFmb1ohRGB09CwuYzM7Bw+0MbDGnaN/RGTLSQVHGFSep8haSjiGfLsaNYvxUbRbNgljppR6+M+U386r9qSv3FQCvZAl6cR6w/vt2jhffUshmay8HoaorBpJDQBvA0dbusOJoTh2RkwiTmX3liBievPI4LwiFTBpJm3TA4PGT965c9iv5EUm/YW45AgMBAAECggEAMs0EtCD4t6i8Ol6Q5EUZa+RrgZLkILkZEjwLnjvCqrUWUD99Z2hnLoe5ut2myZJpcQpg23vpluXe8BOFAZ8RQN2DkcmByzoNQhAieD9VEA+AAXMmrURWmvggulIUiXbn8bCvOLn2rwGQoRI1EpsFanc9IUaDlGZ/qIHiA88DGeFDuchbcgClpXAX4lBzqhLKv/CCIhhGxmyWc1RPlZUef7uTO1Zh7q2HxnO5iBKobvPfMq5VfvzE4rXeSQIHtW+yJFLU2sNjaYry2JTLEqGIZhWMROmzlkW8x/cj0GvjLbY4ltkIpLNSb+WRbY3zZNhQ81kosBZtiYRj3vgr/FzvSwKBgQDQ/dxghryGDqIIOfyZ59+2tpPrrLX4vy7tmAQg6zJIiQVHMZ/Av60QvY7lA0dvDfQmfJuNzTOXtwhgnYkHbg6jlFqpT7P1CZQOzbF0UuFCNvwzE/jgCEKUExckDa+Du7g/VOglB9B4XKqJnQLpZkzz4Hwmuvz4oUtZJCl796HTMwKBgQC+gyPKrNp6AthxZ1gAu+aIjf18vgGZjANQx3cRFQl5BqSyTwZpxIfuiKrT+sU65N2VCWpGH6oTzGruaXBXzG0rlNCMAL5XaJqJehvLAnejUqmXhyi8ouH174Q4azDngPOCFaSjuYJwpX4nUXwMExqs9hwkH9pV3tn6qzEpKJA44wKBgBa+SalHgGSmWwPCK7ckALiFjftrxDHZvGWm0WwzrDkwkB57TANR2KeluVM61B9FyXgWv2bjGeY8yPCatDrWw0kR7/vU7KSR8kbaI6GhE1p9WVzFo6PdwkETAsWtp5gSaa6asti0zP8MNpLbP2He/CjeGSSt3LHB+80rnkgbXsT5AoGAVaTytCxc0zjwQJrqzvmpu33JlQh+A58T5n6lIkLIzWWVKKuqAGZfjXZXBZDYlY9JpbRj2D5n4Rqmf6Na47183ni6ABZde0IFJwSD9hGX4w5IO1yBjbvTxRH16L8XbxtjXPmJ2CiPYUCko9xX3b3A4jyJjhvbtXrHl47aPNyFaW0CgYEAxDOcW5r5pXXkCF/mIqSklGa1jDO9TeMavLo1AguHaUlJEQU8DgGM9Flx3senFASN2EjwwTB3aScVuxe3X29LeZnCNsuTIzCVGpSNyFLUjZVU0fRaf6R9JIcI7dVoflPAPCdrqBVkezK5Eyu8f3NCwnVD+zv+iWA+RE1zUyKy4sM=";
|
||||
|
||||
fn endpoint_and_client(
|
||||
max_datagram: usize,
|
||||
) -> (Endpoint, QuinnClientConfig, CertificateDer<'static>) {
|
||||
let cert_bytes = base64::engine::general_purpose::STANDARD
|
||||
.decode(CERT_DER_B64)
|
||||
.unwrap();
|
||||
let key_bytes = base64::engine::general_purpose::STANDARD
|
||||
.decode(KEY_DER_B64)
|
||||
.unwrap();
|
||||
let cert = CertificateDer::from(cert_bytes);
|
||||
let key = PrivatePkcs8KeyDer::from(key_bytes);
|
||||
let mut server_config =
|
||||
QuinnServerConfig::with_single_cert(vec![cert.clone()], key.into()).unwrap();
|
||||
let mut transport_config = quinn::TransportConfig::default();
|
||||
transport_config.keep_alive_interval(Some(Duration::from_secs(1)));
|
||||
transport_config.datagram_send_buffer_size(max_datagram * 8);
|
||||
server_config.transport_config(Arc::new(transport_config));
|
||||
let endpoint = Endpoint::server(server_config, "127.0.0.1:0".parse().unwrap()).unwrap();
|
||||
let mut roots = rustls::RootCertStore::empty();
|
||||
roots.add(cert.clone()).unwrap();
|
||||
let client = QuinnClientConfig::with_root_certificates(Arc::new(roots)).unwrap();
|
||||
(endpoint, client, cert)
|
||||
}
|
||||
|
||||
fn client_config(port: u16, secret: &str) -> config::ClientConfig {
|
||||
config::Config::from_str(&format!(
|
||||
"[client]\nserver = \"localhost\"\nid = \"test\"\nsecret = \"{secret}\"\n[transport.quic]\nport = {port}\n[transport.auto]\nprobe_timeout_ms = 2000\n[network]\nmax_datagram_size = 1200\n"
|
||||
))
|
||||
.unwrap()
|
||||
.into_client()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn authenticated_datagram_round_trip_and_limit() {
|
||||
let (endpoint, client_tls, _cert) = endpoint_and_client(1200);
|
||||
let mut clients = HashMap::new();
|
||||
clients.insert(
|
||||
"test".to_owned(),
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.decode(SECRET)
|
||||
.unwrap(),
|
||||
);
|
||||
let server = QuicServer::from_endpoint(endpoint, clients, 1200, Duration::from_secs(2));
|
||||
let port = server.local_addr().unwrap().port();
|
||||
let client = QuicClientConnector::with_client_config(client_config(port, SECRET), client_tls);
|
||||
let server_task = tokio::spawn(async move { server.accept().await });
|
||||
let client_session = client.connect_session().await.unwrap();
|
||||
let server_session = server_task.await.unwrap().unwrap();
|
||||
client_session
|
||||
.send_datagram(Bytes::from_static(b"opaque"))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
timeout(Duration::from_secs(2), server_session.recv_datagram())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Bytes::from_static(b"opaque")
|
||||
);
|
||||
assert!(matches!(
|
||||
client_session
|
||||
.send_datagram(Bytes::from(vec![0; 1201]))
|
||||
.await,
|
||||
Err(TransportError::Oversized { maximum: 1200 })
|
||||
));
|
||||
client_session.close().await.unwrap();
|
||||
server_session.close().await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn bad_secret_is_rejected_as_authentication_error() {
|
||||
let (endpoint, client_tls, _cert) = endpoint_and_client(1200);
|
||||
let mut clients = HashMap::new();
|
||||
clients.insert(
|
||||
"test".to_owned(),
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.decode(SECRET)
|
||||
.unwrap(),
|
||||
);
|
||||
let server = QuicServer::from_endpoint(endpoint, clients, 1200, Duration::from_secs(2));
|
||||
let port = server.local_addr().unwrap().port();
|
||||
let wrong = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||
let client = QuicClientConnector::with_client_config(client_config(port, wrong), client_tls);
|
||||
let server_task = tokio::spawn(async move { server.accept().await });
|
||||
let result = client.connect_session().await;
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TransportError::AuthenticationRejected(_))
|
||||
));
|
||||
assert!(matches!(
|
||||
server_task.await.unwrap(),
|
||||
Err(TransportError::AuthenticationRejected(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn certificate_hostname_mismatch_is_not_accepted() {
|
||||
let (endpoint, client_tls, _cert) = endpoint_and_client(1200);
|
||||
let mut clients = HashMap::new();
|
||||
clients.insert(
|
||||
"test".to_owned(),
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.decode(SECRET)
|
||||
.unwrap(),
|
||||
);
|
||||
let server = QuicServer::from_endpoint(endpoint, clients, 1200, Duration::from_secs(2));
|
||||
let port = server.local_addr().unwrap().port();
|
||||
let server_task = tokio::spawn(async move { server.accept().await });
|
||||
// The connector's configured hostname is localhost, which matches. This
|
||||
// test changes it to an unrelated name while retaining the localhost root.
|
||||
let mut mismatch = client_config(port, SECRET);
|
||||
mismatch.client.server = "not-localhost".to_owned();
|
||||
let mismatch = QuicClientConnector::with_client_config(mismatch, client_tls);
|
||||
let result = mismatch.connect_session().await;
|
||||
assert!(result.is_err());
|
||||
server_task.abort();
|
||||
}
|
||||
Reference in new issue
Block a user