feat(app): allow hexadecimal HMAC key specification and redact keys from debug output
This commit is contained in:
14 files changed
+72
-40
No files matched your search
@@ -26,8 +26,9 @@ pub struct CommonClientArgs {
|
||||
#[arg(long, default_value_t = 0, value_parser = parse_length)]
|
||||
pub length: u32,
|
||||
|
||||
/// HMAC key.
|
||||
#[arg(long, value_parser = parse_hmac_key)]
|
||||
// Decode during configuration preparation so Clap cannot echo rejected keys.
|
||||
/// HMAC key: literal UTF-8 or lowercase 0x followed by hexadecimal bytes.
|
||||
#[arg(long)]
|
||||
pub hmac: Option<String>,
|
||||
|
||||
/// Clock mode to request.
|
||||
@@ -267,13 +268,6 @@ pub fn parse_length(input: &str) -> Result<u32, String> {
|
||||
Ok(length)
|
||||
}
|
||||
|
||||
pub fn parse_hmac_key(input: &str) -> Result<String, String> {
|
||||
if input.is_empty() {
|
||||
return Err("HMAC key must not be empty".to_owned());
|
||||
}
|
||||
Ok(input.to_owned())
|
||||
}
|
||||
|
||||
pub fn parse_server_fill(input: &str) -> Result<String, String> {
|
||||
if input.is_empty() {
|
||||
return Err("server fill must not be empty".to_owned());
|
||||
|
||||
@@ -21,8 +21,8 @@ impl CommonClientArgs {
|
||||
/// Build the shared client configuration template for a managed run.
|
||||
///
|
||||
/// The config is endpoint-independent; managed targets own their addresses.
|
||||
pub fn to_client_config(&self, duration: Duration) -> ClientConfig {
|
||||
ClientConfig {
|
||||
pub fn to_client_config(&self, duration: Duration) -> Result<ClientConfig, String> {
|
||||
Ok(ClientConfig {
|
||||
address_family: self.address_family(),
|
||||
open: OpenPolicy {
|
||||
negotiation: if self.loose {
|
||||
@@ -45,9 +45,11 @@ impl CommonClientArgs {
|
||||
},
|
||||
auth: self
|
||||
.hmac
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map(crate::shared::decode_hmac_key)
|
||||
.transpose()?
|
||||
.map_or(Authentication::Unauthenticated, |key| {
|
||||
Authentication::Hmac(HmacKey::new(key.as_bytes()))
|
||||
Authentication::Hmac(HmacKey::new(key))
|
||||
}),
|
||||
socket: SocketConfig {
|
||||
bind_addr: self.bind_addr,
|
||||
@@ -61,7 +63,7 @@ impl CommonClientArgs {
|
||||
..SocketConfig::default()
|
||||
},
|
||||
..ClientConfig::default()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ pub async fn prepare_managed_run(
|
||||
.await?;
|
||||
Ok(ManagedRunSetup {
|
||||
targets,
|
||||
client: common.to_client_config(duration),
|
||||
client: common.to_client_config(duration)?,
|
||||
pacing: selection.pacing.into(),
|
||||
stdin_controlled: selection.stdin_controlled,
|
||||
preparation,
|
||||
|
||||
@@ -88,12 +88,13 @@ impl From<GroupPacingArg> for ManagedPacing {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum TargetParseError {
|
||||
EmptyLabel,
|
||||
EmptyAddress,
|
||||
InvalidEscape,
|
||||
EmptyTarget,
|
||||
InvalidHmac(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetParseError {
|
||||
@@ -103,6 +104,7 @@ impl fmt::Display for TargetParseError {
|
||||
Self::EmptyAddress => "target address must not be empty",
|
||||
Self::InvalidEscape => "invalid target escape sequence",
|
||||
Self::EmptyTarget => "target must not be empty",
|
||||
Self::InvalidHmac(message) => message,
|
||||
};
|
||||
f.write_str(message)
|
||||
}
|
||||
@@ -197,7 +199,10 @@ fn parse_target_syntax(
|
||||
let auth = if value.is_empty() {
|
||||
TargetAuth::Override(Authentication::Unauthenticated)
|
||||
} else {
|
||||
TargetAuth::Override(Authentication::Hmac(HmacKey::new(value.into_bytes())))
|
||||
TargetAuth::Override(Authentication::Hmac(HmacKey::new(
|
||||
crate::shared::decode_hmac_key(&value)
|
||||
.map_err(TargetParseError::InvalidHmac)?,
|
||||
)))
|
||||
};
|
||||
(&input[..index], auth)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/// Decode CLI key syntax without ever including key material in diagnostics.
|
||||
pub(crate) fn decode_hmac_key(input: &str) -> Result<Vec<u8>, String> {
|
||||
let key = match input.strip_prefix("0x") {
|
||||
Some(hex) => hex::decode(hex).map_err(|_| {
|
||||
"HMAC hexadecimal key must contain an even number of valid hexadecimal digits"
|
||||
.to_owned()
|
||||
})?,
|
||||
None => input.as_bytes().to_vec(),
|
||||
};
|
||||
if key.is_empty() {
|
||||
return Err("HMAC key must not be empty".to_owned());
|
||||
}
|
||||
Ok(key)
|
||||
}
|
||||
@@ -1,2 +1,7 @@
|
||||
#[cfg(any(feature = "client", feature = "tui"))]
|
||||
pub mod client;
|
||||
|
||||
#[cfg(any(feature = "client", feature = "tui", feature = "server"))]
|
||||
mod hmac;
|
||||
#[cfg(any(feature = "client", feature = "tui", feature = "server"))]
|
||||
pub(crate) use hmac::decode_hmac_key;
|
||||
Reference in new issue
Block a user