{"record":{"id":"dff48cfc719a1d41","repo":"stamparm/maltrail","slug":"hmac-accepts-a-key-of-any-length","errorCode":null,"errorMessage":"HMAC accepts a key of any length","messagePattern":"HMAC accepts a key of any length","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"sensor/src/output.rs","lineNumber":43,"sourceCode":"use crate::ignore::IgnoreRules;\nuse crate::settings;\nuse crate::whitelist::Whitelist;\n\n/// Immutable, shared output configuration.\n/// `MTS1 <32 hex chars> <payload>` - the authenticated framing for a LOG_SERVER datagram.\n///\n/// The listener on the other end is otherwise open by protocol design: anything that can reach the\n/// port can append to the log an operator reasons from. This is the sending half of closing that;\n/// `core/log.py:mts_open` is the receiving half and the two are pinned together by generated\n/// vectors, because a MAC that disagrees across the two implementations fails as silent data loss -\n/// the server simply drops every event this sensor sends, and nothing says why.\n///\n/// HMAC-SHA256 truncated to 128 bits (RFC 2104 section 5), hex-encoded so the datagram stays\n/// greppable text like everything else on this path.\npub fn mts_sign(secret: &str, payload: &[u8]) -> Vec<u8> {\n    use hmac::{Mac, SimpleHmac};\n    let mut mac =\n        SimpleHmac::<sha2::Sha256>::new_from_slice(secret.as_bytes()).expect(\"HMAC accepts a key of any length\");\n    mac.update(payload);\n    let tag = mac.finalize().into_bytes();\n\n    let mut out = Vec::with_capacity(5 + 32 + 1 + payload.len());\n    out.extend_from_slice(b\"MTS1 \");\n    for byte in &tag[..16] {\n        out.push(HEX[(byte >> 4) as usize]);\n        out.push(HEX[(byte & 0x0f) as usize]);\n    }\n    out.push(b' ');\n    out.extend_from_slice(payload);\n    out\n}\n\nconst HEX: &[u8; 16] = b\"0123456789abcdef\";\n\n#[cfg(test)]\nmod mts_tests {","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/src/output.rs#L25-L61","documentation":"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.","triggerScenarios":"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.","commonSituations":"Refactoring the MAC construction and switching types; OOM on extremely constrained hosts.","solutions":["Keep SimpleHmac (arbitrary-length keys) or switch to Hmac and handle new_from_slice's Result explicitly","If using Hmac, hash or pad the secret to a valid length before constructing the MAC","Propagate the error instead of expect if construction can realistically fail"],"exampleFix":"// before\nlet mut mac = SimpleHmac::<sha2::Sha256>::new_from_slice(secret.as_bytes()).expect(\"HMAC accepts a key of any length\");\n// after (if switching to Hmac)\nlet mut mac = hmac::Hmac::<sha2::Sha256>::new_from_slice(secret.as_bytes())\n    .map_err(|e| format!(\"HMAC key rejected: {e}\"))?;","handlingStrategy":"validation","validationCode":"assert!(!secret.is_empty(), \"MTS secret must be set\"); // SimpleHmac accepts any length; only allocation can fail","typeGuard":null,"tryCatchPattern":"match SimpleHmac::<sha2::Sha256>::new_from_slice(secret.as_bytes()) {\n    Ok(mut mac) => { mac.update(payload); /* ... */ },\n    Err(e) => eprintln!(\"HMAC init failed: {e}\"),\n}","preventionTips":["Keep secrets non-empty and provisioned via secret management","If switching MAC types, handle new_from_slice's Result explicitly","Add a round-trip test against core/log.py vectors for any key length"],"tags":["rust","hmac","crypto"],"backgroundTag":"hmac-key-init-failed","analyzedSha":"77cfb06d7606506d101bbcec0786c77166c4255e","analyzedAt":"2026-09-13T03:50:16.010Z","contentChangedAt":"2026-09-13T03:50:16.010Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}