As our security policy documents, PanicExceptions are not vulnerabilities: https://cryptography.io/en/latest/security/#what-is-a-security-issue
That said this was also already fixed: https://github.com/pyca/cryptography/pull/14428 Finally, this is a public mailing list so this is an incorrect way to disclose an actual security vulnerability. Alex On Mon, Mar 9, 2026 at 10:37 PM Amartya Jha <[email protected]> wrote: > To the Python Cryptographic Authority / cryptography maintainers, > > I am a security researcher at CodeAnt AI. I have discovered a > denial-of-service vulnerability in the cryptography package's PBKDF2HMAC > key derivation function. When iterations=0 is passed, the Rust backend > raises a pyo3_runtime.PanicException, a direct subclass of BaseException, > not Exception — which silently escapes all standard Python exception > handlers and can crash the application process. > > Package: cryptography > Affected Versions: All modern versions (36.x through 46.x) using the > PyO3/Rust backend > Tested Version: 46.0.5 > > CVSS: 5.3 MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L) > CWE: CWE-703 (Improper Check or Handling of Exceptional Conditions), > CWE-20 (Improper Input Validation) > > --- > > What is the Issue? > > PBKDF2HMAC.__init__() in cryptography/hazmat/primitives/kdf/pbkdf2.py > stores the iterations parameter directly without any bounds validation: > > def __init__(self, algorithm, length, salt, iterations, backend=None): > ... > self._iterations = iterations # stored directly, no bounds check > > When derive() is called with iterations=0, this value is passed to the > Rust layer in src/rust/src/backend/kdf.rs:30, which calls .unwrap() on the > OpenSSL error rather than propagating it via map_err. This causes a Rust > panic: > > thread '<unnamed>' panicked at src/rust/src/backend/kdf.rs:30:87: > called `Result::unwrap()` on an `Err` value: ErrorStack([Error { code: > 478150779, > library: "Provider routines", > function: "kdf_pbkdf2_set_ctx_params", > reason: "invalid iteration count", > file: "providers/implementations/kdfs/pbkdf2.c", line: 305 }]) > > PyO3 surfaces Rust panics as pyo3_runtime.PanicException. This class > inherits from BaseException, NOT Exception: > > pyo3_runtime.PanicException → BaseException → object > > --- > > What is the Impact? > > 1. Process crash via DoS: Any application that accepts a user-controlled > iterations value and passes it to PBKDF2HMAC (e.g., via JSON API payload, > config file, URL parameter, or database-stored value) can be crashed by a > single request with iterations=0. > > 2. Silent bypass of all error handling: The universally expected Python > idiom "except Exception:" does NOT catch BaseException subclasses. Every > standard web framework error handler, request handler, and try/except block > in the application stack silently fails to catch this crash: > > try: > kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, > salt=b"salt", iterations=0) > result = kdf.derive(b"password") > except Exception as e: > print("Caught:", e) # <-- NEVER REACHED > > 3. Contrast with iterations=-1: Passing -1 correctly raises OverflowError > (a subclass of Exception) and IS catchable. Only iterations=0 hits the Rust > panic path. > > --- > > Proof of Concept: > > from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC > from cryptography.hazmat.primitives import hashes > > kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b"salt", > iterations=0) > print("__init__ passed: no error raised here") > > try: > result = kdf.derive(b"password") > except Exception as e: > print("Caught (Exception):", e) # NEVER REACHED > > Realistic attack vector (API endpoint accepting PBKDF2 parameters): > > @app.post("/derive-key") > async def derive_key(params: KDFParams): > try: > kdf = PBKDF2HMAC( > algorithm=hashes.SHA256(), > length=params.key_length, > salt=params.salt.encode(), > iterations=params.iterations # user-controlled! > ) > return {"key": kdf.derive(params.password.encode()).hex()} > except Exception as e: > raise HTTPException(status_code=400, detail=str(e)) > # PanicException bypasses the except Exception block above > > --- > > Recommended Fix: > > Option 1 — Python-level validation (fastest fix): > > def __init__(self, algorithm, length, salt, iterations, backend=None): > ... > if not isinstance(iterations, int) or iterations < 1: > raise ValueError( > "iterations must be a positive integer, got > {!r}".format(iterations) > ) > self._iterations = iterations > > Option 2 — Rust-level fix (proper long-term fix): > Replace .unwrap() in src/rust/src/backend/kdf.rs:30 with proper error > propagation using map_err so that OpenSSL errors surface as catchable > Python exceptions via PyO3's error conversion. > > Both fixes should be applied: the Python-level check as a fast guard with > a clear error message, and the Rust-level fix to make the entire error path > robust. > > --- > > I am happy to provide a complete test harness, coordinate a patch review, > or adjust the disclosure timeline. > > --- > > Researcher: CodeAnt AI Security Research Team, part of > https://www.codeant.ai/ > Contact: [email protected] > _______________________________________________ > Cryptography-dev mailing list -- [email protected] > To unsubscribe send an email to [email protected] > https://mail.python.org/mailman3//lists/cryptography-dev.python.org > Member address: [email protected] > -- All that is necessary for evil to succeed is for good people to do nothing.
_______________________________________________ Cryptography-dev mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/cryptography-dev.python.org Member address: [email protected]
