shadowsocks/shadowsocks-rust · error
EIH key length mismatch
Error message
EIH key length mismatch
What it means
The EIH block passed to decrypt_block_b2b must be exactly one 16-byte AES Block; the TryFrom<&[u8]> conversion is unwrapped with expect("EIH key length mismatch"). It panics when the eih slice is not exactly 16 bytes, meaning the received EIH header length did not match the expected identity-hash block size for the configured method.
Source
Thrown at crates/shadowsocks/src/relay/tcprelay/aead_2022.rs:319
match self.user_manager {
Some(ref user_manager) => {
// Assume we have at least 1 EIH
if header_chunk.len() < 16 {
error!("expecting EIH, but header chunk len: {}", header_chunk.len());
return Err(ProtocolError::MissingExtendedIdentityHeader).into();
}
let (eih, remain_header_chunk) = header_chunk.split_at_mut(16);
header_chunk = remain_header_chunk;
let key_material = [key, salt].concat();
let identity_sub_key = blake3::derive_key(AEAD2022_EIH_SUBKEY_CONTEXT, &key_material);
let mut user_hash = Block::from([0u8; 16]);
match self.method {
CipherKind::AEAD2022_BLAKE3_AES_128_GCM => {
let cipher = Aes128::new_from_slice(&identity_sub_key[0..16]).expect("AES-128");
cipher.decrypt_block_b2b(
<&Block as TryFrom<&[u8]>>::try_from(eih).expect("EIH key length mismatch"),
&mut user_hash,
);
}
CipherKind::AEAD2022_BLAKE3_AES_256_GCM => {
let cipher = Aes256::new_from_slice(&identity_sub_key[0..32]).expect("AES-256");
cipher.decrypt_block_b2b(
<&Block as TryFrom<&[u8]>>::try_from(eih).expect("EIH key length mismatch"),
&mut user_hash,
);
}
_ => unreachable!("{} doesn't support EIH", self.method),
}
let user_hash = user_hash.as_slice();
trace!(
"server EIH {:?}, hash: {:?}",
ByteStr::new(eih),
ByteStr::new(user_hash)View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Upgrade shadowsocks-rust: newer versions validate EIH length and return a ProtocolError instead of panicking
- Ensure client and server use matching versions and the same AEAD2022 method
- Validate the EIH length before decrypt_block_b2b and reject short/long headers with an io/protocol error
Example fix
// before
<&Block as TryFrom<&[u8]>>::try_from(eih).expect("EIH key length mismatch"),
// after
let block = <&Block as TryFrom<&[u8]>>::try_from(eih)
.map_err(|_| ProtocolError::EihLengthMismatch)?; Defensive patterns
Strategy: validation
Validate before calling
if eih.len() != 16 { return Err(ProtocolError::EihLengthMismatch.into()); } Type guard
fn is_valid_eih(eih: &[u8]) -> bool { eih.len() == 16 } Try / catch
let block = <&Block as TryFrom<&[u8]>>::try_from(eih)
.map_err(|_| ProtocolError::EihLengthMismatch)?; Prevention
- Keep client and server AEAD2022 versions/methods aligned
- Deploy a build where malformed EIH yields a protocol error, not a panic
- Drop truncated packets at the framing layer before EIH processing
When it happens
Trigger: Server receives an AEAD-2022 connection whose extended identity header chunk is not 16 bytes (truncated packet, attacker-supplied malformed header, or a method/EIH-count mismatch in header parsing) with AES-128 configured.
Common situations: Malformed or hostile client traffic against an AEAD-2022 server; version mismatch between client and server EIH formats; proxy chaining where an intermediate strips/adds EIH bytes.
Related errors
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/db2a79147fa531d2.
Report an issue: GitHub.