Crypto Misuse Reasoning
The name of the primitive is the least interesting fact about a cryptographic system.
"We use AES-256-GCM" tells you nothing about whether the nonce repeats, whether the
ciphertext is authenticated before it is trusted, whether the key came from a password
through a real KDF or a single SHA-256, or whether an attacker can force a downgrade to
a weaker path. The algorithm is almost never the break. The misuse is the break —
and misuse lives in the composition, the parameters, and the surrounding code, not in
the cipher.
Two symmetric failure modes, and this skill exists to catch both:
- Primitive-name assurance. "It's AES, it's fine." A green checkmark on the cipher
choice while the mode is ECB, the IV is a constant, or the MAC is missing entirely.
The strongest cipher in the world, used wrong, is a plaintext leak with extra steps.
- Roll-your-own, including the "small" glue. Not just inventing a cipher — that is
obviously reckless — but the quieter version: a hand-built token format, a custom
"encrypt then base64" with no integrity, a password comparison with
==. The danger
is in the parts people do not think of as "crypto."
The through-line: crypto fails at the seams — where two correct primitives are
composed incorrectly, where a parameter that must be unique is not, where ciphertext is
trusted before it is verified. Reason about the seams.
Composes with the library:
- secure-by-construction — a hostile user picks the nonce, forces the downgrade, replays the token; design for that user
- validate-at-the-boundary — a decrypted or verified blob is still untrusted input until parsed safely; decryption is not validation
- deterministic-core — determinism is a virtue in a sealed decision path and a catastrophe in encryption (deterministic ciphertext leaks equality); keep the two worlds straight
- red-team-auditing — "this is secure" is a CONFIRMED claim; a break needs a demonstrated one, and absence of an obvious break is not proof of security
- secret-lifecycle-discipline — a key is a secret with a lifetime; the strongest scheme dies with a leaked or never-rotated key
Step 1 — Name the guarantee the scheme is supposed to provide
Before judging anything, state what property this crypto must deliver, because misuse
is a gap against that property, not against a vibe:
- Confidentiality — the plaintext stays secret. (Encryption alone.)
- Integrity / authenticity — the ciphertext or message cannot be altered or forged.
(A MAC or signature — encryption does not provide this, and assuming it does is the
single most common misuse.)
- Non-repudiation, freshness, forward secrecy — distinct properties, each requiring
distinct machinery (signatures, nonces/timestamps, ephemeral keys).
A scheme that provides confidentiality but not integrity, deployed where integrity was
the actual requirement, is broken even though every primitive in it is sound. Most
"encryption bugs" are really "we needed authenticated encryption and shipped raw
encryption."
Step 2 — Hunt the uniqueness invariants: nonces, IVs, and randomness
A large fraction of real breaks are a value that had to be unique or unpredictable and
was not. This is the highest-yield place to look:
- Nonce / IV reuse — reusing a nonce under the same key in a stream cipher or GCM is
catastrophic (it can leak plaintext XOR and, for GCM, the authentication key). A
counter that resets, a hardcoded IV, an IV derived from static data — all reuse.
- ECB mode — no IV at all; identical plaintext blocks produce identical ciphertext
blocks. Equal-looking data leaks structure (the classic visible-penguin image). Treat
ECB in any confidentiality context as a finding.
- Predictable "randomness" — an IV, key, or token from a non-cryptographic RNG, a
timestamp, or a weak seed is not random. Distinguish a CSPRNG from
rand().
- Deterministic encryption used unknowingly — if identical plaintext must produce
identical ciphertext (for search/dedup), that is a deliberate confidentiality
tradeoff (it leaks equality), not a default to stumble into.
Step 3 — Check that ciphertext is authenticated before it is trusted
Order of operations at the boundary decides whether a padding-oracle-class attack
exists:
- Encrypt-then-MAC / AEAD — verify the tag before decrypting or acting. If the tag
fails, the ciphertext never becomes plaintext you process. This is the safe shape.
- MAC-then-encrypt / encrypt-and-MAC / no MAC — decrypting first and revealing (even
through timing or error differences) whether padding was valid hands the attacker a
decryption oracle. The bug is the order and the leak, not the cipher.
- Unauthenticated ciphertext — if anyone can flip bits in transit and you cannot
tell, you have confidentiality without integrity (see Step 1). A decrypted blob that
was never authenticated is attacker-controlled input; hand it to
validate-at-the-boundary, do not trust it because it "decrypted successfully."
Step 4 — Follow the key: derivation, storage, comparison, lifetime
The scheme is only as strong as the key handling around it:
- Password-derived keys — a password must pass through a real, tunable KDF
(argon2/scrypt/bcrypt/PBKDF2 with sane cost), not a single fast hash. A SHA-256 of a
password is not a key-derivation function.
- Comparison — comparing MACs, tokens, or password hashes with a non-constant-time
== leaks via timing. Use constant-time comparison for any secret-dependent equality.
- Storage and lifetime — where the key lives, who can read it, whether it is ever
rotated (
secret-lifecycle-discipline). A signing key compromise is retroactive: it
forges everything, past and future.
- Reuse across purposes — one key for encryption and MAC, or across contexts,
violates domain separation and can enable cross-protocol attacks. Separate keys per
purpose.
Step 5 — Assume the attacker chooses the weakest allowed path
Security is set by the worst path the system permits, not the best one it advertises:
- Downgrade / negotiation — if the protocol will accept a weaker cipher, an
unauthenticated mode,
alg: none in a JWT, or an old TLS version, the attacker
negotiates exactly that. Enumerate what is accepted, not what is preferred.
- Trusting attacker-supplied parameters — a token that names its own algorithm, a
message that carries its own IV/salt with no binding, lets the attacker pick
favorable parameters. Bind or fix the security-critical ones server-side.
- Error and side channels — distinct error messages, timing differences, and
compression (CRIME/BREACH-class) leak the secret without breaking the math.
Step 6 — Grade honestly; ABSTAIN beats a false "secure"
Cryptographic judgments carry outsized weight, so calibrate like the rest of the
library:
- State what you checked and what you did not. "AEAD with unique nonces confirmed;
key management not reviewed" is honest. A blanket "the crypto is fine" is the overclaim
red-team-auditing forbids.
- A demonstrated misuse is a finding; a suspected one is a hypothesis. Do not assert
a break you have not shown, and do not assert security you have not verified.
- ABSTAIN when the scheme is non-standard. Custom constructions warrant "cannot
certify; recommend a vetted standard scheme (a named AEAD, a maintained TLS/JOSE
library)" — a documented "I cannot vouch for this homemade protocol" is worth more
than a confident guess. Do not produce attack recipes; produce the judgment and the
safe alternative.
The one-line test
If your assurance rests on the primitive's name — "it's AES, it's RSA, it's fine" — you
have not reviewed the cryptography; you have read the label. The security lives in the
nonce, the mode, the MAC-then-decrypt order, the KDF, and the weakest path the protocol
will accept — check those, or state that you did not.
1---2name: crypto-misuse-reasoning3description: Crypto Misuse Reasoning4---56# Crypto Misuse Reasoning78The name of the primitive is the least interesting fact about a cryptographic system.9"We use AES-256-GCM" tells you nothing about whether the nonce repeats, whether the10ciphertext is authenticated before it is trusted, whether the key came from a password11through a real KDF or a single SHA-256, or whether an attacker can force a downgrade to12a weaker path. The algorithm is almost never the break. **The misuse is the break** —13and misuse lives in the composition, the parameters, and the surrounding code, not in14the cipher.1516Two symmetric failure modes, and this skill exists to catch both:1718- **Primitive-name assurance.** "It's AES, it's fine." A green checkmark on the cipher19 choice while the mode is ECB, the IV is a constant, or the MAC is missing entirely.20 The strongest cipher in the world, used wrong, is a plaintext leak with extra steps.21- **Roll-your-own, including the "small" glue.** Not just inventing a cipher — that is22 obviously reckless — but the quieter version: a hand-built token format, a custom23 "encrypt then base64" with no integrity, a password comparison with `==`. The danger24 is in the parts people do not think of as "crypto."2526The through-line: crypto fails at the seams — where two correct primitives are27composed incorrectly, where a parameter that must be unique is not, where ciphertext is28trusted before it is verified. Reason about the seams.2930Composes with the library:3132- **secure-by-construction** — a hostile user picks the nonce, forces the downgrade, replays the token; design for that user33- **validate-at-the-boundary** — a decrypted or verified blob is still untrusted input until parsed safely; decryption is not validation34- **deterministic-core** — determinism is a virtue in a sealed decision path and a catastrophe in encryption (deterministic ciphertext leaks equality); keep the two worlds straight35- **red-team-auditing** — "this is secure" is a CONFIRMED claim; a break needs a demonstrated one, and absence of an obvious break is not proof of security36- **secret-lifecycle-discipline** — a key is a secret with a lifetime; the strongest scheme dies with a leaked or never-rotated key3738---3940## Step 1 — Name the guarantee the scheme is supposed to provide4142Before judging anything, state what property this crypto must deliver, because misuse43is a gap against that property, not against a vibe:4445- **Confidentiality** — the plaintext stays secret. (Encryption alone.)46- **Integrity / authenticity** — the ciphertext or message cannot be altered or forged.47 (A MAC or signature — *encryption does not provide this*, and assuming it does is the48 single most common misuse.)49- **Non-repudiation, freshness, forward secrecy** — distinct properties, each requiring50 distinct machinery (signatures, nonces/timestamps, ephemeral keys).5152A scheme that provides confidentiality but not integrity, deployed where integrity was53the actual requirement, is broken even though every primitive in it is sound. Most54"encryption bugs" are really "we needed authenticated encryption and shipped raw55encryption."5657---5859## Step 2 — Hunt the uniqueness invariants: nonces, IVs, and randomness6061A large fraction of real breaks are a value that had to be unique or unpredictable and62was not. This is the highest-yield place to look:6364- **Nonce / IV reuse** — reusing a nonce under the same key in a stream cipher or GCM is65 catastrophic (it can leak plaintext XOR and, for GCM, the authentication key). A66 counter that resets, a hardcoded IV, an IV derived from static data — all reuse.67- **ECB mode** — no IV at all; identical plaintext blocks produce identical ciphertext68 blocks. Equal-looking data leaks structure (the classic visible-penguin image). Treat69 ECB in any confidentiality context as a finding.70- **Predictable "randomness"** — an IV, key, or token from a non-cryptographic RNG, a71 timestamp, or a weak seed is not random. Distinguish a CSPRNG from `rand()`.72- **Deterministic encryption used unknowingly** — if identical plaintext must produce73 identical ciphertext (for search/dedup), that is a deliberate confidentiality74 tradeoff (it leaks equality), not a default to stumble into.7576---7778## Step 3 — Check that ciphertext is authenticated before it is trusted7980Order of operations at the boundary decides whether a padding-oracle-class attack81exists:8283- **Encrypt-then-MAC / AEAD** — verify the tag *before* decrypting or acting. If the tag84 fails, the ciphertext never becomes plaintext you process. This is the safe shape.85- **MAC-then-encrypt / encrypt-and-MAC / no MAC** — decrypting first and revealing (even86 through timing or error differences) whether padding was valid hands the attacker a87 decryption oracle. The bug is the *order and the leak*, not the cipher.88- **Unauthenticated ciphertext** — if anyone can flip bits in transit and you cannot89 tell, you have confidentiality without integrity (see Step 1). A decrypted blob that90 was never authenticated is attacker-controlled input; hand it to91 `validate-at-the-boundary`, do not trust it because it "decrypted successfully."9293---9495## Step 4 — Follow the key: derivation, storage, comparison, lifetime9697The scheme is only as strong as the key handling around it:9899- **Password-derived keys** — a password must pass through a real, tunable KDF100 (argon2/scrypt/bcrypt/PBKDF2 with sane cost), *not* a single fast hash. A SHA-256 of a101 password is not a key-derivation function.102- **Comparison** — comparing MACs, tokens, or password hashes with a non-constant-time103 `==` leaks via timing. Use constant-time comparison for any secret-dependent equality.104- **Storage and lifetime** — where the key lives, who can read it, whether it is ever105 rotated (`secret-lifecycle-discipline`). A signing key compromise is retroactive: it106 forges everything, past and future.107- **Reuse across purposes** — one key for encryption and MAC, or across contexts,108 violates domain separation and can enable cross-protocol attacks. Separate keys per109 purpose.110111---112113## Step 5 — Assume the attacker chooses the weakest allowed path114115Security is set by the worst path the system permits, not the best one it advertises:116117- **Downgrade / negotiation** — if the protocol will accept a weaker cipher, an118 unauthenticated mode, `alg: none` in a JWT, or an old TLS version, the attacker119 negotiates exactly that. Enumerate what is *accepted*, not what is *preferred*.120- **Trusting attacker-supplied parameters** — a token that names its own algorithm, a121 message that carries its own IV/salt with no binding, lets the attacker pick122 favorable parameters. Bind or fix the security-critical ones server-side.123- **Error and side channels** — distinct error messages, timing differences, and124 compression (CRIME/BREACH-class) leak the secret without breaking the math.125126---127128## Step 6 — Grade honestly; ABSTAIN beats a false "secure"129130Cryptographic judgments carry outsized weight, so calibrate like the rest of the131library:132133- **State what you checked and what you did not.** "AEAD with unique nonces confirmed;134 key management not reviewed" is honest. A blanket "the crypto is fine" is the overclaim135 `red-team-auditing` forbids.136- **A demonstrated misuse is a finding; a suspected one is a hypothesis.** Do not assert137 a break you have not shown, and do not assert security you have not verified.138- **ABSTAIN when the scheme is non-standard.** Custom constructions warrant "cannot139 certify; recommend a vetted standard scheme (a named AEAD, a maintained TLS/JOSE140 library)" — a documented "I cannot vouch for this homemade protocol" is worth more141 than a confident guess. Do not produce attack recipes; produce the judgment and the142 safe alternative.143144---145146## The one-line test147148If your assurance rests on the primitive's name — "it's AES, it's RSA, it's fine" — you149have not reviewed the cryptography; you have read the label. The security lives in the150nonce, the mode, the MAC-then-decrypt order, the KDF, and the weakest path the protocol151will accept — check those, or state that you did not.