3 Hashing, Password Storage, and Message Authentication
Learn how cryptographic hashes support integrity checks, how salts and costly key-derivation functions protect stored passwords, and how MACs authenticate messages without encrypting them.
Cryptographic hashes and their limits
A takes an input of any practical length and produces a fixed-length output called a hash or . For example, SHA-256 produces a 256-bit . A small change to the input should produce a very different .
A secure hash function is designed to make three kinds of attacks computationally infeasible:
A starts with a and tries to find an input that produces it.
A starts with an input and tries to find a different input with the same .
A tries to find any two different inputs that produce the same .
Hashes can help detect accidental or unauthorized changes when the expected is obtained through a trusted channel. A plain hash does not prove who created a message: an attacker who can replace both a file and its can calculate a new . A hash is also not encryption and is not intended to be reversed to recover the input.
Protecting stored passwords
Passwords are usually easier to guess than randomly generated cryptographic keys. General-purpose hashes such as SHA-256 are deliberately fast, so an attacker with a stolen password database can try guesses rapidly. Password storage therefore uses a , or , that is intentionally expensive to compute. Common choices include Argon2id, scrypt, bcrypt, and PBKDF2; the suitable function and parameters depend on the application's requirements.
A is a unique, randomly generated value used with a password when deriving its stored verifier. It is not secret and is stored alongside the result. Unique salts make identical passwords produce different stored results and prevent attackers from reusing one precomputed lookup table across many accounts. A does not make a weak password strong or stop guesses against a particular account; the KDF's cost makes each guess more expensive.
Storing and checking a verifier
Generate a fresh random for the account.
Derive a verifier using the password, , and chosen KDF parameters.
Store the algorithm and parameters, , and verifier.
At login, derive a new verifier from the submitted password using the stored and parameters, then compare the results securely.
The same password stored for two accounts should yield different verifiers because each account has its own . Systems may also use a , an additional secret kept separately from the password database, as defense in depth. A must remain secret, and it does not replace a unique or a suitable KDF.
Authenticating messages with MACs
A is a tag calculated from a message and a secret key shared by the sender and receiver. The receiver calculates the expected tag with the same key and checks it against the received tag. A valid tag provides evidence that the message was not altered and that it was created by someone who knows the key.
is a widely used MAC construction built from a . It is not simply a hash of the key concatenated with the message; applies the hash in a defined keyed construction.
For example, Alice and Bob share a secret key. Alice sends a message with its tag. Bob uses the shared key to calculate a tag for the received message; if the tags do not match, he rejects the message. Implementations should use established cryptographic libraries and a constant-time tag comparison where available.
A MAC differs from a plain hash because verifying it requires the secret key. Because both parties share that key, a MAC does not by itself prove to an outside observer which party created a message. A MAC also does not encrypt the message: anyone who can read it can still see its contents.