SHA (Secure Hash Algorithm)
Overview
SHA (Secure Hash Algorithm) is a family of cryptographic hash functions published by NIST. SHA-0, SHA-1 and the SHA-2 family were designed by the National Security Agency (NSA). SHA-3 was not: it is the Keccak algorithm, designed by Bertoni, Daemen, Peeters and Van Assche, selected through an open public competition in 2012 and standardized as FIPS 202 in 2015. Hash functions are one-way functions that take input data of any size and produce a fixed-size hash value (digest).
SHA algorithms are used for data integrity verification, digital signatures, and blockchain technology. They produce a unique fingerprint for any given input, changing even one bit of the input produces a completely different hash, and finding two different inputs with the same hash is (for a secure hash) computationally infeasible.
The SHA family is the most consequential piece of cryptographic infrastructure on the modern internet. SHA-256 alone verifies the integrity of the Bitcoin blockchain, signs every TLS certificate, protects the contents of git commits and Docker images, and functions as the underlying primitive in HMAC, HKDF, and hundreds of authentication protocols. If you use a computer with an internet connection in 2026, SHA-2 is running somewhere in almost every interaction you have.
History: SHA-0, SHA-1, SHA-2, SHA-3
The SHA family exists because MD5's design was seen, correctly, as a stopgap that would eventually fall to cryptanalysis. NSA and NIST designed SHA (now called SHA-0) in 1993 as a stronger alternative. Within two years the NSA quietly withdrew SHA-0 and replaced it with SHA-1, which differed only in a single one-bit rotation in the message-schedule expansion. The reason for the change was not publicly explained at the time, though outside cryptographers later showed that SHA-0 was much weaker than SHA-1 for reasons the NSA presumably knew but did not disclose. This was one of the earliest publicly-visible hints that the NSA's cryptanalytic capabilities were ahead of academic knowledge.
SHA-1 was standardised in FIPS 180-1 in 1995 and became the workhorse hash function for a decade. It was used for TLS certificates, code signing, git object hashes, and countless other applications. Cryptanalytic pressure on SHA-1 built through the early 2000s: Wang, Yin and Yu published a theoretical attack in 2005 finding collisions in 263 operations. This was well below the 280 generic bound but still expensive enough that no full collision was publicly produced for another twelve years.
That collision arrived in February 2017 as SHAttered, produced by Google and CWI Amsterdam. The team constructed two PDF files with different visible contents but identical SHA-1 hashes, at a computational cost equivalent to 6,500 CPU-years and 100 GPU-years. Immediately after SHAttered, browsers and CAs accelerated their already-planned migration away from SHA-1 certificates. In 2020 Leurent and Peyrin published a chosen-prefix collision at around 263.4, which was the signature-forgery-enabling attack that had been feared, and reduced its cost to around $45,000 on cloud compute. NIST has set 31 December 2030 as the date SHA-1 is fully retired from federal use, though most industries have long since moved on.
SHA-2 was designed by NSA and standardised in 2001 (FIPS 180-2) as a family of six functions (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256). Structurally SHA-2 is not radically different from SHA-1, both are Merkle–Damgård constructions with similar compression functions, but SHA-2 uses larger internal state, more rounds, better message expansion, and different constants. No practical attack on any full-round SHA-2 variant has ever been demonstrated, and none is expected.
SHA-3 is a deliberately different beast. After the Wang attacks demonstrated that Merkle–Damgård hashes could all potentially be at risk if the cryptanalysis techniques generalised, NIST held a public competition (2007–2012) to standardise a hash function using a fundamentally different structure. Sixty-four candidates were submitted; the winner, Keccak, was designed by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche, Daemen and Van Assche being two of the four cryptographers whose Rijndael design became AES. Keccak uses a sponge construction rather than Merkle–Damgård, which is a completely different mathematical structure with different security properties, notably immunity to length extension. Keccak became SHA-3 in FIPS 202 in 2015.
SHA-3 is not intended to displace SHA-2. Both are believed secure, and SHA-2 is much more widely deployed and typically faster on general-purpose CPUs. SHA-3 exists as insurance: if some catastrophic structural weakness were ever found in the Merkle–Damgård construction that affected SHA-2, SHA-3's sponge design would almost certainly not be vulnerable, giving the world a fallback that does not require a five-year competition to standardise from scratch.
How SHA-2 Actually Works
Understanding SHA-2 at the level of its structure is useful even if you never implement it. All SHA-2 variants share the same overall architecture, differing only in word size, initial hash values, number of rounds, and internal constants.
SHA-256 processes input in 512-bit blocks. Its internal state is eight 32-bit words (a, b, c, d, e, f, g, h), initialised to specific constants derived from the fractional parts of the square roots of the first eight primes, a construction chosen to demonstrate that the initial values were not covertly chosen to introduce a backdoor. This "nothing up my sleeve" convention is standard in modern cipher and hash design and the specific numbers used by SHA-2 are one of the clearest examples.
Each 512-bit block is processed through 64 rounds of the compression function. Each round takes the current 8-word state and one 32-bit word from the expanded message schedule, mixes them with a round constant and a set of Boolean and rotation operations designed for both non-linearity and rapid diffusion, and updates the state. After processing all 64 rounds, the resulting state is added (word by word, modulo 232) to the state as it was before the block, producing the input state for the next block. When all blocks are processed, the final 8-word state is concatenated to form the 256-bit hash output.
The Merkle–Damgård construction that SHA-2 uses has an important structural
property: if you have H(m) and know the length of m, you can
compute H(m || padding || m') for any suffix m' of your
choice, without knowing m. This is called a length-extension attack
and it is why you must never authenticate a message by hashing secret || message.
Use HMAC (which internally uses two hash calls in a specific way that defeats length
extension), or use SHA-3 or SHA-512/256 (which do not have this vulnerability).
A subtle but useful practical point: SHA-512 is often faster than SHA-256 on 64-bit hardware. SHA-512 works on 64-bit words natively; SHA-256 works on 32-bit words, wasting half the width of every register on a 64-bit CPU. If you want a 256-bit output but do not need length-extension resistance for interoperability reasons, SHA-512/256. SHA-512 truncated to 256 bits, is faster than SHA-256 and immune to length extension as a bonus.
SHA Variants
| Variant | Output Size | Block Size | Status |
|---|---|---|---|
| SHA-1 | 160 bits | 512 bits | Deprecated |
| SHA-256 | 256 bits | 512 bits | Secure |
| SHA-224 | 224 bits | 512 bits | Secure |
| SHA-384 | 384 bits | 1024 bits | Secure |
| SHA-512 | 512 bits | 1024 bits | Secure |
| SHA-512/256 | 256 bits | 1024 bits | Secure (resists length extension) |
| SHA3-256 | 256 bits | 1088 bits (rate) | Secure (sponge, not Merkle-Damgård) |
| SHA3-512 | 512 bits | 576 bits (rate) | Secure (sponge, not Merkle-Damgård) |
| SHAKE128 / SHAKE256 | Extendable (any length) | 1344 / 1088 bits (rate) | Secure (extendable-output function) |
SHA-1 and SHA-2 are built on the Merkle-Damgård construction. SHA-3 uses a sponge construction instead, a genuinely different design, chosen deliberately so that a future break of SHA-2 would not also break its replacement. SHA-3 is not "more secure" than SHA-2 in any measurable sense today; it is insurance.
How It Works
- Padding: Add padding to make input length a multiple of block size
- Message Schedule: Break message into blocks
- Compression Function: Process each block through compression rounds
- Final Hash: Combine all block hashes into final digest
Implementation
import hashlib
def sha256_hash(data):
"""Compute SHA-256 hash"""
return hashlib.sha256(data.encode()).hexdigest()
def sha512_hash(data):
"""Compute SHA-512 hash"""
return hashlib.sha512(data.encode()).hexdigest()
# Example usage
message = "Hello, SHA!"
hash_256 = sha256_hash(message)
hash_512 = sha512_hash(message)
print(f"SHA-256: {hash_256}")
print(f"SHA-512: {hash_512}")
# Verification
def verify_integrity(original, received, hash_value):
"""Verify data integrity"""
computed_hash = sha256_hash(received)
return computed_hash == hash_value
Hash Function Properties
- Deterministic: Same input always produces same output
- Fast Computation: Hash can be computed quickly
- Pre-image Resistance: Hard to find input given hash
- Collision Resistance: Hard to find two inputs with same hash
- Avalanche Effect: Small input change causes large hash change
Applications
- Data Integrity: Verify files haven't been modified
- Digital Signatures: Sign documents and messages
- Key Derivation: As the underlying hash inside HMAC, HKDF and PBKDF2
- Blockchain: Bitcoin and other cryptocurrencies
- Version Control: Git historically used SHA-1 for object hashes. Since 2017 it has used SHA-1DC (SHA-1 with collision detection, which rejects SHAttered-style colliding inputs), and Git has supported a full SHA-256 object format since version 2.29 (2020)
Security Considerations
- SHA-1: Broken and retired. A real collision was produced in 2017 (SHAttered), and Leurent & Peyrin demonstrated a practical chosen-prefix collision in 2020, the class of attack that lets an attacker forge certificates. NIST has set 31 December 2030 as the date SHA-1 is fully phased out of federal use.
- SHA-256: Secure, and the right default for most applications.
- SHA-512: Equally secure. Note it is often faster than SHA-256 on 64-bit hardware, so "use SHA-512 when you need more security" is the wrong reason to pick it, pick it for speed on 64-bit platforms, or pick SHA-512/256 when you also want length-extension resistance.
- Length extension: SHA-1 and SHA-2 are Merkle-Damgård constructions, so given
H(secret || m)and the length ofsecret, an attacker can computeH(secret || m || padding || m')without knowing the secret. Never authenticate a message by hashing a secret prefix. Use HMAC, or a hash that is not vulnerable (SHA-3, SHA-512/256, BLAKE2/BLAKE3).
Do not use SHA for password storage
This is the most common way general-purpose hash functions get misused. SHA-256 is designed to be fast, that is the entire point of a hash function, and commodity GPUs evaluate it billions of times per second. Adding a salt defeats precomputed rainbow tables, but it does nothing at all about throughput: an attacker who steals your database simply brute-forces each salted hash individually, very quickly.
Password storage needs a deliberately slow, memory-hard key derivation function, with tunable cost parameters you increase as hardware gets faster:
- Argon2id (RFC 9106), the current first choice, winner of the Password Hashing Competition
- scrypt, memory-hard, widely available
- bcrypt, older but still acceptable; note its 72-byte input limit
- PBKDF2-HMAC-SHA256, the weakest of the four (not memory-hard), but the option to reach for when FIPS compliance requires it. Use a high iteration count.
# WRONG - fast hash, brute-forces at billions of guesses/sec even with a salt
import hashlib
stored = hashlib.sha256(salt + password.encode()).hexdigest()
# RIGHT - slow, memory-hard, tunable cost
from argon2 import PasswordHasher # pip install argon2-cffi
ph = PasswordHasher()
stored = ph.hash(password) # salt is generated and embedded automatically
ph.verify(stored, password_attempt) # raises on mismatch
# Standard-library fallback if you cannot add a dependency
stored = hashlib.scrypt(password.encode(), salt=salt, n=2**15, r=8, p=1)
Related Algorithms
Explore other encryption algorithms:
- MD5 - Legacy hash function
- RSA - Used with SHA for digital signatures
- AES - Symmetric encryption
- Back to Encryption Algorithms Overview
☕ Buy me a coffee — $3