{"record":{"id":"de489f276665c87a","repo":"shadowsocks/shadowsocks-rust","slug":"ipsk-length-mismatch","errorCode":null,"errorMessage":"ipsk length mismatch","messagePattern":"ipsk length mismatch","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/shadowsocks/src/relay/tcprelay/aead_2022.rs","lineNumber":570,"sourceCode":"    ) -> Self {\n        // nonce should be sent with the first packet\n        let mut buffer = BytesMut::with_capacity(nonce.len() + identity_keys.len() * 16);\n        buffer.put(nonce);\n\n        // Extensible Identity Headers\n        // https://github.com/Shadowsocks-NET/shadowsocks-specs/blob/main/2022-2-shadowsocks-2022-extensible-identity-headers.md\n        #[inline]\n        fn make_eih(method: CipherKind, sub_key: &[u8], ipsk: &[u8], buffer: &mut BytesMut) {\n            let ipsk_hash = blake3::hash(ipsk);\n            let ipsk_plain_text = &ipsk_hash.as_bytes()[0..16];\n\n            match method {\n                CipherKind::AEAD2022_BLAKE3_AES_128_GCM => {\n                    let enc_key = &sub_key[0..16];\n                    let cipher = Aes128::new_from_slice(enc_key).expect(\"AES-128\");\n\n                    let ipsk_plain_text =\n                        <&Block as TryFrom<&[u8]>>::try_from(ipsk_plain_text).expect(\"ipsk length mismatch\");\n                    let mut block = Block::from([0u8; 16]);\n                    cipher.encrypt_block_b2b(ipsk_plain_text, &mut block);\n\n                    trace!(\n                        \"client EIH {:?}, hash: {:?}\",\n                        ByteStr::new(block.as_slice()),\n                        ByteStr::new(ipsk_plain_text)\n                    );\n                    buffer.put(block.as_slice());\n                }\n                CipherKind::AEAD2022_BLAKE3_AES_256_GCM => {\n                    let enc_key = &sub_key[0..32];\n                    let cipher = Aes256::new_from_slice(enc_key).expect(\"AES-256\");\n\n                    let ipsk_plain_text =\n                        <&Block as TryFrom<&[u8]>>::try_from(ipsk_plain_text).expect(\"ipsk length mismatch\");\n                    let mut block = Block::from([0u8; 16]);\n                    cipher.encrypt_block_b2b(ipsk_plain_text, &mut block);","sourceCodeStart":552,"sourceCodeEnd":588,"githubUrl":"https://github.com/shadowsocks/shadowsocks-rust/blob/8eb0f0a65b1d976ab6bed5787327ef86529b0435/crates/shadowsocks/src/relay/tcprelay/aead_2022.rs#L552-L588","documentation":"Panic from `<&Block as TryFrom<&[u8]>>::try_from(ipsk_plain_text).expect(\"ipsk length mismatch\")` in `make_eih`. The input is `blake3::hash(ipsk).as_bytes()[0..16]`, so it only fails if the sliced hash is not 16 bytes — which happens when the slice bounds are wrong or, practically, when the code path is fed an unexpected buffer; the slice here is hard-coded to 16 bytes, so a panic indicates a corrupted/short ipsk source or a modified slice length.","triggerScenarios":"Calling `make_eih` (via `with_identity`) where the blake3 hash slice used as `ipsk_plain_text` is not exactly 16 bytes — practically only when the slice expression was changed, or `ipsk` handling produced a non-standard buffer in a patched/forked build.","commonSituations":"Custom forks or vendored patches altering the `[0..16]` slice; misuse of a lower-level helper expecting a 16-byte block but handed the raw ipsk; upstream/downstream version mismatch in the EIH construction API.","solutions":["Use the standard library code path: hash the ipsk with blake3 and slice `[0..16]` exactly as upstream does.","Verify the ipsk bytes passed to `with_identity` are the full pre-shared key (raw 16/32 bytes), not a truncated or base64-string slice.","Remove local patches that change the slice bounds of `ipsk_plain_text`.","If writing your own block conversion, check `ipsk_plain_text.len() == 16` before `try_from` and return an error instead of panicking."],"exampleFix":"// before\nlet ipsk_plain_text = <&Block as TryFrom<&[u8]>>::try_from(ipsk_plain_text).expect(\"ipsk length mismatch\");\n// after\nlet ipsk_plain_text = <&Block as TryFrom<&[u8]>>::try_from(ipsk_plain_text)\n    .expect(\"ipsk plain text must be 16 bytes (blake3 hash slice)\");","handlingStrategy":"validation","validationCode":"let hash = blake3::hash(ipsk).as_bytes()[0..16].to_vec();\nif hash.len() != 16 { return Err(\"ipsk plain text must be 16 bytes\".into()); }","typeGuard":"fn is_block_sized(b: &[u8]) -> bool { b.len() == 16 }","tryCatchPattern":null,"preventionTips":["Use upstream `make_eih` unchanged; do not alter the [0..16] slice.","Pass full raw ipsk bytes into `with_identity`, never pre-truncated buffers.","Avoid vendoring/forking the crate internals."],"tags":["rust","panic","block-length","ipsk","aead-2022"],"backgroundTag":"shape-mismatch","analyzedSha":"8eb0f0a65b1d976ab6bed5787327ef86529b0435","analyzedAt":"2026-09-09T12:20:43.168Z","contentChangedAt":"2026-09-09T12:20:43.168Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}