stamparm/maltrail · warning

HMAC accepts a key of any length

Error message

HMAC accepts a key of any length

What it means

mts_sign builds an HMAC-SHA256 via SimpleHmac::new_from_slice(secret.as_bytes()).expect("HMAC accepts a key of any length"). Unlike the block-size-limited Hmac wrapper, SimpleHmac accepts arbitrary key lengths, so new_from_slice can only fail on allocation failure; the expect documents that any secret string is valid.

Solutions

  1. Keep SimpleHmac (arbitrary-length keys) or switch to Hmac and handle new_from_slice's Result explicitly
  2. If using Hmac, hash or pad the secret to a valid length before constructing the MAC
  3. Propagate the error instead of expect if construction can realistically fail

Example fix

// before
let mut mac = SimpleHmac::<sha2::Sha256>::new_from_slice(secret.as_bytes()).expect("HMAC accepts a key of any length");
// after (if switching to Hmac)
let mut mac = hmac::Hmac::<sha2::Sha256>::new_from_slice(secret.as_bytes())
    .map_err(|e| format!("HMAC key rejected: {e}"))?;
Defensive patterns

Strategy: validation

Validate before calling

assert!(!secret.is_empty(), "MTS secret must be set"); // SimpleHmac accepts any length; only allocation can fail

Try / catch

match SimpleHmac::<sha2::Sha256>::new_from_slice(secret.as_bytes()) {
    Ok(mut mac) => { mac.update(payload); /* ... */ },
    Err(e) => eprintln!("HMAC init failed: {e}"),
}

Prevention

When it happens

Trigger: Practically unreachable with SimpleHmac: only an allocation failure (OOM) during key processing panics. Would become reachable if someone swapped SimpleHmac for hmac::Hmac without handling the error.

Common situations: Refactoring the MAC construction and switching types; OOM on extremely constrained hosts.

Related errors


AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13). Data as JSON: /api/errors/dff48cfc719a1d41. Report an issue: GitHub.

Appendix: source

Thrown at sensor/src/output.rs:43

use crate::ignore::IgnoreRules;
use crate::settings;
use crate::whitelist::Whitelist;

/// Immutable, shared output configuration.
/// `MTS1 <32 hex chars> <payload>` - the authenticated framing for a LOG_SERVER datagram.
///
/// The listener on the other end is otherwise open by protocol design: anything that can reach the
/// port can append to the log an operator reasons from. This is the sending half of closing that;
/// `core/log.py:mts_open` is the receiving half and the two are pinned together by generated
/// vectors, because a MAC that disagrees across the two implementations fails as silent data loss -
/// the server simply drops every event this sensor sends, and nothing says why.
///
/// HMAC-SHA256 truncated to 128 bits (RFC 2104 section 5), hex-encoded so the datagram stays
/// greppable text like everything else on this path.
pub fn mts_sign(secret: &str, payload: &[u8]) -> Vec<u8> {
    use hmac::{Mac, SimpleHmac};
    let mut mac =
        SimpleHmac::<sha2::Sha256>::new_from_slice(secret.as_bytes()).expect("HMAC accepts a key of any length");
    mac.update(payload);
    let tag = mac.finalize().into_bytes();

    let mut out = Vec::with_capacity(5 + 32 + 1 + payload.len());
    out.extend_from_slice(b"MTS1 ");
    for byte in &tag[..16] {
        out.push(HEX[(byte >> 4) as usize]);
        out.push(HEX[(byte & 0x0f) as usize]);
    }
    out.push(b' ');
    out.extend_from_slice(payload);
    out
}

const HEX: &[u8; 16] = b"0123456789abcdef";

#[cfg(test)]
mod mts_tests {

View on GitHub (pinned to 77cfb06d76)