Common Implementation Failures

The catalogue of how correct algorithms get deployed insecurely — oracles, timing leaks, nonce reuse, unverified decryption, misuse-prone APIs — and the design rules that structurally prevent each class.

AES has not been broken. RSA has not been factored. Essentially every real-world cryptographic compromise of the last twenty-five years happened in the gap between a correct algorithm and its deployment. This entry is the catalogue of that gap — organized by failure class, because the individual CVEs are numerous and the underlying mistakes are few and highly repetitive.

1. Oracles: when error handling becomes a decryption service #

An oracle is any observable difference in system behaviour that leaks information about a secret. The attacker submits crafted input and reads the response — success/failure, error type, response time, a log line, a connection reset.

Padding oracles (CBC) — Vaudenay 2002. Distinguishing "bad padding" from "bad MAC" decrypts arbitrary ciphertext without the key. The leak channel kept migrating as each fix landed:

  1. 2002 Vaudenay: the oracle is an explicit distinct error message.
  2. 2010 ASP.NET (CVE-2010-3332): the oracle is an HTTP status code difference — full server compromise on many sites.
  3. 2013 Lucky 13: the oracle is a few microseconds of MAC-check timing. The "constant-time" remediation is notoriously difficult and was gotten wrong repeatedly.
  4. 2014 POODLE: SSL 3.0 padding bytes are not covered by the MAC at all.
  5. 2016 DROWN: an SSLv2 server sharing a key acts as an oracle against modern TLS sessions.

RSA PKCS#1 v1.5 oracles (Bleichenbacher) — the same shape on the asymmetric side, and equally immortal: the original in 1998, ROBOT in 2017 (found in F5, Citrix, Cisco and Facebook front-ends), and Marvin in 2023, a timing variant by Kario that bypassed two decades of accumulated mitigations and hit GnuTLS, NSS, pyca/cryptography and M2Crypto.

2. Timing and side channels #

Any operation whose duration, memory access pattern, or power draw depends on secret data leaks that data.

Channel Mechanism Example
Branch timing secret-dependent control flow early-exit MAC comparison
Cache secret-dependent memory indices AES T-table attacks — full key from 800 writes / 65 ms
Data-dependent arithmetic variable-time bignum, non-constant modular reduction RSA/ECDSA key extraction
Power / EM instruction-correlated consumption DPA on smartcards
Fault induced miscomputation CRT fault reveals pp from one bad signature
python
if computed_mac == provided_mac:        # WRONG — short-circuits on first mismatch
if hmac.compare_digest(computed, provided):   # RIGHT

The rules that actually hold: no branches on secret data, no memory indices derived from secret data, no early exit. Use vetted constant-time primitives (hmac.compare_digest, crypto.timingSafeEqual, CRYPTO_memcmp, subtle), prefer hardware instructions (AES-NI) or inherently constant-time designs (ChaCha20's ARX, Curve25519) over table-driven software.

3. Nonce, IV and randomness failures #

The invariant most often broken, with the most severe consequences:

Failure Consequence
GCM nonce reuse keystream reuse and permanent recovery of the authentication key HH — forge anything, forever
CTR / stream nonce reuse two-time pad; C1⊕C2=P1⊕P2C_1 \oplus C_2 = P_1 \oplus P_2
ECDSA/DSA nonce reuse private key recovered from two signatures
Biased signature nonce private key via lattice reduction from ~100 signatures
Predictable CBC IV BEAST-style chosen-boundary plaintext recovery
Unseeded RNG at boot factorable keys at scale — 64,000 TLS hosts in 2012

Real instances: the PS3's constant ECDSA nonce (2010, master signing key extracted); Android's SecureRandom flaw repeating nonces in Bitcoin wallets (2013, wallets drained by scanning the public chain for equal r); 184 HTTPS servers found repeating GCM nonces in a 2016 internet-wide scan.

4. Verification that does not verify #

Certificate validation. Georgiev et al. (2012) found broken TLS validation across payment SDKs, cloud client libraries, and merchant software — the underlying TLS was fine; the applications disabled or botched verification. The recurring forms: verify=False / InsecureSkipVerify added to silence a development error and never removed; a custom TrustManager that returns unconditionally; validating the chain but never checking the hostname against the SAN. See PKI.

Signature verification bugs. CVE-2022-21449 — Java 15–18 accepted r = s = 0 as a valid ECDSA signature for any message, because a range check was missing. JWT `alg:none` and RS256→HS256 confusion — the token selects its own verification algorithm.

Releasing unverified plaintext. Decrypting and acting on data before the authentication tag is checked reinstates exactly the oracle AEAD removes. Open is atomic for a reason; for large objects, chunk into individually-authenticated framed segments rather than streaming out unverified bytes.

5. Protocol-level composition errors #

  • Downgrade — FREAK and Logjam forced export-grade parameters; the fix is signing the entire transcript (TLS 1.3) and removing weak options altogether.
  • Cross-protocol / key reuse — DROWN used an SSLv2 endpoint to attack TLS sharing the same RSA key. One key, one purpose; separate keys per protocol and direction via KDF context binding.
  • Replay — encryption alone gives no freshness. Sequence numbers, timestamps or challenge nonces are required; TLS 1.3 0-RTT is explicitly replayable and must carry only idempotent requests.
  • Length-extension MACs — H(secret ‖ message) as an API signature, still routinely reinvented for webhooks. Historical high-water mark: SSL 2.0 standardized it, computing each record's MAC as Hash(Secret, Data, Padding, SequenceNumber) — secret-prefix, over a bare Merkle–Damgård hash. SSL 3.0 moved to a nested construction resembling HMAC; TLS 1.0 adopted real HMAC. The trap was in a shipped internet protocol, and took two revisions to remove.

6. Misuse-prone APIs are the root cause #

Egele et al. (2013) analyzed 11,748 Android apps using cryptography and found 88% contained at least one misuse — ECB by default, hardcoded keys, constant IVs, static salts. That number indicts the interface, not the developers.

The checklist #

DoDon't
Use a high-level library (libsodium, Tink, age)Compose a cipher and a MAC yourself
AEAD for everythingECB, unauthenticated CBC/CTR
Counter-derived or misuse-resistant noncesRandom 96-bit nonces at scale, or reused counters
Constant-time comparison and primitives== on secrets; table-driven AES in software
One generic error, uniform in content and timingDistinct errors for padding vs MAC vs parse
Platform CSPRNG; deterministic signature noncesLanguage default RNG; homegrown entropy
Full certificate + hostname validationverify=False anywhere it can reach production
Verify tag before touching plaintextStreaming out unverified decrypted data
Distinct keys per purpose via KDF labelsOne key across protocols, directions, versions

Sources

  1. paperSecurity Flaws Induced by CBC Padding (Vaudenay, EUROCRYPT 2002)
  2. paperLucky Thirteen: Breaking the TLS and DTLS Record Protocols (AlFardan & Paterson, 2013)
  3. paperAn Empirical Study of Cryptographic Misuse in Android Applications (Egele et al., CCS 2013)
  4. paperThe Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software (Georgiev et al., CCS 2012)
  5. paperEverlasting ROBOT: the Marvin Attack (Kario, 2023)

Searches titles, aliases, headings, summaries, and full text.↑↓ move ↵ open esc close