Cryptographic hashing and integrity

Compute SHA-256 digests and see why a one-character change produces a completely different hash.

Python
import hashlib

print(hashlib.sha256(b"abc").hexdigest())
first = hashlib.sha256(b"transfer 100 to alice").hexdigest()
second = hashlib.sha256(b"transfer 900 to alice").hexdigest()
differing = sum(x != y for x, y in zip(first, second))
print(len(first), first == second)
print(differing > 40)
Output
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
64 False
True

Explanation

A cryptographic hash function maps data of any length to a fixed-size digest. SHA-256, part of the SHA-2 family, always produces 256 bits, shown as 64 hexadecimal characters. A good hash has three properties: preimage resistance (given a digest, you cannot find an input that produces it), second-preimage resistance (given an input, you cannot find a different one with the same digest), and collision resistance (you cannot find any two inputs with the same digest).

Hashes also show the avalanche effect: changing a single character of the input changes about half of the output bits, so the digests look unrelated. That makes a hash useful for detecting accidental corruption and for verifying a download against a published digest. Hashing is not encryption: it has no key and cannot be reversed to recover the data.

Choose the algorithm carefully. MD5 and SHA-1 have practical collision attacks and must not be used where security matters. SHA-256 and SHA-3 are current choices. A plain hash proves integrity only against accidental changes: an attacker who can modify a message can simply recompute the hash. Protection against tampering needs a keyed construction, which the HMAC lesson covers.

Explore the dry run
Guided walkthroughReady
1import hashlib
2
3print(hashlib.sha256(b"abc").hexdigest())
4first = hashlib.sha256(b"transfer 100 to alice").hexdigest()
5second = hashlib.sha256(b"transfer 900 to alice").hexdigest()
6differing = sum(x != y for x, y in zip(first, second))
7print(len(first), first == second)
8print(differing > 40)

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
The SHA-256 digest of 'abc' is the standard test vector published by NIST.ba7816bf…15ad
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Hash one message.ba7816bf…15ad
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Changing one digit of the input is enough to produce a completely different digest.ba7816bf…15ad
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Every SHA-256 digest is 64 hex characters, and these two are different.ba7816bf…15ad
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
64 False
Avalanche effect: far more than half of the 64 hex digits differ.ba7816bf…15ad
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
64 False
True

Cost and common mistakes

Hashing is O(n) time in the input length and O(1) memory when the data is processed in blocks.

  • A hash is not encryption. It cannot be reversed, but short or guessable inputs can be found by trying candidates.
  • Do not use MD5 or SHA-1 for security purposes. Practical collision attacks exist for both.
  • A plain hash does not authenticate a message. Anyone who can change the data can recompute the hash.

Try it yourself

Why can't you recover the original message from its SHA-256 digest?

Reveal answer and reasoning

The digest is only 256 bits, so many different inputs share the same digest in principle. The function is designed so that finding any input for a given digest is computationally infeasible.

Further reading