refactor(client): make authentication explicit and redact HMAC keys
This commit is contained in:
18 files changed
+492
-119
No files matched your search
@@ -0,0 +1,96 @@
|
||||
use std::{net::UdpSocket, thread, time::Duration};
|
||||
|
||||
use irtt_client::{Authentication, Client, ClientConfig, ClientEvent, HmacKey, SocketConfig};
|
||||
use irtt_proto::{decode_request, verify_packet_hmac};
|
||||
use irtt_server::{ServerConfig, ServerCore};
|
||||
|
||||
// Exercise the runtime-free API against the production server core, including
|
||||
// the wire HMAC flag on Open, Echo and Close for an explicitly empty key.
|
||||
#[test]
|
||||
fn concrete_authentication_preserves_session_and_empty_key_behavior() {
|
||||
for key in [None, Some(b"session-test-key".as_slice()), Some(&[])] {
|
||||
let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
socket
|
||||
.set_read_timeout(Some(Duration::from_secs(2)))
|
||||
.unwrap();
|
||||
let addr = socket.local_addr().unwrap();
|
||||
let server = thread::spawn(move || {
|
||||
let config = key.map_or_else(ServerConfig::default, |key| {
|
||||
ServerConfig::default().with_hmac_key(key)
|
||||
});
|
||||
let mut core = ServerCore::new(config);
|
||||
let mut buffer = [0; 2048];
|
||||
for _ in 0..3 {
|
||||
let (len, peer) = socket.recv_from(&mut buffer).unwrap();
|
||||
let packet = &buffer[..len];
|
||||
assert_eq!(decode_request(packet).unwrap().hmac_present, key.is_some());
|
||||
if let Some(key) = key {
|
||||
verify_packet_hmac(key, packet).unwrap();
|
||||
}
|
||||
if let Some(reply) = core.handle_datagram(peer, packet).unwrap() {
|
||||
socket.send_to(reply.bytes(), peer).unwrap();
|
||||
}
|
||||
}
|
||||
assert_eq!(core.session_count(), 0, "Close must release the session");
|
||||
});
|
||||
let mut client = Client::connect(ClientConfig {
|
||||
server_addr: addr.to_string(),
|
||||
auth: key.map_or(Authentication::Unauthenticated, |key| {
|
||||
Authentication::Hmac(HmacKey::new(key))
|
||||
}),
|
||||
open_timeouts: vec![Duration::from_millis(200)],
|
||||
socket_config: SocketConfig {
|
||||
recv_timeout: Some(Duration::from_secs(1)),
|
||||
..SocketConfig::default()
|
||||
},
|
||||
..ClientConfig::default()
|
||||
})
|
||||
.unwrap();
|
||||
client.open().unwrap();
|
||||
client.send_probe().unwrap();
|
||||
assert!(client
|
||||
.recv_once()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|event| { matches!(event, ClientEvent::EchoReply { .. }) }));
|
||||
client.close().unwrap();
|
||||
server.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authentication_debug_redacts_keys_in_enclosing_configurations() {
|
||||
let sentinel = b"CONSPICUOUS_HMAC_SECRET_DO_NOT_PRINT";
|
||||
let key = HmacKey::new(sentinel.as_slice());
|
||||
let auth = Authentication::Hmac(key.clone());
|
||||
let config = ClientConfig {
|
||||
auth: auth.clone(),
|
||||
..ClientConfig::default()
|
||||
};
|
||||
let mut outputs = vec![
|
||||
format!("{key:?}"),
|
||||
format!("{auth:?}"),
|
||||
format!("{config:?}"),
|
||||
];
|
||||
#[cfg(feature = "tokio")]
|
||||
{
|
||||
use irtt_client::managed::{ManagedClientConfig, ManagedTargetConfig, TargetAuth};
|
||||
let mut target = ManagedTargetConfig::new("one", "127.0.0.1:2112");
|
||||
target.auth = TargetAuth::Override(auth);
|
||||
outputs.push(format!("{target:?}"));
|
||||
outputs.push(format!(
|
||||
"{:?}",
|
||||
ManagedClientConfig {
|
||||
client: config,
|
||||
..ManagedClientConfig::default()
|
||||
}
|
||||
));
|
||||
}
|
||||
// Check both textual and ordinary byte-slice Debug encodings so replacing
|
||||
// the opaque formatter with a derived formatter cannot slip through.
|
||||
for output in outputs.drain(..) {
|
||||
assert!(!output.contains(std::str::from_utf8(sentinel).unwrap()));
|
||||
assert!(!output.contains(&format!("{sentinel:?}")));
|
||||
assert!(output.contains("[REDACTED]"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user