Academy

Module 1 Β· Modern Cryptography Fundamentals πŸ”’

Manish Garg
Manish Garg Associate CISSP Β· RingSafe
April 22, 2026
5 min read

Cryptography is the substrate beneath nearly every security control. Knowing what to use when β€” and what NOT to roll yourself β€” is the practical skill that separates engineers who build secure systems from those who introduce subtle vulnerabilities. This module covers the mental model of modern cryptography, the algorithms in widespread use in 2026, and the design patterns that prevent the common mistakes.

The categories you actually use

  • Symmetric encryption β€” same key encrypts and decrypts. Fast, used for bulk data. AES-256-GCM is the default
  • Asymmetric encryption β€” public/private keypair; encrypt with public, decrypt with private. Slow, used for key exchange and digital signatures. RSA-3072+ or elliptic curve (P-256, X25519, Ed25519)
  • Key exchange β€” establishing a shared secret over an insecure channel. ECDH (X25519), classic Diffie-Hellman with strong parameters
  • Hashing β€” one-way function. Output fixed length regardless of input. SHA-256 / SHA-3 / BLAKE2 β€” never MD5 or SHA-1 for security purposes
  • MAC (Message Authentication Code) β€” symmetric authentication. HMAC-SHA-256 standard
  • Password hashing β€” special slow hashes designed to resist brute force. Argon2id (preferred), bcrypt (still acceptable), scrypt. Never SHA-256 alone for passwords
  • Random number generation β€” cryptographically secure. /dev/urandom, os.urandom(), secrets module in Python β€” never random for security

The cardinal rule

Don’t roll your own crypto. Use vetted libraries. The reason every guide repeats this: cryptographic primitives are easy to use wrong in subtle ways that don’t fail loudly. A library that works produces the same output as one that’s vulnerable to a timing attack. You won’t notice until the attacker does.

Use:

  • libsodium (and its bindings) β€” opinionated, modern, hard to misuse. First choice for new code
  • NaCl β€” libsodium’s predecessor
  • Tink (Google) β€” high-level API; choices made for you
  • OpenSSL / BoringSSL β€” workhorses; lower level, more rope to hang yourself
  • Cryptography (Python) β€” Python’s pyca/cryptography library; safe defaults

Symmetric encryption β€” the patterns

AES-GCM (authenticated encryption)

AES in Galois/Counter Mode encrypts AND authenticates in one operation. Modern default for symmetric encryption. Gotchas:

  • Nonce must be unique per key. Reusing a nonce with the same key catastrophically breaks the cipher (key recovery possible). Strategy: random 96-bit nonce per message (probability of collision negligible) OR counter-based with strict accounting
  • Authentication tag must be verified before using plaintext. Some libraries return plaintext even if auth fails β€” read the docs
  • Don’t truncate the auth tag below 96 bits

ChaCha20-Poly1305

Alternative to AES-GCM. Faster on platforms without AES hardware (some IoT, mobile). Same security properties. Same nonce-uniqueness requirement.

What NOT to use

  • ECB mode β€” patterns visible in ciphertext. Never use
  • CBC without auth β€” vulnerable to padding-oracle attacks. If you must use CBC, wrap with HMAC (encrypt-then-MAC)
  • DES, 3DES β€” deprecated; small key/block sizes
  • RC4 β€” broken; many practical attacks

Asymmetric β€” the use cases

  • Key exchange: ECDH (X25519). Two parties exchange public keys, derive shared secret, use that secret with symmetric encryption
  • Digital signatures: Ed25519 (preferred), ECDSA P-256, RSA-PSS. Signs data with private key; anyone can verify with public key
  • Key encapsulation: RSA-OAEP for occasional encryption of small payloads (e.g., wrapping a symmetric key). Less common in modern designs (ECDH is preferred)

Why RSA is fading

RSA still works but:

  • Larger keys (3072+ bits) for equivalent security to elliptic curves (256 bits)
  • Slower
  • More implementation footguns (OAEP vs PKCS#1 v1.5; signing modes)
  • Common-factor attacks if RNG fails

For new designs: prefer X25519 / Ed25519 unless interop forces RSA.

Post-quantum cryptography

Quantum computers (when sufficiently large) break RSA, ECDH, ECDSA. Status in 2026:

  • NIST has standardized: ML-KEM (formerly Kyber) for key exchange, ML-DSA (Dilithium) and SLH-DSA for signatures
  • Deployment is happening incrementally β€” TLS 1.3 hybrid (X25519+ML-KEM) supported by major browsers and servers
  • “Harvest now, decrypt later” attacks make migration urgent for long-lived data
  • For most teams: track the standards; hybrid PQ on the roadmap; not yet a default

Hashing β€” modern uses

  • SHA-256: default general-purpose. SHA-3 / BLAKE2 are alternatives
  • SHA-512: faster on 64-bit systems; same security as SHA-256
  • BLAKE3: very fast, modern; good for non-crypto applications and increasingly crypto
  • HMAC-SHA-256: when you need symmetric authentication

What hashing does NOT do:

  • Encrypt β€” hashes are one-way; you can’t recover the input
  • Slow brute force on small inputs β€” hash a 4-digit OTP, attacker brute-forces 10000 hashes in milliseconds

Password hashing β€” the special case

Passwords need slow, memory-hard hashes. Recommended:

  • Argon2id β€” modern winner. Tune memory cost, time cost, parallelism for ~500ms hash time on your hardware
  • bcrypt β€” still widely used; cost factor 12-14 in 2026
  • scrypt β€” older but still acceptable

NEVER:

  • SHA-256 alone β€” billions of attempts per second on a GPU
  • MD5 β€” broken; trivially crackable
  • SHA-256 with a tiny salt prefix β€” slow only against rainbow tables, not GPUs

Random number generation

Use the OS’s CSPRNG:

# Python
import secrets
token = secrets.token_urlsafe(32)
api_key = secrets.token_hex(16)

# Node.js
const crypto = require('crypto');
const token = crypto.randomBytes(32).toString('base64url');

# Go
import "crypto/rand"
b := make([]byte, 32)
_, err := rand.Read(b)

NEVER use language-default random for security: Python’s random, JavaScript’s Math.random, Java’s Random. Those are predictable seed-based PRNGs.

Key management β€” the harder problem

Picking AES-256-GCM is easy. Managing the keys is where most cryptographic systems fail:

  • Where are keys stored? (HSM > KMS > encrypted file > env var > config file)
  • Who can access them?
  • How are they rotated?
  • What’s the recovery path if a key is lost?
  • How are keys versioned (so you can decrypt old data after rotation)?

Use platform KMS (AWS KMS, Azure Key Vault, GCP Cloud KMS) for cloud workloads. HashiCorp Vault for on-prem or hybrid. Hardware Security Modules (HSMs) for the highest assurance.

Encoding vs encryption

Common confusion. They are not the same:

  • Base64 β€” encoding. Reversible without a key. Just a different representation. Provides no security
  • Encryption β€” requires a key to reverse. Provides confidentiality (and with auth, integrity)

If a “secret” in your system is base64’d plain text, it is not secret.

Constant-time comparison

Comparing tokens, MACs, or password hashes with == is timing-vulnerable. Attacker measures comparison time to learn how many bytes matched. Use constant-time comparison:

# Python
import hmac
hmac.compare_digest(provided_token, expected_token)

# Node.js
const crypto = require('crypto');
crypto.timingSafeEqual(Buffer.from(provided), Buffer.from(expected));

# Go
import "crypto/subtle"
subtle.ConstantTimeCompare([]byte(provided), []byte(expected))

Where this leads

Module 2 covers TLS β€” the protocol everyone deploys, the version differences, cipher suites, and pitfalls. Module 3 covers PKI as a system β€” CAs, certificate lifecycle, ACME, certificate transparency. Module 4 covers secrets management at scale.