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, password hashing, and blockchain technology. They ensure that data hasn't been tampered with by producing a unique fingerprint for any given input.
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