openssl/openssl

openssl 3.5.6 AES-256-CBC bad decrypt

Open

#31.246 geöffnet am 20. Mai 2026

Auf GitHub ansehen
 (2 Kommentare) (0 Reaktionen) (0 zugewiesene Personen)C (11.262 Forks)batch import
branch: 3.5branch: 3.6branch: 4.0branch: masterhelp wantedtriaged: bug

Repository-Metriken

Stars
 (30.157 Stars)
PR-Merge-Metriken
 (Keine gemergten PRs in 30 T)

Beschreibung

Bug Report: AES-256-CBC decryption fails on Zhaoxin KX-7000 CPU with OpenSSL 3.5.x

Environment

Item Details
OS Debian 13 (Trixie), kernel 6.12.88+deb13-amd64
CPU (broken) Zhaoxin KaiXian KX-7000
OpenSSL (broken) 3.5.6
OpenSSL (working, same CPU) 3.2.4 (UOS V25)
Node.js v22.22.3

Description

AES-256-CBC decryption consistently fails with bad decrypt on Zhaoxin KX-7000 CPU when using OpenSSL 3.5.6. The exact same test passes on:

  • Zhaoxin KX-7000 + OpenSSL 3.2.4
  • Intel Celeron J1900 + OpenSSL 3.5.6

This confirms the bug is a regression specific to Zhaoxin KX-7000's AES-NI implementation triggered by changes introduced between OpenSSL 3.2 and 3.5.

AES-256-GCM works correctly on all tested combinations.

Reproduction

const crypto = require('crypto');

const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);

// Encryption succeeds
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update('Hello Pylance!', 'utf8', 'hex');
encrypted += cipher.final('hex');
console.log('Encrypt OK:', encrypted);

// Decryption FAILS on Zhaoxin KX-7000 + OpenSSL 3.5.6
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
try {
  decrypted += decipher.final('utf8');
  console.log('Decrypt OK:', decrypted);
} catch (e) {
  console.log('Decrypt FAILED:', e.code, e.message);
  // Error: error:03000064:digital envelope routines::bad decrypt
  //   at Decipheriv.final (node:internal/crypto/cipher:184:29)
}

Test Results Matrix

CPU OpenSSL AES-256-CBC Encrypt AES-256-CBC Decrypt AES-256-GCM
Zhaoxin KX-7000 3.2.4 ✅ OK OK ✅ OK
Zhaoxin KX-7000 3.5.6 ✅ OK FAIL ✅ OK
Intel Celeron J1900 3.5.6 ✅ OK OK ✅ OK

Key finding: Intel J1900 and Zhaoxin KX-7000 run the same Node.js v22.22.3 + OpenSSL 3.5.6 binary, but only Zhaoxin KX-7000 fails. This rules out any software version difference and points directly to Zhaoxin's AES-NI hardware behavior with the optimized CBC decryption path introduced in OpenSSL 3.3–3.5.

Error Output (Zhaoxin KX-7000 + OpenSSL 3.5.6)

=== 系统信息 ===
Platform: linux x64
CPU: ZHAOXIN KaiXian KX-7000
Node: v22.22.3
OpenSSL: 3.5.6

=== AES-256-CBC ===
Encrypt OK: f0a4ce97fd67763499379c0289e7b919...
FAILED: error:03000064:digital envelope routines::bad decrypt

=== AES-256-GCM ===
Encrypt OK, tag length: 16
Decrypt OK: Pylance cache data with authentication

=== PBKDF2 ===
PBKDF2(10000): 3ms   ← works
PBKDF2(100000): 26ms ← works

Reference Output (Intel Celeron J1900 + OpenSSL 3.5.6)

=== 系统信息 ===
Platform: linux x64
CPU: Intel(R) Celeron(R) CPU J1900 @ 1.99GHz
Node: v22.22.3
OpenSSL: 3.5.6

=== AES-256-CBC ===
Encrypt OK: c674f04f87806bde855875ec2522bd04...
Decrypt OK: Hello Pylance! 这是一个测试   ← passes on Intel

=== AES-256-GCM ===
Encrypt OK, tag length: 16
Decrypt OK: Pylance cache data with authentication

Additional Notes

  • openssl speed -evp aes-256-cbc benchmark runs without errors on Zhaoxin KX-7000 + OpenSSL 3.5.6, suggesting basic AES-NI instruction execution is functional
  • Only the EVP Decipheriv.final() path fails; encryption always succeeds
  • Failure is 100% reproducible — every single AES-256-CBC decryption attempt fails
  • AES-256-GCM on the same hardware + OpenSSL version works perfectly
  • Setting OPENSSL_ia32cap to disable hardware AES caused the affected application (VS Code Pylance) to silently fail entirely, suggesting the flag approach is not a viable workaround

CPU Info

model name      : ZHAOXIN KaiXian KX-7000
OPENSSL_ia32cap : 0x7ffa73e3afcbffff:0x0040001d201c25ab:0x0000000024000000:0x0000000000000000:0x0000000000000000

Hypothesis

The regression was introduced between OpenSSL 3.2 and 3.5, most likely related to:

  • VAES (Vector AES) support added in OpenSSL 3.3
  • Optimized CBC decryption scheduling changes in 3.4–3.5

Zhaoxin KX-7000 implements AES-NI but appears to have subtle behavioral differences from Intel/AMD in specific instruction sequences used by the newer CBC decryption path. The fact that GCM works and CBC encrypt works suggests the issue is isolated to the specific VAES/AES-NI instruction sequence used in the CBC decryption path.

Suggested Investigation

  1. Check if CBC decryption uses a different AES-NI instruction sequence than CBC encryption (e.g., AESDEC vs AESENC pipelining)
  2. Check VAES (VAESDEC/VAESDECLAST) usage in the CBC path — Zhaoxin KX-7000's VAES support may have errata
  3. Consider adding a CBC-decrypt self-test to OPENSSL_cpuid_setup() similar to how some implementations detect RDRAND bugs
  4. A targeted workaround: disable VAES for CBC decrypt specifically when running on Zhaoxin CPUs (detectable via CPUID vendor string CentaurHauls / Shanghai )

Workaround

Use python:3.13-bookworm (Debian 12, OpenSSL 3.1.x) as the runtime environment. OpenSSL 3.1.x is not affected.

Contributor Guide