/reviewing-cryptography
Review cryptographic implementations and protocol usage for misuse — weak primitives, nonce and IV handling, key management, authentication of ciphertext, randomness, timing side channels, TLS and JWT configuration, and password storage. Use when auditing code that encrypts,
$ npx -y skills add trilwu/secskills --skill reviewing-cryptography --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/reviewing-cryptography
Context preview
The summary Claude sees to decide when to auto-load this skill.
Review cryptographic implementations and protocol usage for misuse — weak primitives, nonce and IV handling, key management, authentication of ciphertext, randomness, timing side channels, TLS and JWT configuration, and password storage. Use when auditing code that encrypts,
SKILL.md
reviewing-cryptography.SKILL.mdname: reviewing-cryptography
description: Review cryptographic implementations and protocol usage for misuse — weak primitives, nonce and IV handling, key management, authentication of ciphertext, randomness, timing side channels, TLS and JWT configuration, and password storage. Use when auditing code that encrypts, signs, hashes, or authenticates, or when assessing TLS and token configurations.
verified: 2026-07-27
Reviewing Cryptography
Almost no real system is broken by cryptanalysis. They are broken by misuse: a reused nonce, unauthenticated ciphertext, a comparison that returns early, a key checked into git. Review for misuse, and leave primitive design to cryptographers.
When to Use
- Auditing code that encrypts, decrypts, signs, verifies, or hashes
- Reviewing key management, rotation, and storage
- Assessing TLS/mTLS configuration and certificate validation
- Reviewing JWT, session token, and API signature schemes
- Checking password and secret storage
- Evaluating randomness quality for security-relevant values
When NOT to Use
- **Designing a new primitive or protocol** — that needs a cryptographer and
formal review, not a code audit
- **Breaking cryptography in a CTF** — different discipline; use
`solving`-oriented offensive skills and known-attack tooling
- **General code review** — use `auditing-code-for-vulnerabilities`
- **Password cracking** — use `cracking-passwords`
The Misuse Checklist
Work through these in order. Each has caught real production breaks.
1. Is the ciphertext authenticated?
Encryption without authentication is the single most common serious finding. CBC or CTR without a MAC means an attacker can modify plaintext — and with a decryption oracle, recover it (padding oracle).
Good: AES-GCM, ChaCha20-Poly1305, AES-CBC + HMAC (encrypt-then-MAC)
Bad: AES-CBC alone, AES-ECB (ever), CTR without a MAC, MAC-then-encrypt
rg -n 'AES/ECB|AES\.MODE_ECB|CipherMode\.ECB|"AES"\)' -i
rg -n 'AES/CBC/PKCS5Padding|MODE_CBC|createCipheriv\(.*cbc' -i
If you see CBC, find the MAC. If there is no MAC, that is a finding regardless of how the ciphertext is transported.
2. Nonce and IV handling
| Mode | Rule | Failure | | --- | --- | --- | | GCM / ChaCha20-Poly1305 | Never reuse a (key, nonce) pair | Catastrophic: reveals the auth key, forgery becomes trivial | | CBC | IV must be unpredictable and random per message | Chosen-plaintext attacks (BEAST-class) | | CTR | Never reuse a counter with the same key | Keystream reuse; XOR of plaintexts |
# The classic bug: a fixed or zero IV
rg -n 'iv\s*=\s*(b?["\x27]0|new byte\[\d+\]|bytes\(\d+\)|\[0\]\s*\*)' -i
rg -n 'IvParameterSpec\(new byte\[16\]\)|createCipheriv\([^,]+,[^,]+,\s*["\x27]'
Random 96-bit nonces for GCM are safe up to roughly 2^32 messages per key. A counter-based nonce is safer, but only if the counter state genuinely survives restarts and is not duplicated across instances. Ask where the counter is persisted; "in memory" plus horizontal scaling means reuse.
3. Key management
- Where does the key come from? Hardcoded, env var, KMS/HSM, derived?
- Hardcoded keys, keys in source, keys in config committed to the repo, keys
in container images, keys in CI logs — check all of these.
- Is a key used for exactly one purpose? Key reuse across encryption and
signing, or across tenants, is a finding.
- Is there a rotation path at all? A system that cannot rotate has no response
to compromise.
- Derived keys: is a proper KDF used (HKDF for key material, Argon2id/scrypt/
PBKDF2 for passwords)? A raw SHA-256 of a password is not a KDF.
rg -n 'BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY|-----BEGIN'
rg -n '(secret|api[_-]?key|password|token)\s*[:=]\s*["\x27][A-Za-z0-9/+=]{16,}' -i
gitleaks detect --source . --redact # history matters more than the tree4. Password storage
Correct: Argon2id (preferred), scrypt, bcrypt, PBKDF2-HMAC-SHA256 with a
high iteration count — each with a per-user random salt
Wrong: MD5, SHA-1, SHA-256, SHA-512 (raw or salted), any unsalted hash,
encryption instead of hashing, a "pepper" as the only defenseCheck the work factor against current guidance, not the value that was adequate when the code was written. And check that the verification path uses the library's constant-time verify function rather than comparing strings.
5. Randomness
Security-relevant values — tokens, session IDs, nonces, salts, password reset codes, IVs — must come from a CSPRNG.
rg -n 'math/rand|Math\.random\(\)|random\.random\(|rand\(\)|mt_rand|Random\(\)'
# Correct: crypto/rand, secrets.token_bytes, window.crypto.getRandomValues,
# SecureRandom, os.urandom, RandomNumberGenerator
Also check: seeding with a timestamp or PID, UUIDv1/v4-from-a-weak-source used as a secret, and predictable sequential IDs used where unguessability is assumed.
6. Timing side channels
Any comparison of a secret must be constant time: MACs, tokens, API signatures, password hashes, OTPs.
rg -n 'hmac.*==|token\s*==|signature\s*==|\.equals\(.*(hmac|token|sig)' -i
# Correct: hmac.compare_digest, crypto.timingSafeEqual, subtle.ConstantTimeCompare,
# MessageDigest.isEqual, hash_equals
Early-return string comparison of an HMAC is a practical remote attack, not a theoretical one.
7. Signature verification
- Is the signature actually verified, or merely parsed?
- Is the algorithm taken from the message? (JWT `alg` confusion: `none`, and
RS256→HS256 where the public key becomes the HMAC key.)
- Is the key selected by an identifier the attacker controls (`kid`, `jku`,
`x5u`)? Those fields are attacker input; treat them as such.
- Are claims validated after signature verification: `exp`, `nbf`, `iss`,
`aud`, and — critically — the subject's current authorization?
rg -n 'jwt\.decode\(|verify\s*[:=]\s*(False|false)|algorithms\s*=\s*\[?["\x27]?none' -i
Read more
name: reviewing-cryptography description: Review cryptographic implementations and protocol usage for misuse — weak primitives, nonce and IV handling, key management, authentication of ciphertext, randomness, timing side channels, TLS and JWT configuration, and password storage. Use when auditing code that encrypts, signs, hashes, or authenticates, or when assessing TLS and token configurations. verified: 2026-07-27
Reviewing Cryptography
Almost no real system is broken by cryptanalysis. They are broken by misuse: a reused nonce, unauthenticated ciphertext, a comparison that returns early, a key checked into git. Review for misuse, and leave primitive design to cryptographers.
When to Use
- Auditing code that encrypts, decrypts, signs, verifies, or hashes
- Reviewing key management, rotation, and storage
- Assessing TLS/mTLS configuration and certificate validation
- Reviewing JWT, session token, and API signature schemes
- Checking password and secret storage
- Evaluating randomness quality for security-relevant values
When NOT to Use
- **Designing a new primitive or protocol** — that needs a cryptographer and
formal review, not a code audit
- **Breaking cryptography in a CTF** — different discipline; use
`solving`-oriented offensive skills and known-attack tooling
- **General code review** — use `auditing-code-for-vulnerabilities`
- **Password cracking** — use `cracking-passwords`
The Misuse Checklist
Work through these in order. Each has caught real production breaks.
1. Is the ciphertext authenticated?
Encryption without authentication is the single most common serious finding. CBC or CTR without a MAC means an attacker can modify plaintext — and with a decryption oracle, recover it (padding oracle).
Good: AES-GCM, ChaCha20-Poly1305, AES-CBC + HMAC (encrypt-then-MAC) Bad: AES-CBC alone, AES-ECB (ever), CTR without a MAC, MAC-then-encrypt
rg -n 'AES/ECB|AES\.MODE_ECB|CipherMode\.ECB|"AES"\)' -i rg -n 'AES/CBC/PKCS5Padding|MODE_CBC|createCipheriv\(.*cbc' -i
If you see CBC, find the MAC. If there is no MAC, that is a finding regardless of how the ciphertext is transported.
2. Nonce and IV handling
| Mode | Rule | Failure | | --- | --- | --- | | GCM / ChaCha20-Poly1305 | Never reuse a (key, nonce) pair | Catastrophic: reveals the auth key, forgery becomes trivial | | CBC | IV must be unpredictable and random per message | Chosen-plaintext attacks (BEAST-class) | | CTR | Never reuse a counter with the same key | Keystream reuse; XOR of plaintexts |
# The classic bug: a fixed or zero IV rg -n 'iv\s*=\s*(b?["\x27]0|new byte\[\d+\]|bytes\(\d+\)|\[0\]\s*\*)' -i rg -n 'IvParameterSpec\(new byte\[16\]\)|createCipheriv\([^,]+,[^,]+,\s*["\x27]'
Random 96-bit nonces for GCM are safe up to roughly 2^32 messages per key. A counter-based nonce is safer, but only if the counter state genuinely survives restarts and is not duplicated across instances. Ask where the counter is persisted; "in memory" plus horizontal scaling means reuse.
3. Key management
- Where does the key come from? Hardcoded, env var, KMS/HSM, derived?
- Hardcoded keys, keys in source, keys in config committed to the repo, keys
in container images, keys in CI logs — check all of these.
- Is a key used for exactly one purpose? Key reuse across encryption and
signing, or across tenants, is a finding.
- Is there a rotation path at all? A system that cannot rotate has no response
to compromise.
- Derived keys: is a proper KDF used (HKDF for key material, Argon2id/scrypt/
PBKDF2 for passwords)? A raw SHA-256 of a password is not a KDF.
rg -n 'BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY|-----BEGIN'
rg -n '(secret|api[_-]?key|password|token)\s*[:=]\s*["\x27][A-Za-z0-9/+=]{16,}' -i
gitleaks detect --source . --redact # history matters more than the tree4. Password storage
Correct: Argon2id (preferred), scrypt, bcrypt, PBKDF2-HMAC-SHA256 with a
high iteration count — each with a per-user random salt
Wrong: MD5, SHA-1, SHA-256, SHA-512 (raw or salted), any unsalted hash,
encryption instead of hashing, a "pepper" as the only defenseCheck the work factor against current guidance, not the value that was adequate when the code was written. And check that the verification path uses the library's constant-time verify function rather than comparing strings.
5. Randomness
Security-relevant values — tokens, session IDs, nonces, salts, password reset codes, IVs — must come from a CSPRNG.
rg -n 'math/rand|Math\.random\(\)|random\.random\(|rand\(\)|mt_rand|Random\(\)' # Correct: crypto/rand, secrets.token_bytes, window.crypto.getRandomValues, # SecureRandom, os.urandom, RandomNumberGenerator
Also check: seeding with a timestamp or PID, UUIDv1/v4-from-a-weak-source used as a secret, and predictable sequential IDs used where unguessability is assumed.
6. Timing side channels
Any comparison of a secret must be constant time: MACs, tokens, API signatures, password hashes, OTPs.
rg -n 'hmac.*==|token\s*==|signature\s*==|\.equals\(.*(hmac|token|sig)' -i # Correct: hmac.compare_digest, crypto.timingSafeEqual, subtle.ConstantTimeCompare, # MessageDigest.isEqual, hash_equals
Early-return string comparison of an HMAC is a practical remote attack, not a theoretical one.
7. Signature verification
- Is the signature actually verified, or merely parsed?
- Is the algorithm taken from the message? (JWT `alg` confusion: `none`, and
RS256→HS256 where the public key becomes the HMAC key.)
- Is the key selected by an identifier the attacker controls (`kid`, `jku`,
`x5u`)? Those fields are attacker input; treat them as such.
- Are claims validated after signature verification: `exp`, `nbf`, `iss`,
`aud`, and — critically — the subject's current authorization?
rg -n 'jwt\.decode\(|verify\s*[:=]\s*(False|false)|algorithms\s*=\s*\[?["\x27]?none' -i
**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

