$npx -y skills add briiirussell/cybersecurity-skills --skill crypto-auditAudit cryptography implementation — algorithm choice, key sizes, KDF parameters, IV/nonce handling, signature verification, randomness, TLS configuration, and key rotation. Deeper than owasp-audit A02. Use when the user mentions 'crypto review,' 'cryptography audit,' 'encryption
| 1 | # Crypto Audit — Cryptography Implementation Review |
| 2 | |
| 3 | Audit how cryptography is implemented in an application — algorithm choices, parameters, modes, and the implementation patterns that turn good primitives into broken systems. Deeper than `owasp-audit` A02 (which catches the obvious "MD5 password" and "VERIFY_NONE" cases). This skill is for the subtler implementation review. |
| 4 | |
| 5 | Most crypto failures are not "they used MD5." Most failures are: right primitive, wrong mode (ECB instead of GCM), right algorithm, wrong parameter (PBKDF2 with 1,000 iterations in 2026), right library, wrong call order (init the cipher after the data was loaded). |
| 6 | |
| 7 | Cross-references: `owasp-audit` A02 (baseline) + A07 (timing-safe comparison), `secrets-audit` (key storage), `iam-audit` (KMS / HSM patterns). |
| 8 | |
| 9 | ## Don't roll your own |
| 10 | |
| 11 | The default audit verdict for any custom encryption scheme is "use libsodium / Tink / WebCrypto instead." There are < 50 people on Earth who can design new crypto safely, and they don't work at your company. Unless an explicit threat model says otherwise, custom crypto is a finding. |
| 12 | |
| 13 | ## Audit Checklist |
| 14 | |
| 15 | ### Algorithm and mode |
| 16 | |
| 17 | - **Symmetric:** AES-256-GCM or ChaCha20-Poly1305 (authenticated encryption — confidentiality + integrity in one primitive) |
| 18 | - **Reject:** AES-ECB (block-pattern leak — identical plaintext → identical ciphertext), AES-CBC without HMAC (unauthenticated; padding oracle attacks), AES-CTR without HMAC (malleable; bit-flip = plaintext-flip), DES / 3DES, RC4, Blowfish (use Twofish or skip altogether) |
| 19 | - **Asymmetric:** Ed25519 / X25519 for signatures and key exchange; RSA-OAEP / RSA-PSS at 3072+ bits if compatibility forces RSA; never RSA with PKCS#1 v1.5 padding for encryption (Bleichenbacher); never raw RSA |
| 20 | - **Hashing (general purpose):** SHA-256, SHA-3, BLAKE2 / BLAKE3 |
| 21 | - **Hashing (passwords) — categorically different problem:** Argon2id, scrypt, bcrypt (with cost ≥ 12 for bcrypt; OWASP 2024 floor) |
| 22 | - **MAC:** HMAC-SHA256 minimum; never CBC-MAC; never homemade `hash(key + message)` |
| 23 | - Grep for: `MD5`, `SHA1` (outside of HMAC-SHA1 in legacy compat), `DES`, `RC4`, `Blowfish`, `AES.*ECB`, `pkcs1_v1_5` (Python), `RSA.encrypt` without OAEP |
| 24 | |
| 25 | ### Key derivation |
| 26 | |
| 27 | - **From a password:** Argon2id (memory-hard) or PBKDF2-HMAC-SHA256 with ≥ 600,000 iterations (OWASP 2024) or scrypt with N=2^17, r=8, p=1 |
| 28 | - **From a high-entropy secret:** HKDF-SHA256 — the right primitive when you have key material and need to derive sub-keys |
| 29 | - **From a low-entropy secret to encryption key:** PBKDF2 / Argon2 (treat it as a password) |
| 30 | - Grep for: `PBKDF2` (check iteration count), `HKDF`, `Argon2`, `scrypt`, `pbkdf2_hmac` (Python; check `iterations` arg) |
| 31 | |
| 32 | ### IV / nonce handling |
| 33 | |
| 34 | Wrong IV / nonce handling is one of the top three sources of "the crypto looks right but actually leaks plaintext." |
| 35 | |
| 36 | - **AES-GCM:** unique nonce per encryption under the same key. **NEVER reuse.** If you reuse a GCM nonce with the same key, you give the attacker the XOR of two plaintexts and the ability to forge messages. Use 96-bit random nonces (RFC 5116). For high-volume systems, switch to AES-GCM-SIV (nonce-misuse-resistant) |
| 37 | - **AES-CBC:** IV must be unpredictable AND unique. Random 16-byte IV per encryption |
| 38 | - **AES-CTR:** counter must never repeat for a (key, counter) pair within the lifetime of the key |
| 39 | - **ChaCha20-Poly1305:** unique nonce per message; XChaCha20-Poly1305 has 192-bit nonce so random nonces are safe at scale |
| 40 | - Grep for: hardcoded IVs (`iv = "0000000000000000"`, `iv = bytes(16)`), zero-IV constructors, counter resets |
| 41 | - Specifically grep for: `Cipher.getInstance("AES")` (Java default is ECB), `AES.new(key)` (PyCryptodome default is ECB), `crypto.createCipher` (Node, deprecated, derives IV from key — DON'T) |
| 42 | |
| 43 | ### Authenticated encryption |
| 44 | |
| 45 | - Always use authenticated modes — AES-GCM, ChaCha20-Poly1305, or Encrypt-then-MAC (HMAC-SHA256 over ciphertext) |
| 46 | - Never decrypt → check MAC; always check MAC → then decrypt (otherwise: padding oracle) |
| 47 | - If using Encrypt-then-MAC, use **separate keys** for encryption and authentication (or HKDF-derive both from one master key) |
| 48 | - Verify the MAC with a constant-time compare (`crypto.timingSafeEqual` in Node, `hmac.compare_digest` in Python, `subtle.ConstantTimeCompare` in Go) — see `owasp-audit` A07 |
| 49 | |
| 50 | ### Signature verification |
| 51 | |
| 52 | - The most common signature-verification bug isn't a broken algorith |