Skip to content

Cryptography Primitives#

Problem statement (interviewer prompt)

A service must authenticate users with passwords, encrypt sensitive PII at rest, verify request integrity, and sign API responses. Pick the right cryptographic primitive for each task and explain the security guarantees.

Cryptography in production reduces to a small set of primitives composed correctly. Mixing them up is the root cause of most real-world breaches.

flowchart TB
  subgraph Hash[Hashing - one-way]
    H1[SHA-256, SHA-3, BLAKE3]
    H2[Password: bcrypt, argon2, scrypt]
  end
  subgraph SymKey[Symmetric encryption]
    S1[AES-GCM, ChaCha20-Poly1305]
    S2[same secret to encrypt + decrypt]
  end
  subgraph Asym[Asymmetric encryption + signatures]
    A1[RSA, ECDSA, Ed25519]
    A2[public/private keypair]
  end
  subgraph MAC[Message authentication]
    M1[HMAC-SHA256]
    M2[shared secret + hash]
  end

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class H1,H2,S1,S2,A1,A2,M1,M2 service;
Public-key encryption: Alice's public key encrypts, only Alice's private key decrypts
Source: Wikimedia Commons. CC BY-SA / public domain.

Rule one: never invent your own primitive. Rule two: never use a primitive outside its design space.

The handful of primitives a backend engineer is likely to touch, with the rules that keep them safe.

Hash functions#

A one-way function: digest = H(message). Same input always yields same digest; finding two inputs with the same digest is computationally infeasible.

Algorithm Use
SHA-256 General hashing, content addressing, Merkle trees
SHA-3 / Keccak Newer; not faster but parallel-friendly
BLAKE3 Fastest modern; parallel by design
MD5 Broken; non-security checksums only
SHA-1 Broken since 2017 (SHAttered)

Do not use plain SHA for passwords - too fast; brute-force takes seconds.

Password hashing#

Tuned to be slow and memory-hard to resist brute-force.

Algorithm Notes
bcrypt Old workhorse; cost factor 12+
scrypt Memory-hard
argon2id OWASP recommended; tune t, m, p
PBKDF2 FIPS-required where argon2 unavailable; iterations 600k+

Always salt; bcrypt/argon2/scrypt salt automatically.

Symmetric encryption (AEAD)#

Same key encrypts and decrypts. AEAD (Authenticated Encryption with Associated Data) bundles encryption with integrity:

Algorithm Notes
AES-GCM (256) Default; AES-NI hardware accel
ChaCha20-Poly1305 Wins on mobile without AES-NI
XChaCha20-Poly1305 Extended nonce; safer for high-volume
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key = AESGCM.generate_key(bit_length=256)
nonce = os.urandom(12)  # MUST be unique per (key, message)
ct = AESGCM(key).encrypt(nonce, plaintext, associated_data=b"req-id-123")

Nonce reuse with the same key destroys all confidentiality. XChaCha20 uses 192-bit nonces so random generation is safe; AES-GCM uses 96-bit nonces - safer to use a counter than os.urandom.

Asymmetric encryption and signatures#

Public/private keypair. Anyone with the public key can verify a signature; only the private-key holder can produce one.

Algorithm Key size Use
RSA-2048+ 2048-4096 bit TLS certs, JWT RS256
ECDSA P-256 256-bit TLS, JWT ES256
Ed25519 256-bit SSH, modern signatures; safer defaults
X25519 256-bit Key exchange (Diffie-Hellman)
sequenceDiagram
  participant A as Alice
  participant B as Bob
  Note over A,B: signing
  A->>A: signature = sign(sk_A, message)
  A->>B: message + signature
  B->>B: verify(pk_A, message, signature)

Use Ed25519 for new signing systems; RSA/ECDSA where you need ecosystem compatibility (TLS, JWT, PKI).

HMAC#

Symmetric integrity: HMAC(key, message) = digest. Anyone with the shared key can verify, but no one without the key can forge.

import hmac, hashlib
mac = hmac.new(secret, b"payload", hashlib.sha256).hexdigest()

Used for webhook signatures (Stripe, GitHub), session cookies, JWT HS256.

Key derivation (KDF)#

Stretch low-entropy input (password, master secret) into one or more strong keys:

KDF Use
HKDF Derive child keys from a strong master
PBKDF2 Derive from password
argon2 / scrypt Same; memory-hard variants
TLS key schedule Built on HKDF

Random number generation#

  • Use os.urandom, secrets, crypto.getRandomValues, /dev/urandom.
  • Never Math.random(), rand(), or any non-CSPRNG for security purposes.

Public-key infrastructure (PKI)#

flowchart TB
  Root[Root CA] --> Inter[Intermediate CA]
  Inter --> Cert[Server cert]
  Cert --> Key[Server private key]

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class Root,Inter,Cert datastore;
    class Key service;

Browsers ship root CAs; intermediate CAs are issued by roots; servers get leaf certs from intermediates.

Common pitfalls#

  • Encrypting passwords: passwords get hashed (argon2), never encrypted.
  • Using AES-ECB: leaks patterns in ciphertext. Use AES-GCM.
  • Hand-rolled crypto: use libsodium, cryptography, tink instead.
  • No constant-time compare: timing attacks reveal MAC validity; use hmac.compare_digest.
  • Hard-coded keys: load from a secret manager (Vault, AWS Secrets Manager, KMS).

Where primitives are used#

flowchart TB
  CR((Crypto<br/>primitives))
  SEC[Security fundamentals<br/>parent topic]
  TLS[TLS / mTLS<br/>applied crypto in transit]
  JWT[JWT internals<br/>signed tokens]
  OAUTH[OAuth / SSO<br/>uses asymmetric crypto]
  SEC --> CR
  CR --> TLS
  CR --> JWT
  CR --> OAUTH

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class CR service;
    class SEC,TLS,JWT,OAUTH datastore;

Glossary & fundamentals#

Tag Concept What it is Page
HLD Security fundamentals parent topic security-fundamentals
HLD TLS mTLS applied crypto in transit tls-mtls
HLD JWT internals signed tokens jwt-internals
HLD OAuth SSO uses asymmetric crypto oauth-sso

Quick reference#

Pick by task#

Task Primitive
Content integrity SHA-256
Password storage argon2id / bcrypt
Encrypt at rest / in transit AES-GCM-256 or ChaCha20-Poly1305
Message integrity (shared secret) HMAC-SHA256
Identity / signature Ed25519 (or RSA-2048 / ECDSA P-256 for compat)
Key exchange X25519
Derive multiple keys from one HKDF
Random secrets os.urandom / secrets / CSPRNG

Don'ts#

  • Math.random() / rand() for security
  • AES-ECB (leaks patterns)
  • Encrypt passwords (hash, don't encrypt)
  • Hand-roll crypto (use libsodium, cryptography, tink)
  • Reuse nonces with AES-GCM same key
  • Compare MACs with == (use constant-time)

Key sizes#

  • AES: 256-bit symmetric
  • RSA: 2048-4096 bit
  • ECDSA: 256-bit (P-256) or 384-bit
  • Ed25519: 256-bit
  • HMAC: ≥256-bit key

Algorithm shelf life#

  • MD5, SHA-1: broken
  • AES-CBC + manual MAC: don't; AES-GCM is safer
  • RSA PKCS#1 v1.5: avoid; use RSA-PSS
  • Triple DES: deprecated

Refs#

  • Boneh & Shoup textbook
  • OWASP Password Storage Cheat Sheet
  • libsodium docs
  • Latacora - Cryptographic Right Answers

FAQ#

What is the difference between symmetric and asymmetric encryption?#

Symmetric encryption uses one shared secret to encrypt and decrypt (AES, ChaCha20). Asymmetric encryption uses a public/private keypair (RSA, ECDSA, Ed25519) so anyone can encrypt but only the holder can decrypt.

What is the difference between HMAC and a digital signature?#

HMAC uses a shared secret to prove integrity and authenticity between two parties who both know the key. A digital signature uses a private key so anyone with the public key can verify, giving non-repudiation.

Which password hashing algorithm should I use?#

Use argon2id when available, bcrypt as a safe default, or scrypt. Never use plain SHA-256 or MD5 for passwords; they are too fast and let attackers brute force billions of guesses per second.

Should I use AES-GCM or ChaCha20-Poly1305?#

AES-GCM is faster on modern Intel and ARM with hardware acceleration. ChaCha20-Poly1305 is faster on older mobile CPUs without AES instructions. Both are authenticated, secure, and TLS-approved.

RSA vs ECDSA vs Ed25519, which to pick?#

Ed25519 is the modern default: fast, small keys, fewer footguns. ECDSA is widely supported but has nonce pitfalls. RSA is older, slower, and only used for compatibility or where 4096-bit keys are mandated.

Further reading#