Cryptographic goals
Separate encoding, hashing, and encryption
Distinguish reversible representation, one-way digests, keyed authentication, and encryption so familiar APIs are not used for the wrong job.
Base64 is not encryption. It changes how bytes are written, and that is all. Anyone can reverse it, no key needed:
const encoded = Buffer.from('secret-key').toString('base64')
Buffer.from(encoded, 'base64').toString() //secret-key
Encoding is a reversible representation. Base64, hex, and URL encoding exist so bytes survive a trip through systems that expect text. That is the whole job.
This mistake happens in real applications. An API key gets stored as Base64 in a column called encrypted_key. Anyone who reads the database can decode it. The column name promised a security property the code never provided.
The four operations, side by side
A cryptographic hash maps data to a fixed-size digest. It is one-way. You cannot recover the input, and you cannot find two inputs with the same digest in any practical time.
Let’s hash a string with SHA-256:
import { createHash } from 'node:crypto'
createHash('sha256').update('hello').digest('hex')
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Run it twice with the same input and you get the same digest. Change one character and the whole digest changes.
A MAC authenticates data with a shared secret. Without the key, an attacker cannot produce a valid tag. This is why a plain hash cannot protect a webhook. An attacker who changes the body can calculate a fresh hash for the new body. Only the keyed version stops that.
Encryption hides plaintext so that only a key holder can get it back. It is the only operation on this list meant to be reversed. In any modern design it should include authentication too, and we’ll see why in the symmetric encryption module.
How to pick
The goal picks the primitive.
- Need to move bytes as text? Encode.
- Need a fingerprint or a change detector? Hash.
- Need to prove a message came from someone holding a shared secret? MAC.
- Need to hide content and read it back later? Encrypt.
If you catch yourself hashing something you will need to read back, stop. If you catch yourself encoding something you need to keep secret, stop. The operation does not match the property.
A quick test I use: ask “what does an attacker need to undo this?” For encoding, nothing. For a hash, a lot of guessing. For a MAC or encryption, the key. If the answer is “nothing” and you needed secrecy, you picked the wrong tool.
Try this on your own: classify Base64, SHA-256, HMAC, password hashing, and authenticated encryption by input, key use, reversibility, and the property each gives you. Run each one on a small value. Then decode or modify every result and write down what the operation does not protect. Those gaps are the point of the exercise.
Lesson completed