shadowsocks/shadowsocks-windows · critical · CryptoErrorException
openssl: fail to finalize AEAD
Error message
openssl: fail to finalize AEAD
What it means
Thrown as CryptoErrorException when EVP_CipherFinal_ex returns non-1 during AEAD encryption. CipherFinal_ex flushes the cipher pipeline and, for GCM, finalises tag material. Because AEAD ciphers stream all ciphertext through CipherUpdate with padding disabled, Final succeeding is the expected steady state; failure after a successful Update signals an internally inconsistent or corrupted cipher context.
Source
Thrown at shadowsocks-csharp/Encryption/AEAD/AEADOpenSSLEncryptor.cs:101
}
public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
{
OpenSSL.SetCtxNonce(_encryptCtx, _encNonce, true);
// buf: all plaintext
// outbuf: ciphertext + tag
int ret;
int tmpLen = 0;
clen = 0;
var tagBuf = new byte[tagLen];
ret = OpenSSL.EVP_CipherUpdate(_encryptCtx, ciphertext, out tmpLen,
plaintext, (int) plen);
if (ret != 1) throw new CryptoErrorException("openssl: fail to encrypt AEAD");
clen += (uint) tmpLen;
// For AEAD cipher, it should not output anything
ret = OpenSSL.EVP_CipherFinal_ex(_encryptCtx, ciphertext, ref tmpLen);
if (ret != 1) throw new CryptoErrorException("openssl: fail to finalize AEAD");
if (tmpLen > 0)
{
throw new System.Exception("openssl: fail to finish AEAD");
}
OpenSSL.AEADGetTag(_encryptCtx, tagBuf, tagLen);
Array.Copy(tagBuf, 0, ciphertext, clen, tagLen);
clen += (uint) tagLen;
}
public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen)
{
OpenSSL.SetCtxNonce(_decryptCtx, _decNonce, false);
// buf: ciphertext + tag
// outbuf: plaintext
int ret;
int tmpLen = 0;
plen = 0;
View on GitHub (pinned to 891d971682)
Solutions
- Confirm the method string on both peers is one of the supported AEAD ciphers (aes-128-gcm, aes-192-gcm, aes-256-gcm, chacha20-ietf-poly1305) and matches exactly.
- Re-derive the session subkey from a fresh salt and re-run InitCipher on a new encryptor instance.
- Audit InitCipher to confirm every EVP_CipherInit_ex / EVP_CIPHER_CTX_ctrl / set_key_length return code is checked, not swallowed.
- Verify native libcrypto integrity (reinstall or pin the OpenSSL build).
Example fix
// before
enc.cipherEncrypt(plain, (uint)plain.Length, cipher, ref clen);
// after — recreate the context once on a finalize failure
try {
enc.cipherEncrypt(plain, (uint)plain.Length, cipher, ref clen);
} catch (CryptoErrorException ex) when (ex.Message.Contains("finalize")) {
enc.Dispose();
enc = new AEADOpenSSLEncryptor(method, password);
enc.InitCipher(salt, true, isUdp);
enc.cipherEncrypt(plain, (uint)plain.Length, cipher, ref clen);
} Defensive patterns
Strategy: try-catch
Validate before calling
// nothing to validate at the call site; verify the init steps succeeded upstream
if (_cipherInfoPtr == IntPtr.Zero || _encryptCtx == IntPtr.Zero)
throw new InvalidOperationException("AEAD context not initialised"); Try / catch
try {
enc.cipherEncrypt(plain, (uint)plain.Length, cipher, ref clen);
} catch (CryptoErrorException ex) when (ex.Message.Contains("finalize")) {
// re-key with a fresh salt once on a new encryptor
enc.Dispose();
enc = new AEADOpenSSLEncryptor(method, password);
enc.InitCipher(salt, true, isUdp);
enc.cipherEncrypt(plain, (uint)plain.Length, cipher, ref clen);
} Prevention
- Match method strings between peers exactly.
- Derive the session subkey from a fresh salt each session.
- Verify every EVP_*Ctrl return code in InitCipher is checked.
- Pin the OpenSSL native version at build time.
When it happens
Trigger: The EVP context is corrupted by a memory overwrite or use-after-free between Update and Final. The padding/IV-length ctrl calls in InitCipher were skipped, reordered, or returned non-1. The cipher object behind _cipherInfoPtr is not actually an AEAD type despite the _ciphers entry. A FIPS/self-test abort or native libcrypto fault on the running context.
Common situations: Method strings differ between client and server (one side aes-256-gcm against a peer built on a mismatched lib). Corrupted key-derivation output from a malformed salt. Partial upgrade of the OpenSSL DLL leaving libcrypto internally inconsistent. Heap corruption from another thread stomping the context.
Related errors
- openssl: fail to encrypt AEAD
- openssl: fail to finish AEAD
- openssl: cipher not found
- openssl: fail to create ctx
- openssl: fail to init ctx
AI-assisted analysis of shadowsocks/shadowsocks-windows@891d971682 (2026-08-13).
Data as JSON: /api/errors/cae99c79b75003a6.
Report an issue: GitHub.