The Engineering Codex/Application Security Engineering
DAY 2 · AM
03 / 09

Cryptography You Actually Use

schedule6 minsignal_cellular_altIntermediate1,313 words
Symmetric vs asymmetric, hashing vs encryption, what makes a good password storage, and the TLS handshake — without the math, but with all the engineering trade-offs that decide which mode to pick.

What you will learn

01The Three Goals — and Their Primitives
02Symmetric vs Asymmetric — Pick One, Not Both
03Hashing vs Encryption — A Common Confusion
04Digital Signatures — Identity at a Distance
05The TLS 1.3 Handshake (in 4 messages)
06Crypto Pitfalls Engineers Actually Hit

Cryptography is the rare subfield where building it yourself is almost always wrong. The job of an engineer is not to invent primitives — it is to know which primitive solves which problem, why a single misuse breaks the whole guarantee, and which library does the right thing by default. This chapter is about the mental models that let you read a security RFC and a code review at the same speed.

⚠️
Rule zero
Never roll your own crypto. Use vetted libraries: libsodium / NaCl, the platform-native KMS, AWS/GCP envelope encryption, or your language's audited stdlib. The exceptions are so rare that for the next decade of your career, the answer is use the library.

The Three Goals — and Their Primitives

🔒 Confidentiality
  • Symmetric encryption — AES-GCM, ChaCha20-Poly1305
  • Hybrid — RSA/ECDH wraps a symmetric key
  • Used for: TLS payloads, disk encryption, secrets at rest
✍️ Integrity & Authenticity
  • MAC — HMAC-SHA-256 (shared secret)
  • Digital signature — Ed25519, ECDSA, RSA-PSS (key pair)
  • Used for: webhooks, JWTs, software updates, code signing

Symmetric vs Asymmetric — Pick One, Not Both

Or rather: pick the right one for the right job. The two families differ in keys, speed, and what problem they solve.

Symmetric one shared secret Alice Bob 🔑 🔑 same key AES-GCM(plaintext, k) Asymmetric public key + private key Alice Bob 🔓 🔐 Bob's public Bob's private RSA-OAEP(plaintext, pub_b)
Symmetric crypto is fast (gigabytes/sec) but needs key exchange. Asymmetric crypto solves key exchange but is ~1000× slower — so we use it only to set up symmetric keys.
SymmetricAsymmetric
KeysOne shared secretPublic + private pair
AlgorithmsAES-GCM, ChaCha20-Poly1305RSA, ECDSA, Ed25519, ECDH (X25519)
Speed~GB/s on modern CPUs~thousands of ops/s
SolvesBulk encryption / authenticationKey exchange, signatures, identity
PitfallKey distributionSlow; padding/curve mistakes

The Modes Matter

AES alone is a block cipher; mode turns it into something usable. Use AES-GCM or ChaCha20-Poly1305 — both are authenticated (AEAD), meaning they encrypt and integrity-check in one operation. Avoid AES-ECB entirely (a meme of "never use ECB" exists for a reason: identical plaintext blocks produce identical ciphertext). Avoid AES-CBC without an HMAC — the moment you encrypt without authentication you open the door to padding-oracle attacks (Bleichenbacher 1998, POODLE 2014).

🚫
Encryption ≠ authentication
If you can decrypt without verifying integrity, you can be tricked into decrypting something an attacker chose. Always use AEAD modes (GCM, Poly1305, OCB), or encrypt-then-MAC if you must compose.

Hashing vs Encryption — A Common Confusion

People say "the password is encrypted in the database." Almost always wrong — and dangerously so. Encryption is reversible (with the key). Hashing is one-way. Different problems, different tools.

Avalanche effect (one-bit input change ⇒ ~50% of output bits flip) hello hellp SHA-256 one-way 2cf24dba5fb0a30e26… d76b39f00ec1f7c43c… Same length. No trace of the input. Cannot be reversed.
A cryptographic hash. Two inputs differing by one bit produce outputs that look unrelated.
PropertyEncryptionHashing
Reversible?Yes (with key)No
Output size≥ inputFixed (e.g., 256 bits)
Use caseHide content you'll later readVerify content; index it; store passwords
ExamplesAES-GCM, RSA, ChaCha20SHA-256, SHA-3, BLAKE2/3

Password Storage — A Special Hash Problem

SHA-256 of a password is not enough. SHA-256 is fast — billions of guesses per GPU per second. The whole point of password hashing is to be deliberately slow and memory-hard, so each guess costs the attacker.

passwordhunter2 + unique saltrandom 16 bytes argon2idslow + memory-hardm=64MB t=3 p=4 stored hash$argon2id$v=19$…
Password hashing pipeline. The salt makes precomputed (rainbow-table) attacks impossible; argon2 makes brute-force expensive even with custom hardware.
AlgorithmYearStatus (2025)Use when
MD5, SHA-1, plain SHA-256❌ Broken / unsuitable for passwordsNever, for passwords
bcrypt1999✅ OK; max 72-byte inputLegacy systems; widely available
scrypt2009✅ Good; memory-hardWhere argon2 is unavailable
Argon2id2015✅ Recommended (RFC 9106, OWASP)New systems
Quick check
A team uses SHA-256(password + global_pepper) to store passwords. List two reasons this is unsafe.
Show answer
(1) Speed. SHA-256 is fast; a modern GPU can try billions of guesses per second. Password storage demands a deliberately slow / memory-hard function. (2) No per-user salt. A single global value lets an attacker who steals the database (and pepper) crack every password in parallel via a precomputed table. Use Argon2id with a unique random salt per user.

Digital Signatures — Identity at a Distance

A signature proves who produced a piece of data and that it has not changed since. The signer keeps a private key; anyone with the public key can verify. This is the spine of TLS certificates, software updates, JWTs, code signing, and SSH.

Modern choices, in order of preference for new code:

  • Ed25519 — fast, small, modern, no parameter mistakes possible. Default for new systems.
  • ECDSA P-256 — widely supported in HSMs and JWT libraries.
  • RSA-PSS 3072 — when interop demands RSA. Avoid plain RSA-PKCS#1 v1.5 if you can.

The TLS 1.3 Handshake (in 4 messages)

You will read about TLS hundreds of times in this career. The handshake is fewer moving parts than it looks.

Client Server ClientHello supported ciphers, key share, SNI ServerHello + Cert + Finished picks cipher, sends key share, signs handshake Finished (encrypted) verifies cert, derives keys via ECDHE (X25519) Application Data 🔒 AES-GCM or ChaCha20-Poly1305 with the derived session key 1. 2. 3.
TLS 1.3 needs 1 round trip (1-RTT) before encrypted data flows. ECDHE gives forward secrecy: even if the server's private key leaks tomorrow, today's traffic stays safe.

Three things to notice:

  1. Asymmetric crypto runs once — to authenticate the server (signature on the handshake) and exchange a session key. Bulk encryption is symmetric.
  2. Forward secrecy — the session key is derived from ephemeral key shares, not the server's certificate key. Compromising the cert later does not retroactively decrypt sessions.
  3. The client validates the certificate — name, validity, chain to a trusted root. We will dissect this on Day 5.

Crypto Pitfalls Engineers Actually Hit

  • Reusing a nonce with AES-GCM — catastrophic; reveals XOR of the two plaintexts. Use a random 96-bit nonce or a counter you store.
  • Using == to compare HMACs — leaks via timing. Use hmac.compare_digest / crypto.timingSafeEqual.
  • Trusting a JWT's alg header — the legendary alg: none bypass. Pin the algorithm server-side.
  • Encrypting without authentication — opens padding-oracle attacks. Use AEAD.
  • Generating keys with Math.random() — predictable. Use crypto.getRandomValues or os.urandom.
  • Hardcoding keys in source — they end up in git history, container images, and Slack. Use a secret manager.
Flashcard
Why must a webhook receiver compare HMAC signatures with a constant-time function?
Click to flip ↻
Answer
A normal byte-by-byte compare exits as soon as it finds a mismatched byte. The remote attacker measures response time and learns one byte at a time, recovering the whole MAC. Constant-time comparison takes the same time regardless — no timing side-channel.
Mnemonic — picking primitives
"Hash to fingerprint, MAC to verify with a shared secret, sign to prove identity at a distance, encrypt to hide."
  • Hash — Argon2id (passwords) · SHA-256 / BLAKE3 (other)
  • MAC — HMAC-SHA-256
  • Sign — Ed25519 first, ECDSA P-256 if forced
  • Encrypt — AES-GCM or ChaCha20-Poly1305
🔑
Key takeaways
1) Symmetric for bulk; asymmetric for key exchange and signatures — TLS uses both. 2) Always pick AEAD modes (GCM, Poly1305). 3) Passwords use Argon2id with per-user salt — never plain SHA-X. 4) Never roll your own; never trust the JWT alg header; never compare MACs with ==; never reuse a nonce.

Finished reading?