contentintech
Learn/cybersecurity/Cryptography
Intermediate~24 min read

Cryptography

Symmetric and asymmetric encryption, hashing, password storage with bcrypt/argon2, digital signatures, and TLS.

HashingAESRSATLS

What Cryptography Protects

Cryptography is the science of protecting information so that only intended parties can read or trust it. Applied security rests on four goals, and most real-world protocols combine several of them:

GoalMeaningPrimitive
ConfidentialityOnly authorized parties can read the dataEncryption (AES, ChaCha20)
IntegrityData has not been alteredHashes, MACs
AuthenticityData really came from the claimed senderMACs, signatures
Non-repudiationSender cannot later deny sending itDigital signatures

Rule zero

Don't roll your own crypto. Use well-reviewed, high-level libraries (libsodium, Web Crypto, Tink, or your platform's audited primitives) rather than composing low-level pieces yourself. Most real-world failures come from misusing correct algorithms, not from broken math.

Symmetric Encryption

In symmetric encryption the same secret key both encrypts and decrypts. It is fast and used for bulk data. The modern default is AES-256-GCM or ChaCha20-Poly1305 — both are AEAD (Authenticated Encryption with Associated Data) ciphers that provide confidentiality and integrity in one operation.

Modes and Why They Matter

A block cipher like AES needs a mode of operation to encrypt data longer than one block. The mode choice is where security is won or lost:

  1. ECB is broken for real data. It encrypts identical plaintext blocks to identical ciphertext blocks, leaking structure (the infamous "ECB penguin"). Never use ECB.
  2. You need an IV/nonce. Modes like GCM and CTR require a unique initialization vector/nonce per encryption so the same plaintext produces different ciphertext. Reusing a nonce with the same key in GCM is catastrophic — it can leak the authentication key.
  3. Authenticate, don't just encrypt. Plain CBC has no integrity check and is vulnerable to padding-oracle and bit-flipping attacks. AEAD modes bind an authentication tag so tampering is detected.

AES-GCM Example (Node.js)

javascript
const crypto = require('crypto');

// 256-bit key from a secure source (KMS / env, not hardcoded)
const key = crypto.randomBytes(32);

function encrypt(plaintext) {
  const iv = crypto.randomBytes(12);            // 96-bit nonce, UNIQUE per message
  const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
  const ct = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
  const tag = cipher.getAuthTag();              // integrity tag
  return { iv, ct, tag };
}

function decrypt({ iv, ct, tag }) {
  const d = crypto.createDecipheriv('aes-256-gcm', key, iv);
  d.setAuthTag(tag);                            // throws if tampered
  return Buffer.concat([d.update(ct), d.final()]).toString('utf8');
}

Asymmetric Encryption & Key Exchange

Asymmetric (public-key) crypto uses a key pair: a public key anyone can hold and a private key kept secret. It is slow, so in practice it is used to establish or protect a symmetric key, and to sign.

RSA relies on the difficulty of factoring large numbers; use 3072-bit keys or larger in 2026, and always with padding (OAEP for encryption, PSS for signatures). ECC (elliptic-curve) offers the same security with much smaller keys — a 256-bit curve like P-256 or Curve25519 roughly matches 3072-bit RSA. For key agreement, Diffie-Hellman and its elliptic-curve form ECDH let two parties derive a shared secret over an untrusted channel. Ephemeral variants (DHE/ECDHE) give forward secrecy: capturing today's traffic and later stealing the long-term key still does not decrypt past sessions.

Post-quantum on the horizon

In 2024 NIST finalized post-quantum standards — ML-KEM (Kyber) for key exchange and ML-DSA (Dilithium) / SLH-DSA for signatures. Through 2026 the practical move is hybrid key exchange (e.g., X25519 + ML-KEM), already shipping in TLS in major browsers. Plan for crypto-agility so algorithms can be swapped later.

Hashing

A cryptographic hash maps any input to a fixed-size digest and should be: one-way (can't reverse), collision-resistant (can't find two inputs with the same digest), and deterministic. Use SHA-256, SHA-512, or SHA-3 for general integrity. MD5 and SHA-1 are broken — practical collisions exist — so never use them for signatures, certificates, or any security decision.

bash
# File integrity check
sha256sum ubuntu.iso
# compare against the publisher's published SHA-256

# In code (Node)
crypto.createHash('sha256').update(data).digest('hex');

Password Storage (Critical)

Passwords must never be stored in plaintext, encrypted (reversible), or with a plain fast hash like SHA-256. Those are far too fast, so attackers who steal the database can try billions of guesses per second. Use a purpose-built password hashing function that is deliberately slow and salted: Argon2id (first choice in 2026), scrypt, or bcrypt.

A per-password salt (stored alongside the hash) defeats rainbow tables and ensures identical passwords get different hashes. These functions generate and embed the salt for you.

javascript
# Argon2id (Node, 'argon2' package) - recommended
const argon2 = require('argon2');

const hash = await argon2.hash(password, {
  type: argon2.argon2id,
  memoryCost: 19456,   // ~19 MiB (OWASP 2026 baseline)
  timeCost: 2,
  parallelism: 1,
});
const ok = await argon2.verify(hash, password);

# bcrypt alternative (Python) - cost factor >= 12
import bcrypt
h = bcrypt.hashpw(pw.encode(), bcrypt.gensalt(rounds=12))
ok = bcrypt.checkpw(pw.encode(), h)

MACs, HMAC, and Digital Signatures

A MAC (Message Authentication Code) proves integrity and authenticity using a shared secret key. HMAC (e.g., HMAC-SHA256) is the standard construction. When verifying a MAC, always use a constant-time comparison to avoid timing attacks. A digital signature is the asymmetric analog: the signer uses a private key, and anyone can verify with the public key — providing authenticity and non-repudiation. Common schemes: Ed25519 (fast, modern) and RSA-PSS / ECDSA.

Key Management, PKI, and TLS

Cryptography is only as strong as its key handling. Keep keys out of source code and config files — use a KMS, HSM, or secrets manager, rotate keys, and scope access tightly. PKI (Public Key Infrastructure) binds public keys to identities via certificates signed by trusted Certificate Authorities. Your browser trusts a chain up to a root CA.

A TLS 1.3 handshake (2026 default) at a high level: the client and server negotiate parameters, perform an ephemeral ECDH key exchange to derive a shared secret, the server proves its identity with a certificate and signature, and both derive symmetric AEAD keys for the session. TLS 1.3 dropped legacy ciphers, made forward secrecy mandatory, and reduced the handshake to one round trip.

Common Mistakes

  1. Hardcoded keys/secrets in code or repos — use a secrets manager and rotate anything committed.
  2. Nonce/IV reuse with the same key (especially in GCM) — always generate fresh, unique nonces.
  3. Rolling your own crypto or inventing protocols — use vetted libraries.
  4. Weak randomness — never use Math.random() or rand() for keys/tokens; use a CSPRNG (crypto.randomBytes, secrets, /dev/urandom).
  5. Encrypt-only, no authentication — prefer AEAD; never trust unauthenticated ciphertext.
  6. Fast hashes for passwords or missing salts — use Argon2id/scrypt/bcrypt.

Algorithm Recommendations for 2026

PurposeUseAvoid
Symmetric encryptionAES-256-GCM, ChaCha20-Poly1305DES, 3DES, RC4, AES-ECB
Key exchangeECDHE (X25519), hybrid + ML-KEMStatic RSA key transport
SignaturesEd25519, RSA-PSS 3072+, ML-DSARSA-PKCS#1 v1.5, DSA
HashingSHA-256, SHA-512, SHA-3MD5, SHA-1
Password hashingArgon2id, scrypt, bcryptPlain SHA/MD5, unsalted hashes
TransportTLS 1.3TLS 1.0/1.1, SSLv3

Practice Exercises

  1. Hash and verify passwords with Argon2id (and separately bcrypt cost 12) in a small script; confirm identical passwords produce different stored hashes.
  2. Encrypt and then decrypt a file with AES-256-GCM, storing the nonce and tag; then deliberately flip one ciphertext byte and confirm decryption fails.
  3. Generate an RSA-3072 and an ECC (P-256 / Ed25519) key pair with OpenSSL and compare key sizes and generation speed.
  4. Inspect a real TLS certificate: openssl s_client -connect example.com:443, then decode it and note issuer, validity, and signature algorithm.
  5. Sign a message with your private key and verify it with the public key; then alter the message and confirm verification fails.
  6. Solve at least one introductory challenge on CryptoHack or a CTF crypto category to see how nonce reuse or a weak mode is actually exploited.

Section navigation