shadowsocks/shadowsocks-rust · error
AES-128 init
Error message
AES-128 init
What it means
Panic from `Aes128::new_from_slice(ipsk).expect("AES-128 init")` in the UDP AEAD-2022 `encrypt_message`. The `[SessionID + PacketID]` header is ECB-encrypted with the identity PSK; `Aes128::new_from_slice` requires exactly 16 bytes, so the panic means the ipsk supplied is not 16 bytes long for `AEAD2022_BLAKE3_AES_128_GCM`.
Source
Thrown at crates/shadowsocks/src/relay/udprelay/aead_2022.rs:227
// AES-*-GCM uses derived key, and part of the packet header as nonce
let cipher = get_cipher(method, key, session_id);
// Encrypt the rest of the packet with AEAD cipher (AES-*-GCM)
let (packet_header, mut message) = packet.split_at_mut(16);
let nonce = &packet_header[4..16];
if eih_len > 0 {
message = &mut message[eih_len..];
}
cipher.encrypt_packet(nonce, message);
// [SessionID + PacketID] is encrypted with AES-ECB with PSK
// No padding is required because these 2 fields are 128-bits, which is exactly the same as AES's block size
match method {
CipherKind::AEAD2022_BLAKE3_AES_128_GCM => {
let cipher = Aes128::new_from_slice(ipsk).expect("AES-128 init");
let block = <&mut Block as TryFrom<&mut [u8]>>::try_from(packet_header)
.expect("Packet header length mismatch");
cipher.encrypt_block(block);
}
CipherKind::AEAD2022_BLAKE3_AES_256_GCM => {
let cipher = Aes256::new_from_slice(ipsk).expect("AES-256 init");
let block = <&mut Block as TryFrom<&mut [u8]>>::try_from(packet_header)
.expect("Packet header length mismatch");
cipher.encrypt_block(block);
}
_ => unreachable!("{} is not an AES-*-GCM cipher", method),
}
}
_ => unreachable!("{} is not an AEAD 2022 cipher", method),
}
}
fn decrypt_message(View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Provide an identity PSK of exactly 16 raw bytes when the method is `AEAD2022_BLAKE3_AES_128_GCM` (base64 of 16 bytes = 24 chars).
- Match the cipher kind to the key: use the 256-bit branch (`AES-256 init` path) with 32-byte keys instead of forcing a 128-bit method.
- Decode base64 keys and assert length before calling the payload encryption API.
- Align identity-key length expectations between client and server configs.
Example fix
// before
let cipher = Aes128::new_from_slice(ipsk).expect("AES-128 init");
// after
assert_eq!(ipsk.len(), 16, "AEAD2022_AES_128_GCM identity PSK must be 16 bytes, got {}", ipsk.len());
let cipher = Aes128::new_from_slice(ipsk).expect("AES-128 init"); Defensive patterns
Strategy: validation
Validate before calling
// before UDP payload encryption
if matches!(method, CipherKind::AEAD2022_BLAKE3_AES_128_GCM) && ipsk.len() != 16 {
return Err(format!("identity PSK must be 16 bytes for AES-128-GCM, got {}", ipsk.len()));
} Type guard
fn is_valid_ipsk(kind: CipherKind, ipsk: &[u8]) -> bool {
match kind {
CipherKind::AEAD2022_BLAKE3_AES_128_GCM => ipsk.len() == 16,
CipherKind::AEAD2022_BLAKE3_AES_256_GCM => ipsk.len() == 32,
_ => false,
}
} Prevention
- Validate PSK length against the cipher kind at startup.
- Use raw decoded keys, not passwords, for the identity PSK.
- Keep identity-key length consistent across client and server.
When it happens
Trigger: Calling `encrypt_message` (directly or via `encrypt_client_payload_aead_2022` / `encrypt_server_payload_aead_2022`) with `method = AEAD2022_BLAKE3_AES_128_GCM` and an `ipsk` that is not exactly 16 bytes — e.g. a 32-byte identity key or a raw password.
Common situations: Using the main PSK (32 bytes for AES-256) as the identity PSK while the method is 128-bit; misreading the spec and passing a password instead of the raw 16-byte additional PSK; configs where client and server identity-key lengths differ.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/3ddfef153d6b24d6.
Report an issue: GitHub.