refactor(client): make authentication explicit and redact HMAC keys

This commit is contained in:
Nils Andreas Svee committed 2026-10-04 16:58:44 +07:00
1 parent 9c703ecff2
commit 156db4c720
16 files changed
+184 -119

No files matched your search

+19 -2
View File
@@ -1,10 +1,10 @@
use std::time::Duration; use std::{fmt, time::Duration};
use clap::ValueEnum; use clap::ValueEnum;
use irtt_client::{MAX_DSCP_CODEPOINT, MAX_SERVER_FILL_BYTES, MAX_TTL, MAX_UDP_PAYLOAD_LENGTH}; use irtt_client::{MAX_DSCP_CODEPOINT, MAX_SERVER_FILL_BYTES, MAX_TTL, MAX_UDP_PAYLOAD_LENGTH};
use irtt_proto::{Clock, ReceivedStats, StampAt}; use irtt_proto::{Clock, ReceivedStats, StampAt};
#[derive(Debug, Clone, clap::Args)] #[derive(Clone, clap::Args)]
pub struct CommonClientArgs { pub struct CommonClientArgs {
/// Probe interval. /// Probe interval.
#[arg(long, default_value = "1s", value_parser = parse_duration)] #[arg(long, default_value = "1s", value_parser = parse_duration)]
@@ -63,6 +63,23 @@ pub struct CommonClientArgs {
pub loose: bool, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum ClockArg { pub enum ClockArg {
Wall, Wall,
+7 -2
View File
@@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
use irtt_client::{ClientConfig, NegotiationPolicy, SocketConfig}; use irtt_client::{Authentication, ClientConfig, HmacKey, NegotiationPolicy, SocketConfig};
use super::args::CommonClientArgs; use super::args::CommonClientArgs;
@@ -21,7 +21,12 @@ impl CommonClientArgs {
stamp_at: self.tstamp.into(), stamp_at: self.tstamp.into(),
clock: self.clock.into(), clock: self.clock.into(),
dscp: self.dscp, 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(), server_fill: self.server_fill.clone(),
negotiation_policy: if self.loose { negotiation_policy: if self.loose {
NegotiationPolicy::Loose NegotiationPolicy::Loose
+6 -38
View File
@@ -2,8 +2,8 @@ use std::{collections::HashSet, fmt};
use clap::ValueEnum; use clap::ValueEnum;
use irtt_client::{ use irtt_client::{
managed::{ManagedPacing, ManagedTargetConfig, TargetId}, managed::{ManagedPacing, ManagedTargetConfig, TargetAuth, TargetId},
ClientAuthConfig, Authentication, HmacKey,
}; };
/// One raw positional target captured by Clap. /// One raw positional target captured by Clap.
@@ -35,23 +35,6 @@ pub fn parse_target(input: &str) -> Result<TargetArg, String> {
Ok(TargetArg::new(input)) Ok(TargetArg::new(input))
} }
#[derive(Clone, PartialEq, Eq)]
pub enum TargetAuth {
Inherit,
Override(Vec<u8>),
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(<redacted>)"),
Self::Disable => f.write_str("Disable"),
}
}
}
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub struct TargetSpec { pub struct TargetSpec {
pub label: String, pub label: String,
@@ -80,16 +63,7 @@ impl fmt::Debug for PreparedTarget {
f.debug_struct("PreparedTarget") f.debug_struct("PreparedTarget")
.field("label", &self.label) .field("label", &self.label)
.field("server_addr", &self.managed.server_addr) .field("server_addr", &self.managed.server_addr)
.field( .field("auth", &self.managed.auth)
"auth",
&match &self.managed.auth {
None => TargetAuth::Inherit,
Some(ClientAuthConfig { hmac_key: Some(_) }) => {
TargetAuth::Override(Vec::new())
}
Some(ClientAuthConfig { hmac_key: None }) => TargetAuth::Disable,
},
)
.finish() .finish()
} }
} }
@@ -216,9 +190,9 @@ fn parse_target_syntax(
Some(index) => { Some(index) => {
let value = unescape(&input[index + "@hmac=".len()..])?; let value = unescape(&input[index + "@hmac=".len()..])?;
let auth = if value.is_empty() { let auth = if value.is_empty() {
TargetAuth::Disable TargetAuth::Override(Authentication::Unauthenticated)
} else { } else {
TargetAuth::Override(value.into_bytes()) TargetAuth::Override(Authentication::Hmac(HmacKey::new(value.into_bytes())))
}; };
(&input[..index], auth) (&input[..index], auth)
} }
@@ -287,13 +261,7 @@ pub fn prepare_managed_targets(specs: Vec<TargetSpec>) -> Result<Vec<PreparedTar
let mut targets = Vec::with_capacity(specs.len()); let mut targets = Vec::with_capacity(specs.len());
for spec in specs { for spec in specs {
let mut managed = ManagedTargetConfig::new(TargetId::from(spec.label.clone()), spec.addr); let mut managed = ManagedTargetConfig::new(TargetId::from(spec.label.clone()), spec.addr);
managed.auth = match spec.auth { managed.auth = spec.auth;
TargetAuth::Inherit => None,
TargetAuth::Override(hmac_key) => Some(ClientAuthConfig {
hmac_key: Some(hmac_key),
}),
TargetAuth::Disable => Some(ClientAuthConfig { hmac_key: None }),
};
targets.push(PreparedTarget { targets.push(PreparedTarget {
label: spec.label, label: spec.label,
managed, managed,
+17 -9
View File
@@ -149,29 +149,37 @@ fn default_client_output_emits_a_packet_summary() {
fn target_authentication_inherits_overrides_or_disables_the_global_key() { fn target_authentication_inherits_overrides_or_disables_the_global_key() {
use clap::Parser; use clap::Parser;
use irtt_app::cmd::client::args::ClientArgs; use irtt_app::cmd::client::args::ClientArgs;
use irtt_client::{managed::TargetAuth, Authentication, HmacKey};
let args = ClientArgs::try_parse_from([ let args = ClientArgs::try_parse_from([
"irtt-client", "irtt-client",
"--hmac", "--hmac",
"global-test-key", "CONSPICUOUS_GLOBAL_HMAC_KEY",
"inherit=127.0.0.1:2112", "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=", "disable=127.0.0.1:2114@hmac=",
]) ])
.unwrap(); .unwrap();
let setup = args.prepare().unwrap(); let setup = args.prepare().unwrap();
assert_eq!( assert_eq!(
setup.managed_config().client.hmac_key.as_deref(), setup.managed_config().client.auth,
Some(b"global-test-key".as_slice()) Authentication::Hmac(HmacKey::new(b"CONSPICUOUS_GLOBAL_HMAC_KEY".as_slice()))
); );
let targets = setup.managed_targets(); let targets = setup.managed_targets();
let authentication: Vec<_> = targets let authentication: Vec<_> = targets.iter().map(|target| target.auth.clone()).collect();
.iter()
.map(|target| target.auth.as_ref().map(|auth| auth.hmac_key.as_deref()))
.collect();
assert_eq!( assert_eq!(
authentication, 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. // A real authenticated server detects broken client argument-to-config mapping.
+19
View File
@@ -34,6 +34,25 @@ dependency at all.
See `examples/` in the repository for runnable examples of each tier. 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 ## Documentation
Full API documentation is on [docs.rs/irtt-client](https://docs.rs/irtt-client). Full API documentation is on [docs.rs/irtt-client](https://docs.rs/irtt-client).
+44
View File
@@ -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<Arc<[u8]>>) -> 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()),
}
}
}
+6 -29
View File
@@ -1,5 +1,7 @@
use std::{net::SocketAddr, time::Duration}; use std::{net::SocketAddr, time::Duration};
use crate::Authentication;
/// Protocol compatibility bound for a requested `server_fill` value, in UTF-8 /// Protocol compatibility bound for a requested `server_fill` value, in UTF-8
/// bytes. /// bytes.
/// ///
@@ -118,11 +120,10 @@ pub struct ClientConfig {
/// This is the six-bit DSCP value, not the full traffic-class byte, and /// This is the six-bit DSCP value, not the full traffic-class byte, and
/// must be less than or equal to [`MAX_DSCP_CODEPOINT`]. /// must be less than or equal to [`MAX_DSCP_CODEPOINT`].
pub dscp: u8, 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 peers must use the same key. An empty HMAC key remains authenticated.
/// HMAC authentication. The peer must be configured with the same key. pub auth: Authentication,
pub hmac_key: Option<Vec<u8>>,
/// Optional server payload fill request. /// Optional server payload fill request.
/// ///
/// `None` leaves server fill behavior unspecified. `Some(value)` requests /// `None` leaves server fill behavior unspecified. `Some(value)` requests
@@ -155,30 +156,6 @@ pub struct ClientConfig {
pub max_pending_probes: usize, 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<Vec<u8>>,
}
impl From<Option<Vec<u8>>> for ClientAuthConfig {
fn from(hmac_key: Option<Vec<u8>>) -> Self {
Self { hmac_key }
}
}
impl From<ClientAuthConfig> for Option<Vec<u8>> {
fn from(auth: ClientAuthConfig) -> Self {
auth.hmac_key
}
}
impl Default for ClientConfig { impl Default for ClientConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
@@ -190,7 +167,7 @@ impl Default for ClientConfig {
stamp_at: StampAt::Both, stamp_at: StampAt::Both,
clock: Clock::Both, clock: Clock::Both,
dscp: 0, dscp: 0,
hmac_key: None, auth: Authentication::Unauthenticated,
server_fill: None, server_fill: None,
open_timeouts: DEFAULT_OPEN_TIMEOUTS.to_vec(), open_timeouts: DEFAULT_OPEN_TIMEOUTS.to_vec(),
run_mode: RunMode::Normal, run_mode: RunMode::Normal,
+4 -2
View File
@@ -34,6 +34,7 @@
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
mod async_client; mod async_client;
mod auth;
mod client; mod client;
mod config; mod config;
mod error; mod error;
@@ -49,10 +50,11 @@ mod timing;
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
pub use async_client::AsyncClient; pub use async_client::AsyncClient;
pub use auth::{Authentication, HmacKey};
pub use client::Client; pub use client::Client;
pub use config::{ pub use config::{
ClientAuthConfig, ClientConfig, NegotiationPolicy, RecvBudget, RunMode, SocketConfig, ClientConfig, NegotiationPolicy, RecvBudget, RunMode, SocketConfig, MAX_DSCP_CODEPOINT,
MAX_DSCP_CODEPOINT, MAX_SERVER_FILL_BYTES, MAX_TTL, MAX_UDP_PAYLOAD_LENGTH, MAX_SERVER_FILL_BYTES, MAX_TTL, MAX_UDP_PAYLOAD_LENGTH,
}; };
pub use error::ClientError; pub use error::ClientError;
pub use event::{ pub use event::{
+26 -4
View File
@@ -2,7 +2,7 @@ use std::{fmt, net::SocketAddr, sync::Arc, time::Duration};
use thiserror::Error; use thiserror::Error;
use crate::{ClientAuthConfig, ClientConfig, ClientError, ClientEvent}; use crate::{Authentication, ClientConfig, ClientError, ClientEvent};
use super::TargetId; use super::TargetId;
@@ -26,12 +26,34 @@ pub struct TargetInstance {
pub generation: u64, 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)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct ManagedTargetConfig { pub struct ManagedTargetConfig {
pub id: TargetId, pub id: TargetId,
pub server_addr: String, pub server_addr: String,
pub auth: Option<ClientAuthConfig>, pub auth: TargetAuth,
} }
impl ManagedTargetConfig { impl ManagedTargetConfig {
@@ -39,7 +61,7 @@ impl ManagedTargetConfig {
Self { Self {
id: id.into(), id: id.into(),
server_addr: server_addr.into(), server_addr: server_addr.into(),
auth: None, auth: TargetAuth::Inherit,
} }
} }
} }
+2 -6
View File
@@ -945,9 +945,7 @@ impl ManagedClientTask {
} }
let mut client_config = self.config.client.clone(); let mut client_config = self.config.client.clone();
client_config.server_addr.clone_from(&target.server_addr); client_config.server_addr.clone_from(&target.server_addr);
if let Some(auth) = &target.auth { client_config.auth = target.auth.resolve(&self.config.client.auth);
client_config.hmac_key.clone_from(&auth.hmac_key);
}
validate_target_config(&client_config).map_err(|source| { validate_target_config(&client_config).map_err(|source| {
ManagedCommandApplyError::InvalidTarget { ManagedCommandApplyError::InvalidTarget {
id: target.id.clone(), id: target.id.clone(),
@@ -2251,9 +2249,7 @@ fn build_task(
.ok_or(ManagedConfigError::GenerationExhausted)?; .ok_or(ManagedConfigError::GenerationExhausted)?;
let mut client_config = config.client.clone(); let mut client_config = config.client.clone();
client_config.server_addr.clone_from(&target.server_addr); client_config.server_addr.clone_from(&target.server_addr);
if let Some(auth) = &target.auth { client_config.auth = target.auth.resolve(&config.client.auth);
client_config.hmac_key.clone_from(&auth.hmac_key);
}
validate_target_config(&client_config).map_err(|source| { validate_target_config(&client_config).map_err(|source| {
ManagedConfigError::InvalidTarget { ManagedConfigError::InvalidTarget {
id: target.id.clone(), id: target.id.clone(),
+6 -6
View File
@@ -201,7 +201,7 @@ impl SessionMachine {
} }
pub(crate) fn has_hmac(&self) -> bool { 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<PreparedOpenRequest, ClientError> { pub(crate) fn prepare_open_request(&self) -> Result<PreparedOpenRequest, ClientError> {
@@ -211,7 +211,7 @@ impl SessionMachine {
params: &self.requested, params: &self.requested,
no_test: self.config.run_mode == RunMode::NoTest, no_test: self.config.run_mode == RunMode::NoTest,
}, },
self.config.hmac_key.as_deref(), self.config.auth.hmac_key(),
)?; )?;
Ok(PreparedOpenRequest { Ok(PreparedOpenRequest {
bytes: bytes.into_boxed_slice(), bytes: bytes.into_boxed_slice(),
@@ -223,7 +223,7 @@ impl SessionMachine {
packet: &[u8], packet: &[u8],
) -> Result<OpenDatagramDisposition, ClientError> { ) -> Result<OpenDatagramDisposition, ClientError> {
self.ensure_connected()?; 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)), Ok(reply) => Ok(OpenDatagramDisposition::Trusted(reply)),
Err(error @ irtt_proto::ProtoError::ZeroToken) => Err(ClientError::Protocol(error)), Err(error @ irtt_proto::ProtoError::ZeroToken) => Err(ClientError::Protocol(error)),
Err( Err(
@@ -250,7 +250,7 @@ impl SessionMachine {
let cleanup_close = if !reply_is_close && reply.token != 0 { let cleanup_close = if !reply_is_close && reply.token != 0 {
let bytes = encode_request( let bytes = encode_request(
RequestToEncode::Close { token: reply.token }, RequestToEncode::Close { token: reply.token },
self.config.hmac_key.as_deref(), self.config.auth.hmac_key(),
) )
.map_err(ClientError::from) .map_err(ClientError::from)
.map_err(OpenAcceptanceFailure::without_cleanup)?; .map_err(OpenAcceptanceFailure::without_cleanup)?;
@@ -313,7 +313,7 @@ impl SessionMachine {
params: &session.negotiated.params, params: &session.negotiated.params,
payload: &[], payload: &[],
}, },
self.config.hmac_key.as_deref(), self.config.auth.hmac_key(),
)?; )?;
Ok(PreparedProbe { Ok(PreparedProbe {
bytes: bytes.into_boxed_slice(), bytes: bytes.into_boxed_slice(),
@@ -679,7 +679,7 @@ impl SessionMachine {
decode_echo_reply( decode_echo_reply(
packet, packet,
&session.negotiated.params, &session.negotiated.params,
self.config.hmac_key.as_deref(), self.config.auth.hmac_key(),
) )
.ok() .ok()
} }
+4 -3
View File
@@ -1,10 +1,11 @@
use super::*; use super::*;
use irtt_client::{Authentication, HmacKey};
#[test] #[test]
fn hmac_open_ignores_missing_hmac_before_valid_reply() { fn hmac_open_ignores_missing_hmac_before_valid_reply() {
let key = b"secret".to_vec(); let key = b"secret".to_vec();
let mut config = default_test_config(SocketAddr::from(([127, 0, 0, 1], 1))); 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 params = default_params();
let server = start_fake_server(move |socket, tx| { let server = start_fake_server(move |socket, tx| {
let (_, peer) = recv_request(&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 key = b"secret".to_vec();
let wrong_key = b"wrong".to_vec(); let wrong_key = b"wrong".to_vec();
let mut config = default_test_config(SocketAddr::from(([127, 0, 0, 1], 1))); 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 params = default_params();
let server = start_fake_server(move |socket, tx| { let server = start_fake_server(move |socket, tx| {
let (_, peer) = recv_request(&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() { fn post_token_hmac_negotiation_failure_sends_authenticated_cleanup_close() {
let key = b"secret".to_vec(); let key = b"secret".to_vec();
let mut config = default_test_config(SocketAddr::from(([127, 0, 0, 1], 1))); 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(); let mut returned = default_params();
returned.interval_ns += 1; returned.interval_ns += 1;
let server_key = key.clone(); let server_key = key.clone();
@@ -1,3 +1,4 @@
use irtt_client::{Authentication, HmacKey};
use std::{ use std::{
future::Future, future::Future,
net::UdpSocket, net::UdpSocket,
@@ -54,7 +55,9 @@ fn config(addr: SocketAddr, key: Option<Vec<u8>>, dscp: u8) -> ClientConfig {
received_stats: ReceivedStats::None, received_stats: ReceivedStats::None,
stamp_at: StampAt::None, stamp_at: StampAt::None,
dscp, dscp,
hmac_key: key, auth: key.map_or(Authentication::Unauthenticated, |key| {
Authentication::Hmac(HmacKey::new(key))
}),
open_timeouts: vec![Duration::from_millis(200)], open_timeouts: vec![Duration::from_millis(200)],
socket_config: SocketConfig { socket_config: SocketConfig {
recv_timeout: Some(Duration::from_millis(200)), recv_timeout: Some(Duration::from_millis(200)),
+14 -9
View File
@@ -1,3 +1,4 @@
use irtt_client::{Authentication, HmacKey};
mod support; mod support;
use std::time::Duration; use std::time::Duration;
@@ -27,7 +28,7 @@ fn hmac_open_success_negotiates_without_warnings() {
TimestampFields::default(), TimestampFields::default(),
Some(key), Some(key),
|addr| ClientConfig { |addr| ClientConfig {
hmac_key: Some(config_key), auth: Authentication::Hmac(HmacKey::new(config_key)),
..config_for_params(addr, &config_params) ..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| { let run = run_one_probe_with_config(params.clone(), standard_timestamps(), Some(key), |addr| {
ClientConfig { ClientConfig {
hmac_key: Some(config_key), auth: Authentication::Hmac(HmacKey::new(config_key)),
..config_for_params(addr, &config_params) ..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 params = default_params();
let server = start_hmac_close_server(params.clone(), key.clone()); let server = start_hmac_close_server(params.clone(), key.clone());
let mut config = config_for_params(server.addr, &params); let mut config = config_for_params(server.addr, &params);
config.hmac_key = Some(key); config.auth = Authentication::Hmac(HmacKey::new(key));
let mut client = Client::connect(config).unwrap(); let mut client = Client::connect(config).unwrap();
let outcome = client.open().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 server = start_hmac_required_open_drop_server(server_key, Duration::from_millis(250));
let mut config = config_for_params(server.addr, &default_params()); let mut config = config_for_params(server.addr, &default_params());
config.open_timeouts = vec![Duration::from_millis(200)]; 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(); let mut client = Client::connect(config).unwrap();
assert!(matches!(client.open(), Err(ClientError::OpenTimeout))); 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 params = default_params();
let server = start_bad_hmac_echo_reply_server(params.clone(), key.clone()); let server = start_bad_hmac_echo_reply_server(params.clone(), key.clone());
let mut config = config_for_params(server.addr, &params); let mut config = config_for_params(server.addr, &params);
config.hmac_key = Some(key); config.auth = Authentication::Hmac(HmacKey::new(key));
config.socket_config = SocketConfig { config.socket_config = SocketConfig {
recv_timeout: Some(Duration::from_millis(500)), recv_timeout: Some(Duration::from_millis(500)),
..Default::default() ..Default::default()
@@ -205,7 +208,7 @@ fn hmac_open_reply_with_bad_hmac_is_ignored_until_timeout() {
let params = default_params(); let params = default_params();
let server = support::start_bad_hmac_open_reply_server(params.clone(), key.clone(), wrong_key); let server = support::start_bad_hmac_open_reply_server(params.clone(), key.clone(), wrong_key);
let mut config = config_for_params(server.addr, &params); let mut config = config_for_params(server.addr, &params);
config.hmac_key = Some(key); config.auth = Authentication::Hmac(HmacKey::new(key));
let mut client = Client::connect(config).unwrap(); let mut client = Client::connect(config).unwrap();
assert!(matches!(client.open(), Err(ClientError::OpenTimeout))); 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 server = support::start_open_server(params.clone(), None);
let mut config = config_for_params(server.addr, &params); let mut config = config_for_params(server.addr, &params);
config.hmac_key = None; config.auth = Authentication::Unauthenticated;
let mut client = Client::connect(config).unwrap(); let mut client = Client::connect(config).unwrap();
let outcome = client.open().unwrap(); let outcome = client.open().unwrap();
@@ -278,7 +281,7 @@ fn backend_hmac_correct_key_succeeds() {
let params = default_params(); let params = default_params();
let peer = BackendPeer::start_open_echo(Some(key.clone())); let peer = BackendPeer::start_open_echo(Some(key.clone()));
let mut config = config_for_params(peer.addr(), &params); let mut config = config_for_params(peer.addr(), &params);
config.hmac_key = Some(key); config.auth = Authentication::Hmac(HmacKey::new(key));
let mut client = Client::connect(config).unwrap(); let mut client = Client::connect(config).unwrap();
let outcome = client.open().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 peer = BackendPeer::start_hmac_required(server_key);
let mut config = config_for_params(peer.addr(), &default_params()); let mut config = config_for_params(peer.addr(), &default_params());
config.open_timeouts = vec![Duration::from_millis(200)]; 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(); let mut client = Client::connect(config).unwrap();
assert!(matches!(client.open(), Err(ClientError::OpenTimeout))); assert!(matches!(client.open(), Err(ClientError::OpenTimeout)));
@@ -1,3 +1,4 @@
use irtt_client::{Authentication, HmacKey};
use std::{ use std::{
future::Future, future::Future,
net::{SocketAddr, UdpSocket}, net::{SocketAddr, UdpSocket},
@@ -14,8 +15,8 @@ use irtt_proto::{
}; };
use tokio::runtime::{Builder, Runtime}; use tokio::runtime::{Builder, Runtime};
use irtt_client::RunMode;
use irtt_client::{managed::*, ClientConfig}; use irtt_client::{managed::*, ClientConfig};
use irtt_client::{ClientAuthConfig, RunMode};
use tokio::sync::broadcast; use tokio::sync::broadcast;
const TOKEN: u64 = 0x1234_5678_90ab_cdef; 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 key = b"managed-peer-close".to_vec();
let server = start_server(ServerBehavior::PeerClose, Some(key.clone())); let server = start_server(ServerBehavior::PeerClose, Some(key.clone()));
let mut configured = target("peer", server.addr); let mut configured = target("peer", server.addr);
configured.auth = Some(ClientAuthConfig { configured.auth = TargetAuth::Override(Authentication::Hmac(HmacKey::new(key)));
hmac_key: Some(key),
});
let (task, _) = let (task, _) =
ManagedClient::task(config(ManagedPacing::Staggered), vec![configured]).unwrap(); ManagedClient::task(config(ManagedPacing::Staggered), vec![configured]).unwrap();
let outcome = runtime().block_on(task); 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.client.max_pending_probes = 1;
managed.final_drain = Duration::from_millis(50); managed.final_drain = Duration::from_millis(50);
let mut configured = target("one", server.addr); let mut configured = target("one", server.addr);
configured.auth = Some(ClientAuthConfig { configured.auth = TargetAuth::Override(Authentication::Hmac(HmacKey::new(key)));
hmac_key: Some(key),
});
let (task, _) = ManagedClient::task(managed, vec![configured]).unwrap(); let (task, _) = ManagedClient::task(managed, vec![configured]).unwrap();
let outcome = runtime().block_on(async { let outcome = runtime().block_on(async {
tokio::time::timeout(Duration::from_secs(1), task) tokio::time::timeout(Duration::from_secs(1), task)
@@ -1,3 +1,4 @@
use irtt_client::{Authentication, HmacKey};
mod support; mod support;
mod protocol_options { mod protocol_options {
pub mod backend_smoke; 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 config_key = key.clone();
let run = run_one_probe_with_config(params.clone(), standard_timestamps(), Some(key), |addr| { let run = run_one_probe_with_config(params.clone(), standard_timestamps(), Some(key), |addr| {
ClientConfig { ClientConfig {
hmac_key: Some(config_key), auth: Authentication::Hmac(HmacKey::new(config_key)),
..config_for_params(addr, &config_params) ..config_for_params(addr, &config_params)
} }
}); });