stamparm/maltrail · info
hmac accepts any key length
Error message
hmac accepts any key length
What it means
`hkdf_extract` initializes an HMAC-SHA256 instance from the salt via `Mac::new_from_slice(salt).expect("hmac accepts any key length")`. HMAC (and the `hmac`/`digest` crates' `Mac` trait) accepts keys of any length, so `new_from_slice` is infallible in practice and returns `Result` only for API symmetry; the `expect` documents that invariant. A panic here would indicate the HMAC implementation changed or the salt/ikm arguments were swapped into an invalid position.
Solutions
- Confirm the `hmac` crate version still guarantees arbitrary key lengths (it does for HMAC per RFC 2104); pin/adjust Cargo.toml if an upgrade altered behavior.
- If the primitive was swapped, replace `expect` with proper error handling: match on `new_from_slice` and propagate `Err` to the QUIC key-derivation caller.
- Keep the `expect` but add a comment/test asserting arbitrary-length salts (0-byte and very long keys) derive without panic.
- Run the QUIC initial-key derivation test vectors (RFC 9001) to verify the derive path is intact.
Example fix
// before
let mut mac = <HmacSha256 as Mac>::new_from_slice(salt).expect("hmac accepts any key length");
// after (robust if the MAC type may enforce key lengths)
let mut mac = <HmacSha256 as Mac>::new_from_slice(salt)
.expect("HMAC accepts any key length per RFC 2104"); // or map_err and return Result<[u8;32], MacError> Defensive patterns
Strategy: type-guard
Validate before calling
// HMAC accepts any key length (RFC 2104); assert in a unit test
#[test] fn hmac_any_salt_len() { for n in [0usize, 1, 32, 1024] { let _ = hkdf_extract(&vec![0u8; n], b"ikm"); } } Type guard
fn salt_is_bytes(s: &[u8]) -> bool { true } // any &[u8] is a valid HMAC key Try / catch
let mut mac = <HmacSha256 as Mac>::new_from_slice(salt)
.map_err(|e| CryptoError::MacInit(e))?; // only if MAC type may restrict keys Prevention
- Pin hmac/digest versions and read changelogs on upgrade
- Keep RFC 9001 derivation test vectors in CI
- Comment expect()s that encode cryptographic invariants
- Re-verify assumptions if the MAC primitive type changes
When it happens
Trigger: Panic occurs only if `Hmac::<Sha256>::new_from_slice` returns `Err`, which the current `hmac` crate never does for any byte slice. Practically triggered by upgrading `hmac`/`digest` to a version with different trait semantics, or by a refactor that replaces `HmacSha256` with a MAC that does enforce key-length limits (e.g. a fixed-key cipher-based MAC).
Common situations: Dependency upgrades changing the `Mac` trait; swapping the primitive in `type HmacSha256` for something with a key-length constraint; misuse where a caller passes a typed non-byte argument that fails conversion.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/9683524a2b48a1b3.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/protocols/quic.rs:36
/// `core/quic_sni.py:MAX_INITIAL_DECRYPT`
pub const MAX_INITIAL_DECRYPT: usize = 2048;
/// RFC 9001 (QUIC v1) initial salt
const INITIAL_SALT_V1: [u8; 20] = [
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f,
0x0a,
];
/// RFC 9369 (QUIC v2) initial salt
const INITIAL_SALT_V2: [u8; 20] = [
0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e,
0xd9,
];
type HmacSha256 = Hmac<Sha256>;
fn hkdf_extract(salt: &[u8], ikm: &[u8]) -> [u8; 32] {
let mut mac = <HmacSha256 as Mac>::new_from_slice(salt).expect("hmac accepts any key length");
mac.update(ikm);
mac.finalize().into_bytes().into()
}
/// Every output this schedule asks for is 32 bytes or fewer, so the buffers live on the stack.
///
/// The general form allocated a Vec for the output, another for the running block, and one more
/// per HMAC round via `to_vec()` - and `hkdf_expand_label` added two more building its info
/// string. Four labels are derived for every QUIC Initial packet, so that was a dozen small
/// allocations on a path that runs per packet. Identical bytes out: the loop is unchanged, it
/// just writes into fixed storage.
const HKDF_MAX: usize = 32;
fn hkdf_expand_into(prk: &[u8], info: &[u8], length: usize, out: &mut [u8; HKDF_MAX]) {
debug_assert!(length <= HKDF_MAX);
let mut written = 0usize;
let mut have_prev = false;
let mut prev = [0u8; 32];View on GitHub (pinned to 77cfb06d76)