# Unauthenticated encryption and padding oracles

Ciphertext accepted without MAC, distinguishable padding/timing errors, or nonce reuse leaking key validity

_Area: Web infrastructure and server-side requests. Seen in 3 disclosed reports; latest 2026-10-05. Updated 2026-11-02._

## How it works
Encryption is treated as if it also proved integrity. Two assumptions break. First, a decryptor that reports *why* decryption failed (bad RSA padding vs. wrong key length vs. bad CBC padding) turns the error channel into an oracle: an attacker learns whether a guessed key or padding was partly right, and can recover plaintext or the content-encryption key one bit at a time. Second, a cipher that reuses a nonce or derives its key from a predictable seed produces identical keystreams or a fully computable key, so confidentiality and integrity both collapse without any error channel at all [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g6cj-pr64-35w5.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g8-5h73-26h4.md)[E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md).

## Where it shows up
- PKCS#7/CMS/EnvelopedData decrypt paths with separate error strings or code paths for RSA unpad, key-length check, and CBC unpad [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g6cj-pr64-35w5.md)
- A key-size error that echoes the recovered length N [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g6cj-pr64-35w5.md)
- Async crypto APIs where a nonce or sequence counter is read, then incremented across an `await` [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g8-5h73-26h4.md)
- Key derivation that calls `random.seed`, `Random(`, or `getrandbits` on a secret [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md)
- Short secrets stretched into keys; long secrets used raw as the key [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md)

## How to review
For every decryptor, enumerate each distinct error message and code path between RSA unpad, key-length validation, and CBC unpad; the invariant is that all failures are indistinguishable in message and timing (RFC 3218 implicit rejection). For stateful AEAD, the invariant is that nonce computation and increment are atomic — no `await` between reading and advancing the counter. For key derivation, the invariant is that the key is a cryptographic function of the secret, never a PRNG seeded by it.

## How finders spotted it
- Read `_ensure_valid_key` in the auth service, noticed the `random.seed(raw_key)` branch for short secrets and the raw-key branch for long ones, then confirmed by storing a sentinel credential via the API and recovering it from raw DB ciphertext [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md)
- Read the decrypt path stage by stage (RSA PKCS#1 v1.5 → AES build → CBC unpad), noticing each stage surfaced a different error and that the key-length error disclosed N, plus a timing side channel [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g6cj-pr64-35w5.md)
- Noticed `seal()` is async while the context manages nonces via a shared sequence counter, hypothesized a race between nonce computation and increment, and confirmed with `Promise.all` producing identical nonces [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g8-5h73-26h4.md)

## Variants seen
- Deterministic key from a non-crypto PRNG seeded with the secret [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md)
- Bleichenbacher-style padding oracle via distinguishable errors and timing [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g6cj-pr64-35w5.md)
- AEAD nonce reuse from an unsynchronized async counter [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g8-5h73-26h4.md)

## Impact and severity
E1 let anyone with the secret_key file and DB decrypt all stored credentials, escalating to instance-wide exfiltration across all users when chained with a path traversal [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md). E2 exposed the content-encryption key, undermining all enveloped messages [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g6cj-pr64-35w5.md). E3 broke confidentiality and integrity of HPKE messages [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g8-5h73-26h4.md). Severity rises with reach (multi-tenant, all users) and with chaining to a file-read primitive [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md).

## Seen in real software
Open-source packages and projects, and products, where this pattern was found. If the target runs one, compare the version it runs with the fix.
- **langflow (pip)** <= 1.10.0, fixed in 1.10.1: Langflow: Weak Fernet Key via random.seed() (CVE-2026-9205). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md. Fix: https://github.com/langflow-ai/langflow/commit/094694d3f20c1da499f4d8dbac15c510609e3026
- **cryptography (pip)** >= 44.0.0, < 50.0.0, fixed in 50.0.0: cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle through distinguishable errors and timing (CVE-2026-69247). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-g6cj-pr64-35w5.md. Fix: https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f
- **@hpke/core (npm)** <= 1.7.4, fixed in 1.7.5: @hpke/core reuses AEAD nonces (CVE-2025-64767). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-73g8-5h73-26h4.md. Fix: https://github.com/dajiaji/hpke-js/commit/94a767c9b9f37ce48d5cd86f7017d8cacd294aaf

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-jxw3-mjmx-3pqm.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-g6cj-pr64-35w5.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-73g8-5h73-26h4.md
