Never Roll Your Own
The "do not roll your own crypto" rule, explained: why even experts use vetted libraries, the failure modes, and what counts as "rolling".
Crypto — do not roll your own
EXAMPLE
# ===== The rule =====
# Do not implement crypto primitives yourself. Use vetted, audited, widely-deployed libraries.
# This applies to: ciphers, hashes, MACs, signatures, key derivation, RNGs, protocol layers.
# ===== Why =====
# 1. Crypto bugs are SILENT — the code 'works' but is broken
# 2. Side channels: timing, cache, power — bypass perfect math
# 3. Subtle math errors (modular arithmetic, padding, nonce reuse) destroy security
# 4. Years of attacker scrutiny found bugs in major libraries; your code has none
# 5. Maintenance: as attacks evolve, vetted libs get patched; yours does not
# ===== What counts as 'rolling your own' =====
# - Writing AES / SHA / RSA / Ed25519 yourself
# - Building a custom 'encryption protocol' from primitives (key exchange, framing, ...)
# - Padding implementations (PKCS#1 v1.5 has classic bugs)
# - Custom random number generators
# - Re-implementing TLS / SSH / Noise
# - 'Encrypting' by XORing with a key (Cipher 101 - this is broken)
# ===== What is fine =====
# - Calling vetted libraries (OpenSSL, libsodium, BoringSSL, ring, libtomcrypt)
# - Combining primitives in WELL-KNOWN patterns (AEAD encryption, sign-then-encrypt, etc)
# - Implementing application-level protocols ABOVE TLS
# ===== A concrete safe pattern =====
# Symmetric encryption with libsodium (or node:crypto):
import { createCipheriv, randomBytes } from 'node:crypto';
const key = randomBytes(32); // from a vetted CSPRNG
const nonce = randomBytes(12); // unique per message
const cipher = createCipheriv('aes-256-gcm', key, nonce);
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const tag = cipher.getAuthTag();
// Store: nonce || ct || tag
# This is fine: you used vetted primitives via a vetted library.
# ===== Common 'rolled' failure modes =====
# 1. ECB mode (encrypts every block independently) -> patterns visible
# 2. CBC without HMAC -> padding-oracle attacks
# 3. Reused nonces with stream ciphers -> XOR ciphertexts to plaintext
# 4. RSA without padding -> textbook RSA is broken
# 5. Comparing MACs with == -> timing side-channel
# 6. Math.random for secrets -> predictable
# 7. Custom KDF (e.g. password = key) -> dictionary attack
# ===== What if no vetted library exists? =====
# - Reconsider whether you need the obscure primitive
# - Hire an actual cryptographer; pay for an audit
# - Publish for community review BEFORE production
# - Even then, expect bugs
# ===== Libraries that ARE vetted =====
# JS / Node: node:crypto, libsodium-wrappers, @noble/curves, @noble/hashes
# Python: cryptography, PyNaCl
# Go: crypto/* (std), x/crypto/...
# Rust: ring, rustcrypto/* crates
# Java: JCE (Bouncy Castle, Tink)
# C/C++: OpenSSL, libsodium, BoringSSL, mbedTLS
# ===== AEAD over manual chaining =====
# Modern primitives: AES-GCM, ChaCha20-Poly1305 give confidentiality + integrity in one call.
# Do not stack CBC + HMAC manually unless you know exactly what you are doing.
# ===== Hashing passwords =====
# Don't roll: 'I'll just SHA-256 the password.' That is broken.
# Use Argon2id, scrypt, bcrypt — designed to be SLOW and use memory.
# ===== Patterns to internalise =====
# - Vetted libraries, every time
# - AEAD primitives, not 'encrypt + MAC' by hand
# - Vetted password hash families for passwords
# - Constant-time comparisons (timingSafeEqual, hmac.compare_digest)
# - Audit your own usage; even calling a library wrong can be insecure
# ===== Pitfalls =====
# - 'It is just for X' — your weak primitive ships everywhere eventually
# - Custom protocol design without an expert review
# - Updating crypto code last because 'it just works' (stale TLS = vulnerable TLS)
# - Believing that 'we have not been broken' = 'we are secure'
Why it matters
Use vetted crypto libraries. The math is hard; the side channels are harder; the maintenance is endless. Custom protocols are silently broken until someone notices. Reach for AEAD, Argon2id, vetted libraries, and timingSafeEqual. The day to roll your own crypto is the day you have published peer-reviewed papers and someone is paying for the audit.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Do not implement primitives yourself. Tiny mistakes are catastrophic. // Use libsodium / Tink / WebCrypto / OpenSSL. The smaller your code, the safer.Try it Yourself »
Discussion
Loading…