{"record":{"id":"9683524a2b48a1b3","repo":"stamparm/maltrail","slug":"hmac-accepts-any-key-length","errorCode":null,"errorMessage":"hmac accepts any key length","messagePattern":"hmac accepts any key length","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"sensor/src/protocols/quic.rs","lineNumber":36,"sourceCode":"\n/// `core/quic_sni.py:MAX_INITIAL_DECRYPT`\npub const MAX_INITIAL_DECRYPT: usize = 2048;\n\n/// RFC 9001 (QUIC v1) initial salt\nconst INITIAL_SALT_V1: [u8; 20] = [\n    0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f,\n    0x0a,\n];\n/// RFC 9369 (QUIC v2) initial salt\nconst INITIAL_SALT_V2: [u8; 20] = [\n    0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e,\n    0xd9,\n];\n\ntype HmacSha256 = Hmac<Sha256>;\n\nfn hkdf_extract(salt: &[u8], ikm: &[u8]) -> [u8; 32] {\n    let mut mac = <HmacSha256 as Mac>::new_from_slice(salt).expect(\"hmac accepts any key length\");\n    mac.update(ikm);\n    mac.finalize().into_bytes().into()\n}\n\n/// Every output this schedule asks for is 32 bytes or fewer, so the buffers live on the stack.\n///\n/// The general form allocated a Vec for the output, another for the running block, and one more\n/// per HMAC round via `to_vec()` - and `hkdf_expand_label` added two more building its info\n/// string. Four labels are derived for every QUIC Initial packet, so that was a dozen small\n/// allocations on a path that runs per packet. Identical bytes out: the loop is unchanged, it\n/// just writes into fixed storage.\nconst HKDF_MAX: usize = 32;\n\nfn hkdf_expand_into(prk: &[u8], info: &[u8], length: usize, out: &mut [u8; HKDF_MAX]) {\n    debug_assert!(length <= HKDF_MAX);\n    let mut written = 0usize;\n    let mut have_prev = false;\n    let mut prev = [0u8; 32];","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/src/protocols/quic.rs#L18-L54","documentation":"`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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nlet mut mac = <HmacSha256 as Mac>::new_from_slice(salt).expect(\"hmac accepts any key length\");\n// after (robust if the MAC type may enforce key lengths)\nlet mut mac = <HmacSha256 as Mac>::new_from_slice(salt)\n    .expect(\"HMAC accepts any key length per RFC 2104\"); // or map_err and return Result<[u8;32], MacError>","handlingStrategy":"type-guard","validationCode":"// HMAC accepts any key length (RFC 2104); assert in a unit test\n#[test] fn hmac_any_salt_len() { for n in [0usize, 1, 32, 1024] { let _ = hkdf_extract(&vec![0u8; n], b\"ikm\"); } }","typeGuard":"fn salt_is_bytes(s: &[u8]) -> bool { true } // any &[u8] is a valid HMAC key","tryCatchPattern":"let mut mac = <HmacSha256 as Mac>::new_from_slice(salt)\n    .map_err(|e| CryptoError::MacInit(e))?; // only if MAC type may restrict keys","preventionTips":["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"],"tags":["rust","crypto","hmac","quic","expect"],"backgroundTag":"internal-invariant-violation","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"}