Hashing & Integrity

Overview

Fingerprinting data: hash functions, MACs, and storing passwords.

Hashing is how cryptography answers the question “is this the same data, and has anyone changed it?”. A cryptographic hash turns any input into a short, fixed-size fingerprint that is practically impossible to forge or reverse. That single idea underpins integrity checks, digital signatures, content-addressed storage, message authentication, and password storage, and each of those uses has its own pitfalls. This group breaks them apart.

The properties that make a hash cryptographic

Any function that maps arbitrary data to a fixed size is a hash. What makes one cryptographic is a set of hardness guarantees that ordinary checksums (like CRC32) do not provide:

PropertyMeaningWhy it matters
Preimage resistanceGiven a hash, you cannot find an input that produces itProtects one-way commitments
Second-preimage resistanceGiven one input, you cannot find a different input with the same hashStops targeted substitution
Collision resistanceYou cannot find any two inputs with the same hashUnderpins signatures and dedupe
Avalanche effectOne flipped input bit changes about half the output bitsNo structure leaks through

A CRC will happily collide on purpose and is trivial to reverse, so it detects accidental corruption but never a deliberate attacker. Cryptographic hashes (SHA-256, SHA-3) are built to resist an adversary who is actively trying to cheat. The Hash Functions page goes deep on these properties and on which algorithms still hold them.

Try a hash and watch the avalanche effect:

mkdir -p ~/crypto-lab && cd ~/crypto-lab
printf 'the quick brown fox' | openssl dgst -sha256
printf 'the quick brown fox.' | openssl dgst -sha256

Expected output:

SHA2-256(stdin)= 05c6bbda2f5b8 decafe... (64 hex chars)
SHA2-256(stdin)= 9e1a7c33b0f21 differsentirely... (64 hex chars)

Adding a single period changes essentially every output bit. That is the avalanche effect, and it is what lets a hash act as a reliable fingerprint.

Proving a message is untampered and from the right sender

A plain hash detects accidental corruption, but it does not prove who sent something: an attacker who alters a message can simply recompute the hash. To bind a message to a secret, you need a Message Authentication Code (MAC). HMAC combines a hash with a shared secret key so that only holders of the key can produce or verify the tag. If two parties share a key, a valid HMAC proves the message is unchanged and came from someone with the key.

When the parties do not share a secret (for example, verifying software from a vendor), you use a digital signature instead, which relies on public-key cryptography. The trade-off between shared-key MACs and public-key signatures is covered in HMAC & MACs.

cd ~/crypto-lab
KEY=$(openssl rand -hex 32)
printf 'transfer approved' | openssl dgst -sha256 -mac HMAC -macopt hexkey:$KEY

Expected output:

HMAC-SHA2-256(stdin)= 7b1e0a...c4 (64 hex chars, depends on the random key)

Why password storage is its own problem

It is tempting to reuse a fast hash like SHA-256 to store passwords, but that is a serious mistake. Fast hashes are fast for the attacker too: a leaked database of SHA-256 password hashes can be cracked at billions of guesses per second on a GPU. Password storage needs the opposite of speed. It needs functions that are deliberately slow and memory-hard, with a unique salt per password, so that guessing is expensive for the attacker while a single legitimate check stays cheap enough. That is the job of bcrypt, scrypt, and Argon2, covered in Password Hashing.

The three sub-pages map to three distinct problems, and mixing them up is a classic source of vulnerabilities:

  • Integrity and fingerprinting: plain cryptographic hashes (Hash Functions).
  • Authenticity with a shared key: MACs and HMAC (HMAC & MACs).
  • Storing secrets that humans choose: slow password hashes (Password Hashing).

Practical Guidance

  1. Use SHA-256, SHA-512, or SHA-3 for integrity and fingerprinting. Never use MD5 or SHA-1 for security purposes (see Hash Functions).
  2. Never use a bare hash for authenticity. To prove a message came from a key holder, use HMAC, not hash(message).
  3. Never store passwords with a plain fast hash. Use Argon2id, scrypt, or bcrypt with a per-password salt and a tuned work factor.
  4. Do not confuse a checksum (CRC) with a cryptographic hash. Only the latter resists a deliberate attacker.
  5. Verify hashes and MACs with constant-time comparison to avoid timing side channels. Libraries expose this; use it.
  6. Match the tool to the problem: fingerprints, authenticity, and password storage are three different jobs with three different primitives.