Hash functions take arbitrary input and produce a fixed-size digest. They are one-way, deterministic, and collision-resistant. Today covers SHA-2 and SHA-3, HMAC, password hashing, and Merkle trees.
SHA-256 produces a 256-bit digest. Any change to the input produces a completely different output (avalanche effect). SHA-256 powers Bitcoin mining, Git commit IDs, TLS certificates, and software integrity verification. SHA-3 (Keccak) uses a fundamentally different construction (sponge function) and provides an alternative if SHA-2 is ever weakened. MD5 and SHA-1 are broken — never use them for security.
HMAC combines a hash function with a secret key: HMAC(K, M) = H((K XOR opad) || H((K XOR ipad) || M)). This produces a MAC — a value that proves both the message integrity and that the sender knew the key. HMAC-SHA256 is used in JWTs, API request signing (AWS SigV4), TLS record authentication, and cookie signing. Verify HMAC before decrypting to avoid padding oracle attacks.
Never hash passwords with SHA-256 — it is too fast. Attackers with GPUs can test 10 billion SHA-256 hashes/second. Password hashing functions are deliberately slow and memory-hard. bcrypt has been standard since 1999. Argon2 (NIST recommended, PHC winner) is the current best choice. Use a work factor that takes 100-300ms on your hardware. Always use a unique random salt per password.
import hashlib, hmac, os
from argon2 import PasswordHasher
# SHA-256 hash
message = b'Precision AI Academy'
digest = hashlib.sha256(message).hexdigest()
print(f'SHA-256: {digest}')
# HMAC-SHA256 (message authentication)
key = os.urandom(32)
tag = hmac.new(key, message, hashlib.sha256).hexdigest()
print(f'HMAC: {tag}')
# Constant-time comparison (prevents timing attacks)
def verify_hmac(key, message, tag):
expected = hmac.new(key, message, hashlib.sha256).digest()
return hmac.compare_digest(expected, bytes.fromhex(tag))
# Argon2 password hashing
ph = PasswordHasher(time_cost=2, memory_cost=65536, parallelism=2)
hash_val = ph.hash('user_password_here')
print(f'Argon2 hash: {hash_val[:50]}...')
# Verify password
try:
ph.verify(hash_val, 'user_password_here')
print('Password correct')
except:
print('Password incorrect')
Implement a file integrity monitoring system that hashes all files in a directory, saves the hashes to a signed manifest (HMAC-SHA256), and detects any changes when run again.