Cryptography Basics: Hashing and Secure Randomness
Use Python's standard-library crypto primitives correctly — and know their limits
hashlib, hmac and secrets. The skill is not writing crypto — it is choosing the right primitive and using it without the classic footguns (using random for security, storing raw hashes as passwords, or comparing secrets with ==).
- distinguish hashing, encryption and encoding and pick the right one,
- compute content hashes with
hashlib, including large files streamed in chunks, - authenticate messages with
hmacand compare secrets in constant time, - generate secure tokens and random values with
secretsinstead ofrandom, - store passwords with a salted, slow key-derivation function (scrypt / PBKDF2),
- explain why you must never invent your own cryptography.
1. Three things people confuse: hashing vs encryption vs encoding
Before touching a single API, fix the mental model. These three operations look similar (bytes in, bytes out) but solve completely different problems.
| Operation | Reversible? | Needs a key? | Purpose |
|---|---|---|---|
| Encoding (base64, hex, UTF-8) | Yes, trivially | No | Represent bytes in another alphabet. Not security. |
| Hashing (SHA-256) | No (one-way) | No | Fixed-size fingerprint of data; integrity, deduplication, lookup. |
| Encryption (AES) | Yes, with the key | Yes | Keep data confidential so only key holders can read it. |
The single most common beginner error is treating encoding as security. Base64 is not encryption — anyone can decode it in one line. And a hash is not encryption either: there is no key and no way to get the original back. A hash answers "are these two things identical?" and "has this data been tampered with?", never "hide this so I can read it later".
cryptography package's AES-GCM / Fernet) is a separate, key-management-heavy topic.