shadowsocks/shadowsocks-rust · error
EIH key length mismatch
Error message
EIH key length mismatch
What it means
While decrypting the Extensible Identity Header, the 16-byte EIH slice is converted to an aes::Block via TryFrom<&mut [u8]>; only a slice of exactly 16 bytes converts successfully, otherwise .expect("EIH key length mismatch") panics. This is an internal invariant: EIH blocks are fixed at 16 bytes by the 2022 EIH spec, so failure means the packet buffer layout is wrong.
Source
Thrown at crates/shadowsocks/src/relay/udprelay/aead_2022.rs:351
// Extensible Identity Header
// https://github.com/Shadowsocks-NET/shadowsocks-specs/blob/main/2022-2-shadowsocks-2022-extensible-identity-headers.md
let (eih, remain_message) = message.split_at_mut(16);
message = remain_message;
let session_id_packet_id = &packet_header[0..16];
trace!(
"server EIH {:?}, session_id_packet_id: {:?}",
ByteStr::new(eih),
ByteStr::new(session_id_packet_id)
);
match method {
CipherKind::AEAD2022_BLAKE3_AES_128_GCM => {
let cipher = Aes128::new_from_slice(key).expect("AES-128 init");
cipher.decrypt_block(
<&mut Block as TryFrom<&mut [u8]>>::try_from(eih).expect("EIH key length mismatch"),
);
}
CipherKind::AEAD2022_BLAKE3_AES_256_GCM => {
let cipher = Aes256::new_from_slice(key).expect("AES-256 init");
cipher.decrypt_block(
<&mut Block as TryFrom<&mut [u8]>>::try_from(eih).expect("EIH key length mismatch"),
)
}
_ => unreachable!("{} doesn't support EIH", method),
}
for i in 0..16 {
eih[i] ^= session_id_packet_id[i];
}
match user_manager.clone_user_by_hash(eih) {
None => {
error!("user with identity {:?} not found", ByteStr::new(eih));View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Before decryption, check packet.len() >= method.tag_len() + 16 + 16*eih_count so header plus every EIH block fits; drop short packets.
- Verify the client's EIH implementation emits exactly 16 bytes per identity header per the shadowsocks-2022 EIH spec.
- Only enable EIH-capable methods with clients that actually support EIH; mismatched implementations produce malformed packets.
- Patch the expect to return ProtocolError::DecryptPayloadError so malformed packets are dropped instead of crashing the relay task.
Example fix
// before
decrypt_server_payload_aead_2022(ctx, method, key, &mut buf) // buf may be truncated
// after
let eih_count = ident_psks.len();
if buf.len() < method.tag_len() + 16 + 16 * eih_count {
return; // drop malformed packet
}
decrypt_server_payload_aead_2022(ctx, method, key, &mut buf) Defensive patterns
Strategy: validation
Validate before calling
// ensure packet fits header + all EIH blocks before decrypt
let eih_count = ident_psks.len();
if packet.len() < method.tag_len() + 16 + 16 * eih_count {
return Ok(()); // drop malformed packet
} Type guard
fn fits_eih(method: CipherKind, packet: &[u8], eih_count: usize) -> bool {
packet.len() >= method.tag_len() + 16 + 16 * eih_count
} Try / catch
// drop on decrypt failure instead of panicking:
match decrypt_server_payload_aead_2022(ctx, method, key, &mut buf) {
Err(_) => { /* drop malformed EIH packet */ }
Ok(_) => { /* proceed */ }
} Prevention
- Size-check datagrams against tag + 16 + 16*EIH-count before decryption.
- Only enable EIH with clients verified to emit 16-byte identity headers.
- Drop (don't unwrap on) packets from unauthenticated sources.
- Fuzz-test the server UDP path with truncated EIH packets.
When it happens
Trigger: decrypt_client_payload_aead_2022 / decrypt_server_payload_aead_2022 on a packet where the bytes following the 16-byte header are fewer than 16 (message too short to contain the EIH), so eih = message.split_at_mut(16) cannot yield a full block — the split itself panics — or a shorter slice is fed into try_from(eih).
Common situations: Truncated or malformed client UDP packets reaching the EIH branch, a client implementing EIH incorrectly (EIH length not 16), or fuzzing/scanner traffic on an EIH-enabled server.
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
- {method} don't know how to generate nonce
- missing manager config
- EIH key length mismatch
- cipher is None
- BloomFilter1
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/035efb961d887bd7.
Report an issue: GitHub.