The Cryptographic Toolbox
Cryptography is not magic — it’s a toolbox of primitives with precise guarantees and precise failure modes. This topic gives you the working mental model a systems engineer needs: what each tool guarantees, when to use it, and — most importantly — the rules that make it safe (never roll your own, always use vetted libraries).
Symmetric Encryption: One Key for Both Sides
Symmetric encryption uses the same secret key to encrypt and decrypt. The modern standards are AES (block cipher, ubiquitous, hardware-accelerated — used by TLS, disk encryption, databases) and ChaCha20 (stream cipher, fast in software, popular in mobile contexts).
plaintext --encrypt(key)--> ciphertext --decrypt(key)--> plaintext
AES encrypts 128-bit blocks; real data needs a mode of operation that handles more than one block and ties blocks together. The critical operational details:
- Key length — AES-128 is secure today; AES-256 is the safe default. Keys must be random — a weak key (“password123”) defeats the strongest cipher.
- IV / nonce — most modes need a unique, unpredictable initialization vector per message. Reusing an IV with the same key leaks data (the classic CTR-mode disaster).
- Authenticated encryption (GCM/ChaCha20-Poly1305) — encrypt and authenticate in one primitive; this is what modern TLS uses. Bare encryption without authentication allows tampering — an attacker can flip ciphertext bits to flip plaintext bits.
The rule: use AES-GCM (or ChaCha20-Poly1305). Never “AES with a password” — that’s a KDF problem.
Asymmetric Encryption: Public and Private Keys
Asymmetric encryption uses a key pair: a public key anyone can have, and a private key only the owner holds.
- Encryption: anyone encrypts to the public key; only the private key decrypts. (Confidentiality — but hard to revoke and rarely the default.)
- The same machinery powers signatures (below) and key exchange (TLS’s ECDHE).
Algorithms: RSA (the classic, based on integer factorization) and ECC / Ed25519 (elliptic curve — smaller keys, faster, the modern preference). The operational rule: asymmetric crypto is slow — it’s used to exchange a session key or sign a digest, while the bulk data goes through symmetric encryption. That division is exactly how TLS works.
Cryptographic Hashing: One-Way Fingerprints
A cryptographic hash (SHA-256) maps any input to a fixed-size digest with three properties:
- Preimage resistance — given the digest, you can’t find an input that hashes to it.
- Second-preimage resistance — given an input, you can’t find a different input with the same digest.
- Collision resistance — you can’t find any two inputs with the same digest (not better than brute force).
Hashing guarantees integrity: hash(data) detects any change. Uses: file/software integrity, content-addressing (Git, container layers), password storage, and as the digest under signatures.
The famous caveat: SHA-256 is not password storage. Passwords are low-entropy, so attackers brute-force them against a hash table. Password storage needs a slow, salted hash — bcrypt, scrypt, or Argon2 — where each attempt is deliberately expensive, and every password gets a unique random salt.
HMAC: Authenticating with a Shared Secret
HMAC is a keyed hash — HMAC(key, message) — that proves the message was written by someone holding the shared key and wasn’t modified in transit. It’s the workhorse of message authentication between two parties that share a secret: API request signing, cookie tamper-evidence, webhook signatures (e.g., Stripe signing its requests). It is not encryption — the message is visible; HMAC proves authenticity.
Digital Signatures: Proof of Origin
A digital signature combines asymmetric crypto and hashing:
- The signer hashes the message.
- The signer encrypts the digest with their private key — that’s the signature.
- Anyone verifies by decrypting with the signer’s public key and comparing digests.
This gives authenticity (only the private key could have produced it), integrity (any change breaks the digest), and non-repudiation (the signer can’t deny signing). Signatures are how software is authenticated (package managers, container images — cosign), how TLS certificates bind identities to keys, and how JWTs/assertions are validated.
The identity binding is the hard part: a public key alone is just a number. Certificates (X.509) tie a public key to an identity, signed by a certificate authority (CA) — the trust chain you meet in TLS. An attacker can’t forge Google’s signature because they don’t hold Google’s private key.
TLS: The Whole Toolbox in One Handshake
TLS puts it together (the full handshake was covered in the HTTP topic; here’s the cryptographic view):
- Key exchange (ECDHE) — client and server agree on a fresh session key using ephemeral elliptic-curve Diffie-Hellman. Forward secrecy: the session key is destroyed at session end, so even a stolen long-term key can’t decrypt past traffic.
- Authentication — the server presents its certificate (public key + identity, CA-signed); the client verifies the signature chain.
- Bulk encryption — the session key drives AES-GCM/ChaCha20 for the actual data, with HMAC/authenticated modes ensuring integrity.
Cipher suites like TLS_AES_128_GCM_SHA256 literally spell this out: TLS version + symmetric cipher + mode + hash. Understanding the components is understanding what a cipher suite is choosing.
The Rules That Keep You Safe
- Never roll your own crypto — homegrown AES or “I hash with a salt twice” is how breaches happen. Use vetted libraries.
- Never implement crypto primitives from a blog post — use
libsodium/ the platform’scryptomodule. - Use authenticated encryption (AES-GCM/ChaCha20-Poly1305) as the default.
- Hash passwords with Argon2/bcrypt/scrypt + salt, never SHA-256 alone.
- Separate keys by purpose — an encryption key and a signing key are different keys; reuse is how one compromise cascades.
The Visualizer
Use the cryptography visualizer above to step through four toy-sized scenarios by hand — they use numbers small enough that you can verify every transform yourself:
- Symmetric (Caesar) — Alice and Bob share key
k = 3; watchHELLO WORLD → KHOOR ZRUOG → HELLO WORLD, and see why Eve is stuck. This is the mechanism; AES-GCM is the production version. - Hashing & Salt — hash “the quick brown fox…”, flip one letter to “…cog”, and watch ~half the digest bits change (avalanche). Then see why passwords need a salted slow hash.
- RSA — generate a real key pair from
p=61, q=53(n=3233, e=17, d=2753), encryptm=65toc=2790, decrypt it back, then sign and verify a digest. - Diffie-Hellman — with
g=5, p=23, Alice and Bob exchange public values (A=8, B=19) yet both compute the shared secrets=2while Eve can’t.
Each step highlights the active parties, the value boxes, and the formula — and the pseudocode panel tracks the algorithm line by line. This is the same math TLS uses (ECDHE on an elliptic curve, AES-GCM for bulk data) scaled down so you can check it.
Practice Trajectory
- Encrypt a file with AES-GCM and try to tamper with a ciphertext byte — observe the authentication failure.
- Hash a string with SHA-256 and confirm any single-bit change produces a completely different digest.
- Sign a message with an Ed25519 key and verify it with the public key; tamper and watch verification fail.
- Crack a (deliberately weak)
md5("password")style hash in a toy challenge, then repeat with a bcrypt-hashed password and feel the difference. - Inspect a real TLS certificate with
openssl s_clientand read the cipher suite, the CA chain, and the key type.
When It’s the Right Tool
| Situation | Takeaway |
|---|---|
| Data at rest / in transit | Symmetric encryption (AES-GCM) |
| Key exchange + auth over the wire | TLS (ECDHE + certificates) |
| Integrity / tamper detection | SHA-256 hash or HMAC |
| Proving origin of software/docs | Digital signatures |
| Storing passwords | Argon2/bcrypt/scrypt + salt |