From 156db4c7201f846dc7ab3beac1e53bdc92c0c14c Mon Sep 17 00:00:00 2001 From: Nils Andreas Svee Date: Sun, 4 Oct 2026 16:45:59 +0700 Subject: [PATCH] refactor(client): make authentication explicit and redact HMAC keys --- crates/irtt-app/src/shared/client/args.rs | 21 ++++++++- crates/irtt-app/src/shared/client/config.rs | 9 +++- crates/irtt-app/src/shared/client/targets.rs | 44 +++---------------- crates/irtt-app/tests/cli.rs | 26 +++++++---- crates/irtt-client/README.md | 19 ++++++++ crates/irtt-client/src/auth.rs | 44 +++++++++++++++++++ crates/irtt-client/src/config.rs | 35 +++------------ crates/irtt-client/src/lib.rs | 6 ++- crates/irtt-client/src/managed/model.rs | 30 +++++++++++-- crates/irtt-client/src/managed/task.rs | 8 +--- crates/irtt-client/src/session/machine.rs | 12 ++--- crates/irtt-client/tests/client/hmac.rs | 7 +-- .../tests/async_client_lifecycle.rs | 5 ++- crates/irtt-integration-tests/tests/hmac.rs | 23 ++++++---- .../tests/managed_lifecycle.rs | 11 ++--- .../tests/protocol_options.rs | 3 +- 16 files changed, 184 insertions(+), 119 deletions(-) create mode 100644 crates/irtt-client/src/auth.rs diff --git a/crates/irtt-app/src/shared/client/args.rs b/crates/irtt-app/src/shared/client/args.rs index b705dbc..699c439 100644 --- a/crates/irtt-app/src/shared/client/args.rs +++ b/crates/irtt-app/src/shared/client/args.rs @@ -1,10 +1,10 @@ -use std::time::Duration; +use std::{fmt, time::Duration}; use clap::ValueEnum; use irtt_client::{MAX_DSCP_CODEPOINT, MAX_SERVER_FILL_BYTES, MAX_TTL, MAX_UDP_PAYLOAD_LENGTH}; use irtt_proto::{Clock, ReceivedStats, StampAt}; -#[derive(Debug, Clone, clap::Args)] +#[derive(Clone, clap::Args)] pub struct CommonClientArgs { /// Probe interval. #[arg(long, default_value = "1s", value_parser = parse_duration)] @@ -63,6 +63,23 @@ pub struct CommonClientArgs { pub loose: bool, } +impl fmt::Debug for CommonClientArgs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CommonClientArgs") + .field("interval", &self.interval) + .field("length", &self.length) + .field("hmac", &self.hmac.as_ref().map(|_| "[REDACTED]")) + .field("clock", &self.clock) + .field("tstamp", &self.tstamp) + .field("stats", &self.stats) + .field("server_fill", &self.server_fill) + .field("dscp", &self.dscp) + .field("ttl", &self.ttl) + .field("loose", &self.loose) + .finish() + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] pub enum ClockArg { Wall, diff --git a/crates/irtt-app/src/shared/client/config.rs b/crates/irtt-app/src/shared/client/config.rs index 8246e06..311f0eb 100644 --- a/crates/irtt-app/src/shared/client/config.rs +++ b/crates/irtt-app/src/shared/client/config.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use irtt_client::{ClientConfig, NegotiationPolicy, SocketConfig}; +use irtt_client::{Authentication, ClientConfig, HmacKey, NegotiationPolicy, SocketConfig}; use super::args::CommonClientArgs; @@ -21,7 +21,12 @@ impl CommonClientArgs { stamp_at: self.tstamp.into(), clock: self.clock.into(), dscp: self.dscp, - hmac_key: self.hmac.as_ref().map(|key| key.as_bytes().to_vec()), + auth: self + .hmac + .as_ref() + .map_or(Authentication::Unauthenticated, |key| { + Authentication::Hmac(HmacKey::new(key.as_bytes())) + }), server_fill: self.server_fill.clone(), negotiation_policy: if self.loose { NegotiationPolicy::Loose diff --git a/crates/irtt-app/src/shared/client/targets.rs b/crates/irtt-app/src/shared/client/targets.rs index 40d3166..0d3bc38 100644 --- a/crates/irtt-app/src/shared/client/targets.rs +++ b/crates/irtt-app/src/shared/client/targets.rs @@ -2,8 +2,8 @@ use std::{collections::HashSet, fmt}; use clap::ValueEnum; use irtt_client::{ - managed::{ManagedPacing, ManagedTargetConfig, TargetId}, - ClientAuthConfig, + managed::{ManagedPacing, ManagedTargetConfig, TargetAuth, TargetId}, + Authentication, HmacKey, }; /// One raw positional target captured by Clap. @@ -35,23 +35,6 @@ pub fn parse_target(input: &str) -> Result { Ok(TargetArg::new(input)) } -#[derive(Clone, PartialEq, Eq)] -pub enum TargetAuth { - Inherit, - Override(Vec), - Disable, -} - -impl fmt::Debug for TargetAuth { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Inherit => f.write_str("Inherit"), - Self::Override(_) => f.write_str("Override()"), - Self::Disable => f.write_str("Disable"), - } - } -} - #[derive(Clone, PartialEq, Eq)] pub struct TargetSpec { pub label: String, @@ -80,16 +63,7 @@ impl fmt::Debug for PreparedTarget { f.debug_struct("PreparedTarget") .field("label", &self.label) .field("server_addr", &self.managed.server_addr) - .field( - "auth", - &match &self.managed.auth { - None => TargetAuth::Inherit, - Some(ClientAuthConfig { hmac_key: Some(_) }) => { - TargetAuth::Override(Vec::new()) - } - Some(ClientAuthConfig { hmac_key: None }) => TargetAuth::Disable, - }, - ) + .field("auth", &self.managed.auth) .finish() } } @@ -216,9 +190,9 @@ fn parse_target_syntax( Some(index) => { let value = unescape(&input[index + "@hmac=".len()..])?; let auth = if value.is_empty() { - TargetAuth::Disable + TargetAuth::Override(Authentication::Unauthenticated) } else { - TargetAuth::Override(value.into_bytes()) + TargetAuth::Override(Authentication::Hmac(HmacKey::new(value.into_bytes()))) }; (&input[..index], auth) } @@ -287,13 +261,7 @@ pub fn prepare_managed_targets(specs: Vec) -> Result None, - TargetAuth::Override(hmac_key) => Some(ClientAuthConfig { - hmac_key: Some(hmac_key), - }), - TargetAuth::Disable => Some(ClientAuthConfig { hmac_key: None }), - }; + managed.auth = spec.auth; targets.push(PreparedTarget { label: spec.label, managed, diff --git a/crates/irtt-app/tests/cli.rs b/crates/irtt-app/tests/cli.rs index 494f9be..a66f492 100644 --- a/crates/irtt-app/tests/cli.rs +++ b/crates/irtt-app/tests/cli.rs @@ -149,29 +149,37 @@ fn default_client_output_emits_a_packet_summary() { fn target_authentication_inherits_overrides_or_disables_the_global_key() { use clap::Parser; use irtt_app::cmd::client::args::ClientArgs; + use irtt_client::{managed::TargetAuth, Authentication, HmacKey}; let args = ClientArgs::try_parse_from([ "irtt-client", "--hmac", - "global-test-key", + "CONSPICUOUS_GLOBAL_HMAC_KEY", "inherit=127.0.0.1:2112", - "override=127.0.0.1:2113@hmac=target-test-key", + "override=127.0.0.1:2113@hmac=CONSPICUOUS_TARGET_HMAC_KEY", "disable=127.0.0.1:2114@hmac=", ]) .unwrap(); let setup = args.prepare().unwrap(); assert_eq!( - setup.managed_config().client.hmac_key.as_deref(), - Some(b"global-test-key".as_slice()) + setup.managed_config().client.auth, + Authentication::Hmac(HmacKey::new(b"CONSPICUOUS_GLOBAL_HMAC_KEY".as_slice())) ); let targets = setup.managed_targets(); - let authentication: Vec<_> = targets - .iter() - .map(|target| target.auth.as_ref().map(|auth| auth.hmac_key.as_deref())) - .collect(); + let authentication: Vec<_> = targets.iter().map(|target| target.auth.clone()).collect(); assert_eq!( authentication, - [None, Some(Some(b"target-test-key".as_slice())), Some(None)] + [ + TargetAuth::Inherit, + TargetAuth::Override(Authentication::Hmac(HmacKey::new( + b"CONSPICUOUS_TARGET_HMAC_KEY".as_slice() + ))), + TargetAuth::Override(Authentication::Unauthenticated) + ] ); + for output in [format!("{args:?}"), format!("{setup:?}")] { + assert!(!output.contains("CONSPICUOUS_GLOBAL_HMAC_KEY")); + assert!(!output.contains("CONSPICUOUS_TARGET_HMAC_KEY")); + } } // A real authenticated server detects broken client argument-to-config mapping. diff --git a/crates/irtt-client/README.md b/crates/irtt-client/README.md index 8bcc1ed..7e496fd 100644 --- a/crates/irtt-client/README.md +++ b/crates/irtt-client/README.md @@ -34,6 +34,25 @@ dependency at all. See `examples/` in the repository for runnable examples of each tier. +## Authentication + +`ClientConfig.auth` is concrete: `Authentication::Unauthenticated` or +`Authentication::Hmac(HmacKey::new(bytes))`. Keys share their bytes across +clones and print only `HmacKey([REDACTED])` through `Debug`. Borrowing key +material requires an explicit `as_bytes()` call. An empty key still selects +HMAC authentication; textual key syntax belongs to the application. + +For managed targets, `ManagedTargetConfig.auth` defaults to `TargetAuth::Inherit`. +`TargetAuth::Override(Authentication::Unauthenticated)` disables shared +authentication, and `TargetAuth::Override(Authentication::Hmac(key))` replaces +the shared key. Initial construction and update planning resolve inheritance +into concrete session authentication before execution. Switching between +inheritance and an override replaces the target generation even when their +resolved authentication is equal. + +This replaces the former `ClientConfig.hmac_key` and `ClientAuthConfig` API; +there are no compatibility aliases. + ## Documentation Full API documentation is on [docs.rs/irtt-client](https://docs.rs/irtt-client). diff --git a/crates/irtt-client/src/auth.rs b/crates/irtt-client/src/auth.rs new file mode 100644 index 0000000..8e3cf07 --- /dev/null +++ b/crates/irtt-client/src/auth.rs @@ -0,0 +1,44 @@ +use std::{fmt, sync::Arc}; + +/// Shared HMAC key bytes. Debug output never includes the bytes or a fingerprint. +/// +/// An empty key is still an explicitly configured HMAC key. +#[derive(Clone, PartialEq, Eq)] +pub struct HmacKey(Arc<[u8]>); + +impl HmacKey { + /// Store key bytes without interpreting textual key syntax. + pub fn new(bytes: impl Into>) -> Self { + Self(bytes.into()) + } + + /// Explicitly borrow the key material for authentication. + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +impl fmt::Debug for HmacKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("HmacKey([REDACTED])") + } +} + +/// Concrete authentication for a low-level client session. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub enum Authentication { + /// Send and accept packets without HMAC authentication. + #[default] + Unauthenticated, + /// Authenticate packets with this key, including when it is empty. + Hmac(HmacKey), +} + +impl Authentication { + pub(crate) fn hmac_key(&self) -> Option<&[u8]> { + match self { + Self::Unauthenticated => None, + Self::Hmac(key) => Some(key.as_bytes()), + } + } +} diff --git a/crates/irtt-client/src/config.rs b/crates/irtt-client/src/config.rs index 5ac4497..250c46d 100644 --- a/crates/irtt-client/src/config.rs +++ b/crates/irtt-client/src/config.rs @@ -1,5 +1,7 @@ use std::{net::SocketAddr, time::Duration}; +use crate::Authentication; + /// Protocol compatibility bound for a requested `server_fill` value, in UTF-8 /// bytes. /// @@ -118,11 +120,10 @@ pub struct ClientConfig { /// This is the six-bit DSCP value, not the full traffic-class byte, and /// must be less than or equal to [`MAX_DSCP_CODEPOINT`]. pub dscp: u8, - /// Optional HMAC key used to authenticate IRTT packets. + /// Concrete authentication for open, echo, and close packets. /// - /// When present, open, echo, and close packets are encoded and decoded with - /// HMAC authentication. The peer must be configured with the same key. - pub hmac_key: Option>, + /// HMAC peers must use the same key. An empty HMAC key remains authenticated. + pub auth: Authentication, /// Optional server payload fill request. /// /// `None` leaves server fill behavior unspecified. `Some(value)` requests @@ -155,30 +156,6 @@ pub struct ClientConfig { pub max_pending_probes: usize, } -/// Authentication settings used by client sessions. -/// -/// `ClientConfig` keeps its existing top-level HMAC field for source -/// compatibility. Managed multi-target callers can use this smaller type for -/// per-target auth overrides without carrying a full independent -/// [`ClientConfig`]. -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct ClientAuthConfig { - /// Optional HMAC key used to authenticate IRTT packets. - pub hmac_key: Option>, -} - -impl From>> for ClientAuthConfig { - fn from(hmac_key: Option>) -> Self { - Self { hmac_key } - } -} - -impl From for Option> { - fn from(auth: ClientAuthConfig) -> Self { - auth.hmac_key - } -} - impl Default for ClientConfig { fn default() -> Self { Self { @@ -190,7 +167,7 @@ impl Default for ClientConfig { stamp_at: StampAt::Both, clock: Clock::Both, dscp: 0, - hmac_key: None, + auth: Authentication::Unauthenticated, server_fill: None, open_timeouts: DEFAULT_OPEN_TIMEOUTS.to_vec(), run_mode: RunMode::Normal, diff --git a/crates/irtt-client/src/lib.rs b/crates/irtt-client/src/lib.rs index d1a435c..64b6062 100644 --- a/crates/irtt-client/src/lib.rs +++ b/crates/irtt-client/src/lib.rs @@ -34,6 +34,7 @@ #[cfg(feature = "tokio")] mod async_client; +mod auth; mod client; mod config; mod error; @@ -49,10 +50,11 @@ mod timing; #[cfg(feature = "tokio")] pub use async_client::AsyncClient; +pub use auth::{Authentication, HmacKey}; pub use client::Client; pub use config::{ - ClientAuthConfig, ClientConfig, NegotiationPolicy, RecvBudget, RunMode, SocketConfig, - MAX_DSCP_CODEPOINT, MAX_SERVER_FILL_BYTES, MAX_TTL, MAX_UDP_PAYLOAD_LENGTH, + ClientConfig, NegotiationPolicy, RecvBudget, RunMode, SocketConfig, MAX_DSCP_CODEPOINT, + MAX_SERVER_FILL_BYTES, MAX_TTL, MAX_UDP_PAYLOAD_LENGTH, }; pub use error::ClientError; pub use event::{ diff --git a/crates/irtt-client/src/managed/model.rs b/crates/irtt-client/src/managed/model.rs index 1fa046e..68f3999 100644 --- a/crates/irtt-client/src/managed/model.rs +++ b/crates/irtt-client/src/managed/model.rs @@ -2,7 +2,7 @@ use std::{fmt, net::SocketAddr, sync::Arc, time::Duration}; use thiserror::Error; -use crate::{ClientAuthConfig, ClientConfig, ClientError, ClientEvent}; +use crate::{Authentication, ClientConfig, ClientError, ClientEvent}; use super::TargetId; @@ -26,12 +26,34 @@ pub struct TargetInstance { pub generation: u64, } -/// Endpoint and optional authentication override for one managed target. +/// Authentication specification for a managed target. +/// +/// Equality preserves the specification: inheritance and an explicit override +/// remain distinct even when they currently resolve to the same authentication. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub enum TargetAuth { + /// Use the shared client configuration's authentication. + #[default] + Inherit, + /// Replace shared authentication. `Unauthenticated` explicitly disables it. + Override(Authentication), +} + +impl TargetAuth { + pub(crate) fn resolve(&self, shared: &Authentication) -> Authentication { + match self { + Self::Inherit => shared.clone(), + Self::Override(auth) => auth.clone(), + } + } +} + +/// Endpoint and authentication specification for one managed target. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ManagedTargetConfig { pub id: TargetId, pub server_addr: String, - pub auth: Option, + pub auth: TargetAuth, } impl ManagedTargetConfig { @@ -39,7 +61,7 @@ impl ManagedTargetConfig { Self { id: id.into(), server_addr: server_addr.into(), - auth: None, + auth: TargetAuth::Inherit, } } } diff --git a/crates/irtt-client/src/managed/task.rs b/crates/irtt-client/src/managed/task.rs index e99ab11..f780a4f 100644 --- a/crates/irtt-client/src/managed/task.rs +++ b/crates/irtt-client/src/managed/task.rs @@ -945,9 +945,7 @@ impl ManagedClientTask { } let mut client_config = self.config.client.clone(); client_config.server_addr.clone_from(&target.server_addr); - if let Some(auth) = &target.auth { - client_config.hmac_key.clone_from(&auth.hmac_key); - } + client_config.auth = target.auth.resolve(&self.config.client.auth); validate_target_config(&client_config).map_err(|source| { ManagedCommandApplyError::InvalidTarget { id: target.id.clone(), @@ -2251,9 +2249,7 @@ fn build_task( .ok_or(ManagedConfigError::GenerationExhausted)?; let mut client_config = config.client.clone(); client_config.server_addr.clone_from(&target.server_addr); - if let Some(auth) = &target.auth { - client_config.hmac_key.clone_from(&auth.hmac_key); - } + client_config.auth = target.auth.resolve(&config.client.auth); validate_target_config(&client_config).map_err(|source| { ManagedConfigError::InvalidTarget { id: target.id.clone(), diff --git a/crates/irtt-client/src/session/machine.rs b/crates/irtt-client/src/session/machine.rs index 060c439..260adc9 100644 --- a/crates/irtt-client/src/session/machine.rs +++ b/crates/irtt-client/src/session/machine.rs @@ -201,7 +201,7 @@ impl SessionMachine { } pub(crate) fn has_hmac(&self) -> bool { - self.config.hmac_key.is_some() + self.config.auth.hmac_key().is_some() } pub(crate) fn prepare_open_request(&self) -> Result { @@ -211,7 +211,7 @@ impl SessionMachine { params: &self.requested, no_test: self.config.run_mode == RunMode::NoTest, }, - self.config.hmac_key.as_deref(), + self.config.auth.hmac_key(), )?; Ok(PreparedOpenRequest { bytes: bytes.into_boxed_slice(), @@ -223,7 +223,7 @@ impl SessionMachine { packet: &[u8], ) -> Result { self.ensure_connected()?; - match irtt_proto::decode_open_reply(packet, self.config.hmac_key.as_deref()) { + match irtt_proto::decode_open_reply(packet, self.config.auth.hmac_key()) { Ok(reply) => Ok(OpenDatagramDisposition::Trusted(reply)), Err(error @ irtt_proto::ProtoError::ZeroToken) => Err(ClientError::Protocol(error)), Err( @@ -250,7 +250,7 @@ impl SessionMachine { let cleanup_close = if !reply_is_close && reply.token != 0 { let bytes = encode_request( RequestToEncode::Close { token: reply.token }, - self.config.hmac_key.as_deref(), + self.config.auth.hmac_key(), ) .map_err(ClientError::from) .map_err(OpenAcceptanceFailure::without_cleanup)?; @@ -313,7 +313,7 @@ impl SessionMachine { params: &session.negotiated.params, payload: &[], }, - self.config.hmac_key.as_deref(), + self.config.auth.hmac_key(), )?; Ok(PreparedProbe { bytes: bytes.into_boxed_slice(), @@ -679,7 +679,7 @@ impl SessionMachine { decode_echo_reply( packet, &session.negotiated.params, - self.config.hmac_key.as_deref(), + self.config.auth.hmac_key(), ) .ok() } diff --git a/crates/irtt-client/tests/client/hmac.rs b/crates/irtt-client/tests/client/hmac.rs index da4a310..41cafab 100644 --- a/crates/irtt-client/tests/client/hmac.rs +++ b/crates/irtt-client/tests/client/hmac.rs @@ -1,10 +1,11 @@ use super::*; +use irtt_client::{Authentication, HmacKey}; #[test] fn hmac_open_ignores_missing_hmac_before_valid_reply() { let key = b"secret".to_vec(); let mut config = default_test_config(SocketAddr::from(([127, 0, 0, 1], 1))); - config.hmac_key = Some(key.clone()); + config.auth = Authentication::Hmac(HmacKey::new(key.clone())); let params = default_params(); let server = start_fake_server(move |socket, tx| { let (_, peer) = recv_request(&socket, &tx); @@ -26,7 +27,7 @@ fn hmac_open_ignores_bad_hmac_before_valid_reply() { let key = b"secret".to_vec(); let wrong_key = b"wrong".to_vec(); let mut config = default_test_config(SocketAddr::from(([127, 0, 0, 1], 1))); - config.hmac_key = Some(key.clone()); + config.auth = Authentication::Hmac(HmacKey::new(key.clone())); let params = default_params(); let server = start_fake_server(move |socket, tx| { let (_, peer) = recv_request(&socket, &tx); @@ -47,7 +48,7 @@ fn hmac_open_ignores_bad_hmac_before_valid_reply() { fn post_token_hmac_negotiation_failure_sends_authenticated_cleanup_close() { let key = b"secret".to_vec(); let mut config = default_test_config(SocketAddr::from(([127, 0, 0, 1], 1))); - config.hmac_key = Some(key.clone()); + config.auth = Authentication::Hmac(HmacKey::new(key.clone())); let mut returned = default_params(); returned.interval_ns += 1; let server_key = key.clone(); diff --git a/crates/irtt-integration-tests/tests/async_client_lifecycle.rs b/crates/irtt-integration-tests/tests/async_client_lifecycle.rs index 8349de9..210e2ee 100644 --- a/crates/irtt-integration-tests/tests/async_client_lifecycle.rs +++ b/crates/irtt-integration-tests/tests/async_client_lifecycle.rs @@ -1,3 +1,4 @@ +use irtt_client::{Authentication, HmacKey}; use std::{ future::Future, net::UdpSocket, @@ -54,7 +55,9 @@ fn config(addr: SocketAddr, key: Option>, dscp: u8) -> ClientConfig { received_stats: ReceivedStats::None, stamp_at: StampAt::None, dscp, - hmac_key: key, + 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_millis(200)), diff --git a/crates/irtt-integration-tests/tests/hmac.rs b/crates/irtt-integration-tests/tests/hmac.rs index 4a3e560..b40bfa5 100644 --- a/crates/irtt-integration-tests/tests/hmac.rs +++ b/crates/irtt-integration-tests/tests/hmac.rs @@ -1,3 +1,4 @@ +use irtt_client::{Authentication, HmacKey}; mod support; use std::time::Duration; @@ -27,7 +28,7 @@ fn hmac_open_success_negotiates_without_warnings() { TimestampFields::default(), Some(key), |addr| ClientConfig { - hmac_key: Some(config_key), + auth: Authentication::Hmac(HmacKey::new(config_key)), ..config_for_params(addr, &config_params) }, ); @@ -52,7 +53,7 @@ fn hmac_echo_success_verifies_request_and_accepts_reply() { let run = run_one_probe_with_config(params.clone(), standard_timestamps(), Some(key), |addr| { ClientConfig { - hmac_key: Some(config_key), + auth: Authentication::Hmac(HmacKey::new(config_key)), ..config_for_params(addr, &config_params) } }); @@ -94,7 +95,7 @@ fn hmac_close_success_sends_authenticated_close_and_closes_session() { let params = default_params(); let server = start_hmac_close_server(params.clone(), key.clone()); let mut config = config_for_params(server.addr, ¶ms); - config.hmac_key = Some(key); + config.auth = Authentication::Hmac(HmacKey::new(key)); let mut client = Client::connect(config).unwrap(); let outcome = client.open().unwrap(); @@ -135,7 +136,9 @@ fn hmac_required_server_rejects_missing_or_wrong_client_key() { let server = start_hmac_required_open_drop_server(server_key, Duration::from_millis(250)); let mut config = config_for_params(server.addr, &default_params()); config.open_timeouts = vec![Duration::from_millis(200)]; - config.hmac_key = hmac_key; + config.auth = hmac_key.map_or(Authentication::Unauthenticated, |key| { + Authentication::Hmac(HmacKey::new(key)) + }); let mut client = Client::connect(config).unwrap(); assert!(matches!(client.open(), Err(ClientError::OpenTimeout))); @@ -158,7 +161,7 @@ fn bad_hmac_echo_reply_is_rejected_without_echo_reply_event() { let params = default_params(); let server = start_bad_hmac_echo_reply_server(params.clone(), key.clone()); let mut config = config_for_params(server.addr, ¶ms); - config.hmac_key = Some(key); + config.auth = Authentication::Hmac(HmacKey::new(key)); config.socket_config = SocketConfig { recv_timeout: Some(Duration::from_millis(500)), ..Default::default() @@ -205,7 +208,7 @@ fn hmac_open_reply_with_bad_hmac_is_ignored_until_timeout() { let params = default_params(); let server = support::start_bad_hmac_open_reply_server(params.clone(), key.clone(), wrong_key); let mut config = config_for_params(server.addr, ¶ms); - config.hmac_key = Some(key); + config.auth = Authentication::Hmac(HmacKey::new(key)); let mut client = Client::connect(config).unwrap(); assert!(matches!(client.open(), Err(ClientError::OpenTimeout))); @@ -221,7 +224,7 @@ fn non_hmac_client_open_does_not_set_hmac_flag() { ); let server = support::start_open_server(params.clone(), None); let mut config = config_for_params(server.addr, ¶ms); - config.hmac_key = None; + config.auth = Authentication::Unauthenticated; let mut client = Client::connect(config).unwrap(); let outcome = client.open().unwrap(); @@ -278,7 +281,7 @@ fn backend_hmac_correct_key_succeeds() { let params = default_params(); let peer = BackendPeer::start_open_echo(Some(key.clone())); let mut config = config_for_params(peer.addr(), ¶ms); - config.hmac_key = Some(key); + config.auth = Authentication::Hmac(HmacKey::new(key)); let mut client = Client::connect(config).unwrap(); let outcome = client.open().unwrap(); @@ -301,7 +304,9 @@ fn backend_hmac_required_rejects_missing_or_wrong_client_key() { let peer = BackendPeer::start_hmac_required(server_key); let mut config = config_for_params(peer.addr(), &default_params()); config.open_timeouts = vec![Duration::from_millis(200)]; - config.hmac_key = hmac_key; + config.auth = hmac_key.map_or(Authentication::Unauthenticated, |key| { + Authentication::Hmac(HmacKey::new(key)) + }); let mut client = Client::connect(config).unwrap(); assert!(matches!(client.open(), Err(ClientError::OpenTimeout))); diff --git a/crates/irtt-integration-tests/tests/managed_lifecycle.rs b/crates/irtt-integration-tests/tests/managed_lifecycle.rs index 6b8d054..a9b2114 100644 --- a/crates/irtt-integration-tests/tests/managed_lifecycle.rs +++ b/crates/irtt-integration-tests/tests/managed_lifecycle.rs @@ -1,3 +1,4 @@ +use irtt_client::{Authentication, HmacKey}; use std::{ future::Future, net::{SocketAddr, UdpSocket}, @@ -14,8 +15,8 @@ use irtt_proto::{ }; use tokio::runtime::{Builder, Runtime}; +use irtt_client::RunMode; use irtt_client::{managed::*, ClientConfig}; -use irtt_client::{ClientAuthConfig, RunMode}; use tokio::sync::broadcast; const TOKEN: u64 = 0x1234_5678_90ab_cdef; @@ -355,9 +356,7 @@ fn authenticated_peer_close_outcome() { let key = b"managed-peer-close".to_vec(); let server = start_server(ServerBehavior::PeerClose, Some(key.clone())); let mut configured = target("peer", server.addr); - configured.auth = Some(ClientAuthConfig { - hmac_key: Some(key), - }); + configured.auth = TargetAuth::Override(Authentication::Hmac(HmacKey::new(key))); let (task, _) = ManagedClient::task(config(ManagedPacing::Staggered), vec![configured]).unwrap(); let outcome = runtime().block_on(task); @@ -418,9 +417,7 @@ fn pending_limit_failure_drains_reply_and_closes_session() { managed.client.max_pending_probes = 1; managed.final_drain = Duration::from_millis(50); let mut configured = target("one", server.addr); - configured.auth = Some(ClientAuthConfig { - hmac_key: Some(key), - }); + configured.auth = TargetAuth::Override(Authentication::Hmac(HmacKey::new(key))); let (task, _) = ManagedClient::task(managed, vec![configured]).unwrap(); let outcome = runtime().block_on(async { tokio::time::timeout(Duration::from_secs(1), task) diff --git a/crates/irtt-integration-tests/tests/protocol_options.rs b/crates/irtt-integration-tests/tests/protocol_options.rs index c43ca36..fe5ac2d 100644 --- a/crates/irtt-integration-tests/tests/protocol_options.rs +++ b/crates/irtt-integration-tests/tests/protocol_options.rs @@ -1,3 +1,4 @@ +use irtt_client::{Authentication, HmacKey}; mod support; mod protocol_options { pub mod backend_smoke; @@ -148,7 +149,7 @@ fn hmac_rich_mode_uses_negotiated_echo_layout_and_decodes_reply() { let config_key = key.clone(); let run = run_one_probe_with_config(params.clone(), standard_timestamps(), Some(key), |addr| { ClientConfig { - hmac_key: Some(config_key), + auth: Authentication::Hmac(HmacKey::new(config_key)), ..config_for_params(addr, &config_params) } });