H4CK0R Network Security Β· Recon Β· Education

πŸ”‘ Cryptography You Actually Need to Know

Cryptography You Actually Need to Know

Almost nobody breaks AES. Systems get broken at the joints: a mode of operation chosen badly, a nonce reused, a key derived from a password with a fast hash, a signature verified with a comparison that leaks timing. This module covers the primitives and, more importantly, the rules for wiring them together.

Two families, two different jobs

Symmetric cryptography uses one key for both encryption and decryption. It is fast β€” a modern CPU with AES-NI instructions encrypts gigabytes per second β€” but it leaves you with the key distribution problem: both sides must already share a secret.

Asymmetric (public key) cryptography uses a key pair. Anyone can encrypt to your public key or verify your signature; only the private key decrypts or signs. It is orders of magnitude slower and it can only handle small messages, so it is never used to encrypt bulk data directly.

Real protocols are hybrid. A TLS connection uses an asymmetric key agreement (ECDHE) plus a signature to authenticate the server, derives a symmetric key from that exchange, and then encrypts every byte of traffic with AES-GCM or ChaCha20-Poly1305. Asymmetric crypto solves trust and key agreement; symmetric crypto does the work.

AES is not the interesting part β€” the mode is

AES is a block cipher: it transforms exactly 16 bytes at a time under a 128-, 192- or 256-bit key. Real messages are not 16 bytes, so a mode of operation describes how blocks are chained. The mode, not the cipher, is where systems fail.

ModeWhat it doesVerdict
ECBEncrypts each block independentlyNever use. Identical plaintext blocks produce identical ciphertext blocks, so structure leaks straight through. The classic demonstration is a bitmap of the Linux mascot encrypted in ECB β€” the penguin is still clearly visible in the "encrypted" image.
CBCXORs each plaintext block with the previous ciphertext block; needs an IV and paddingOnly with a separate MAC, applied encrypt-then-MAC. Unauthenticated CBC is malleable and vulnerable to padding-oracle attacks (Vaudenay, 2002), the family that produced POODLE and Lucky 13.
CTREncrypts a counter to make a keystream, then XORs it with the plaintextNo padding, parallelisable, but provides zero integrity on its own β€” and a repeated counter is catastrophic.
GCMCTR encryption plus a GHASH authentication tag (AEAD)Preferred. Confidentiality and integrity in one pass; hardware-accelerated on x86 and ARM.
ChaCha20-Poly1305Stream cipher plus a Poly1305 tag (AEAD, RFC 8439)Preferred, especially without AES hardware β€” it is fast and naturally constant-time in software. This is why mobile clients often negotiate it.

AEAD means Authenticated Encryption with Associated Data. It gives you three things at once: the ciphertext is confidential, any modification is detected (the 128-bit tag fails to verify), and you can bind unencrypted context β€” a message ID, a record header, a user ID β€” into the tag as associated data, so an attacker cannot move a valid ciphertext to a different context. If you are choosing a mode in 2026 and it is not AEAD, choose again.

// Browser-native AES-256-GCM. No library, no CDN.
const key = await crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);

const iv = crypto.getRandomValues(new Uint8Array(12));   // 96-bit nonce
const ct = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv, additionalData: aad }, key, plaintext);
// ct = ciphertext || 128-bit authentication tag (WebCrypto appends it)
A command-line trap openssl enc -aes-256-gcm does not do what people assume: the enc utility has no way to emit or check an AEAD tag, so you get CTR-mode encryption with no integrity at all. Use a library binding (or openssl_encrypt() in PHP, which returns the tag by reference) rather than enc for AEAD.

Nonces and IVs: the single most common fatal bug

A nonce is a number used once. It does not need to be secret and it usually travels alongside the ciphertext in the clear. It must never repeat under the same key.

Hash functions: three properties, and what "broken" means

A cryptographic hash maps arbitrary input to a fixed-size digest, and is expected to offer:

MD5 collisions have been practical since Wang and Yu's 2004–2005 work, and chosen-prefix collisions are cheap. The Flame malware used an MD5 chosen-prefix collision in 2012 to forge a code-signing certificate that chained to a Microsoft CA. SHA-1 fell publicly in February 2017 when CWI Amsterdam and Google published SHAttered, two different PDFs with the same SHA-1 digest; a chosen-prefix SHA-1 collision followed in 2020.

What that does mean: never use MD5 or SHA-1 anywhere an attacker influences the content being hashed β€” certificates, code signing, git-style content addressing of untrusted input, deduplication that grants trust. What it does not mean: a collision is not a preimage. Nobody can take the MD5 digest of an unpredictable input and invert it β€” though a digest of something guessable, such as a password, is still recovered instantly by brute force, which is a different problem entirely. HMAC-MD5 and HMAC-SHA-1 are not broken by these collision results, because HMAC's security rests on the compression function behaving as a pseudorandom function rather than on collision resistance β€” which is why HMAC-SHA-1 survives inside TOTP. Migrate anyway; there is no reason to stay.

Use SHA-256 or SHA-512 (SHA-2), SHA-3, or BLAKE2/BLAKE3 for new work. Generate digests across algorithms β†’

$ printf 'hello' | sha256sum
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824  -

HMAC: why you cannot just glue a key to a message

The obvious way to authenticate a message with a shared key is H(key || message). It is broken for every Merkle–DamgΓ₯rd hash β€” MD5, SHA-1 and the whole SHA-2 family β€” because of length extension. The digest is the internal state at the end of the input, so an attacker who knows H(key || message) and the length of the key can resume from that state and compute a valid tag for key || message || padding || attacker_data without ever learning the key.

HMAC (RFC 2104, FIPS 198-1) fixes this with a nested construction:

HMAC(K, m) = H( (K βŠ• opad) || H( (K βŠ• ipad) || m ) )

The outer hash means the attacker never sees a raw internal state. SHA-3 and BLAKE2 are sponge/HAIFA constructions and are not length-extendable, so H(key || message) is safe there β€” but there is no reason not to use HMAC, which is universally available. Always compare tags with a constant-time comparison (hash_equals() in PHP, hmac.compare_digest() in Python); a byte-by-byte == leaks the correct tag one byte at a time. Compute an HMAC β†’

Key derivation: two completely different problems

"KDF" covers two jobs that people constantly conflate:

Using a plain hash β€” even SHA-256, even salted β€” for the second job is the mistake that turns a database leak into a mass account takeover. That is covered in depth in the passwords module.

Public key: RSA, elliptic curves, and equivalent strength

RSA security rests on integer factorisation; elliptic-curve security rests on the discrete logarithm problem in a curve group. ECC reaches the same security level with dramatically smaller keys, which is why every modern protocol defaults to it. NIST SP 800-57 Part 1 Rev 5 gives the comparison:

Security strengthRSA / finite-field DH modulusElliptic curveSymmetric equivalent
112 bits2048224–2553TDEA
128 bits3072256–383 (P-256, Curve25519)AES-128
192 bits7680384–511 (P-384)AES-192
256 bits15360512+ (P-521)AES-256

RSA-2048 is the practical floor and is still everywhere; RSA-3072 or ECDSA P-256 is the sensible target for new certificates. Ed25519 (EdDSA over Curve25519, RFC 8032) is the modern default for signing outside the web PKI β€” 32-byte public keys, 64-byte signatures, roughly 128-bit security, deterministic nonces that remove an entire class of catastrophic implementation bug, and no curve-parameter footguns. X25519 (RFC 7748) is its key-agreement counterpart. Note that RSA padding matters as much as key size: use OAEP for encryption and PSS for signatures, never raw or PKCS#1 v1.5 encryption padding, which has been attackable since Bleichenbacher's 1998 result. Generate keys and secrets β†’

Diffie-Hellman and forward secrecy

Diffie-Hellman lets two parties who have never met derive a shared secret over a public channel: each sends a public value, each combines it with their own private value, and both arrive at the same result that an eavesdropper cannot compute. When both sides generate a fresh, throwaway key pair for every connection β€” ephemeral DH, the E in ECDHE β€” you get forward secrecy: recording today's traffic and stealing the server's long-term private key next year does not decrypt it, because the ephemeral keys were discarded. Static RSA key exchange had no forward secrecy at all, which is precisely why TLS 1.3 removed it.

Signing is not encryption

Encryption uses the recipient's public key and is undone with their private key: it provides confidentiality. Signing uses the signer's private key and is verified with their public key: it provides authenticity and integrity, and no confidentiality whatsoever β€” a signed message is still fully readable. The two use different padding schemes and, in good designs, different key pairs. "Encrypting with the private key" is a phrase you will see; treat it as a red flag, because a real signature scheme (PSS, ECDSA, Ed25519) is not simply encryption run backwards.

Randomness

Keys, nonces, salts, session identifiers and password-reset tokens must come from a cryptographically secure pseudorandom number generator seeded by the operating system. Math.random() is not one β€” V8 implements it with xorshift128+, and an observer who sees a handful of outputs can reconstruct the internal state and predict the rest. Use crypto.getRandomValues() in the browser, random_bytes() in PHP, secrets in Python, crypto/rand in Go, or /dev/urandom. Every generator on this site β€” the key generator, the password generator and the AES tool's nonces β€” draws from crypto.getRandomValues(), and nothing generated in your browser is transmitted anywhere.

Post-quantum, honestly

Two quantum algorithms matter. Shor's algorithm solves integer factorisation and discrete logarithms efficiently, which would break RSA, finite-field Diffie-Hellman and all elliptic-curve cryptography β€” every asymmetric algorithm in use on the public internet today. Grover's algorithm gives only a quadratic speed-up on unstructured search, so it nominally halves the effective strength of a symmetric key; AES-256 remains comfortable, and Grover parallelises poorly enough that the practical margin is wider than the headline suggests. Hash functions are similarly bruised but not broken.

No quantum computer capable of running Shor's algorithm against a 2048-bit key exists, and nobody can credibly say when one will. The concern that justifies acting now is harvest now, decrypt later: an adversary who records encrypted traffic today can decrypt it whenever the capability arrives, so anything that must stay secret for a decade is already exposed. Long-lived signatures matter less, because a signature made today can simply be re-made with a new algorithm.

NIST published the first post-quantum standards in August 2024: FIPS 203 (ML-KEM, derived from CRYSTALS-Kyber, for key encapsulation), FIPS 204 (ML-DSA, from CRYSTALS-Dilithium, for signatures) and FIPS 205 (SLH-DSA, from SPHINCS+, a conservative hash-based signature). Deployment on the web has begun with hybrid key exchange β€” a TLS group that combines X25519 with ML-KEM-768, so the connection stays secure if either component holds. If you see an unfamiliar group name in a modern handshake, that is usually what it is.

Don't roll your own: the practical rules

Practise on the toolkit: AES-GCM encrypt/decrypt β†’ Β· key & secret generator β†’ Β· hash generator β†’ Β· HMAC calculator β†’

Key takeaways
  • Hybrid is normal: asymmetric crypto for key agreement and authentication, symmetric AEAD for the data.
  • The mode of operation is the decision that matters. Prefer AES-GCM or ChaCha20-Poly1305; never ECB; never unauthenticated CBC or CTR.
  • A repeated nonce under the same key breaks confidentiality and, in GCM, authentication too.
  • MD5 and SHA-1 are dead for collision resistance β€” which kills them for certificates and signatures β€” but a collision is not a preimage.
  • HMAC exists because H(key || message) is length-extendable on MD5, SHA-1 and SHA-2.
  • Password hashing (Argon2id, scrypt, bcrypt, PBKDF2) and key derivation from strong secrets (HKDF) are different problems with different tools.
  • ECC gets 128-bit security from a 256-bit key; RSA needs 3072 bits for the same.
  • Randomness must come from a CSPRNG. Math.random() is predictable.
  • Post-quantum standards exist (FIPS 203/204/205, 2024); hybrid TLS key exchange is the migration path, and "harvest now, decrypt later" is the reason to care today.