Hashing Explained: MD5, SHA-256 & More
Every time you log in to a website, your password is not compared against a stored copy of itself — it is compared against a stored hash of itself. Every time you download software, the checksum printed on the download page is a hash. Every block in Bitcoin’s blockchain is linked to the previous block via a hash. Every Git commit is identified by a hash of its contents. Every TLS certificate carries a hash-based signature. Hashing is one of the most foundational operations in all of computing, yet it is routinely misunderstood, misapplied, or confused with encryption. Developers reach for MD5 when they should use SHA-256, store password hashes without salts, or build authentication systems with plain hashes instead of HMAC. This guide explains exactly what hash functions are, how the major algorithms work internally, how their security was broken over time, and how to use them correctly in real applications.
What is a Hash Function?
A hash function is a mathematical function that takes an input of arbitrary size and produces an output of fixed size. The output is called a hash, digest, or checksum. A cryptographic hash function is a hash function with additional security properties that make it suitable for use in security-sensitive contexts.
The distinction matters. A non-cryptographic hash (like the hash functions used internally by hash tables in programming languages) is designed for speed and even distribution. A cryptographic hash function is designed for security: it must be computationally infeasible to reverse or manipulate in specific ways.
Cryptographic hash functions are defined by five core properties:
Deterministic
The same input always produces the same output. No randomness, no timestamps, no machine state — just the input bytes and the algorithm. Run SHA-256 on "hello" today, run it again in ten years on a different machine, and you will get the same 256-bit result. This is what makes hashes useful for verification: you can reproduce the hash independently and compare.
Fixed-Length Output
Regardless of whether the input is one byte or one terabyte, the output digest is always the same size for a given algorithm. SHA-256 always produces exactly 256 bits (32 bytes). SHA-512 always produces exactly 512 bits (64 bytes). This fixed size is critical for building higher-level constructs like digital signatures, where you need to operate on a predictable-length representation of arbitrarily large data.
One-Way (Pre-Image Resistance)
Given a hash output h, it must be computationally infeasible to find any input m such that hash(m) = h. The hash function is a one-way trapdoor: easy to compute in the forward direction, effectively impossible to reverse.
“Computationally infeasible” is a precise term in cryptography: it means the best-known attack requires so many operations that it cannot be completed in any realistic timeframe, even with all the computing resources on earth. For a 256-bit hash, brute-force pre-image search requires on the order of 2^256 operations — a number larger than the estimated number of atoms in the observable universe.
Collision Resistant
A collision occurs when two different inputs produce the same output: hash(m1) = hash(m2) where m1 ≠ m2. Because hash functions map an infinite input space to a finite output space, collisions must exist in principle (this is a consequence of the pigeonhole principle). But for a secure hash function, finding any collision must be computationally infeasible.
There are two levels of collision resistance:
- Collision resistance: Given no constraints, it is infeasible to find any pair
(m1, m2)wherehash(m1) = hash(m2). - Second pre-image resistance: Given a specific input
m1, it is infeasible to find a different inputm2such thathash(m1) = hash(m2).
Second pre-image resistance is stronger: an attacker has a specific target they must collide with. Both properties must hold for a hash function to be considered secure.
The Avalanche Effect
A secure hash function exhibits the avalanche effect: changing even a single bit of the input produces a completely different output, with approximately half of the output bits flipping. There is no correlation between the magnitude of the input change and the magnitude of the output change — a one-bit change looks just as dramatic as changing the entire input.
This property is what makes hash functions useful for integrity checking. You cannot make a small, undetectable modification to a document and expect the hash to change only slightly. Any modification, no matter how subtle, produces a completely unpredictable new hash.
Example (SHA-256):
Input: "hello"
Hash: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Input: "hellp" (one character changed)
Hash: 4cd9b7672d7fbee8fb51fb1bc139700730dfa1572d87be77b8e09a5d9f38aa41
The two hashes share no obvious relationship, despite the inputs differing by a single character.
How Hash Functions Work
Understanding the internal mechanics of hash functions helps explain their properties and limitations. Most widely used hash functions share a common structural approach, even though their specific internal operations differ.
The Merkle-Damgard Construction
MD5, SHA-1, SHA-256, and SHA-512 are all built on the Merkle-Damgard construction, a framework developed independently by Ralph Merkle and Ivan Damgard in 1989 and published in their respective doctoral theses.
The construction works as follows:
-
Padding: The input message is padded to a multiple of the block size. The padding scheme appends a single
1bit, then enough0bits, then a 64-bit (or 128-bit for SHA-512) encoding of the original message length. This is called length padding or Merkle-Damgard strengthening. -
Initialization: The hash state is initialized to a set of fixed constants called the initialization vector (IV). These constants are built into the algorithm specification.
-
Compression: The padded message is divided into fixed-size blocks. Each block is processed by a compression function that takes the current hash state and the current block as inputs and produces a new hash state. The compression function is the cryptographic core of the algorithm.
-
Finalization: After all blocks are processed, the final hash state is the message digest.
The security of the overall construction reduces to the security of the compression function. If the compression function is collision-resistant, the overall hash is collision-resistant (with one caveat: the Merkle-Damgard construction is vulnerable to length extension attacks, which SHA-3 and the SHA-512/256 truncated variants avoid).
Inside a Compression Round: SHA-256
SHA-256 processes 512-bit (64-byte) blocks and maintains a 256-bit internal state composed of eight 32-bit words: A, B, C, D, E, F, G, H.
Each block goes through 64 rounds of mixing. In each round:
- Two non-linear functions (
ChandMaj) mix bits from the current state words. - Two sigma functions (
Σ0andΣ1) apply combinations of right rotations and XOR operations to introduce diffusion. - A round constant
K[i](derived from the fractional parts of the cube roots of the first 64 prime numbers) is added. - A message schedule word
W[i]derived from the current block is added. - All eight state words are updated.
The use of prime-number-derived constants is significant: it guarantees the constants have no special structure that could be exploited. This is the “nothing up my sleeve” principle of cryptographic design — constants chosen transparently so no one can suspect they were chosen to create a backdoor.
After 64 rounds, the block’s output is added to the state from before the block was processed (this feedback is what creates the chained dependency between blocks). The result is the new state.
Worked Example: Hashing “abc” with SHA-256
Let’s trace through what happens at a high level when you hash the ASCII string "abc" (3 bytes: 0x61 0x62 0x63):
Step 1: Padding
The 3-byte message is padded to 64 bytes (one 512-bit block):
61 62 63 80 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18
The 0x80 byte represents the mandatory 1 bit followed by seven 0 bits. The final 8 bytes (00 00 00 00 00 00 00 18) encode the original message length in bits: 24 bits = 0x18.
Step 2: Initialize state
The eight 32-bit state words are set to the SHA-256 initialization vector — the fractional parts of the square roots of the first eight primes in hexadecimal:
A = 0x6a09e667 E = 0x510e527f
B = 0xbb67ae85 F = 0x9b05688c
C = 0x3c6ef372 G = 0x1f83d9ab
D = 0xa54ff53a H = 0x5be0cd19
Step 3: 64 rounds of compression
The single 64-byte block drives 64 rounds of the mixing function, producing a new state.
Step 4: Final digest
The resulting eight words, concatenated in big-endian order, form the 32-byte digest:
ba7816bf 8f01cfea 414140de 5dae2ec7
3b338ac7 bf1f5866 08b61824 1d6a60c9
Which as a hex string is:
ba7816bf8f01cfea414140de5dae2ec73b338ac7bf1f586608b618241d6a60c9
This is the canonical SHA-256 hash of "abc", defined in the SHA-256 test vectors in FIPS PUB 180-4.
The Avalanche in Action
To see the avalanche effect concretely, compare the binary representations of the SHA-256 hashes of "abc" and "abd":
SHA-256("abc"):
ba7816bf8f01cfea414140de5dae2ec73b338ac7bf1f586608b618241d6a60c9
SHA-256("abd"):
fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603
Counting differing bits between these two 256-bit values reveals that approximately 128 bits — roughly half — differ. A change of one ASCII character (from c to d, a 1-bit difference in the last bit) results in a completely different digest with no discernible pattern.
Common Hash Algorithms Compared
Four hash algorithms dominate practical usage: MD5, SHA-1, SHA-256, and SHA-512. They share the Merkle-Damgard structure but differ in block size, state size, number of rounds, and most importantly, security status.
| Algorithm | Output Size | Block Size | Internal State | Rounds | Speed (relative) | Security Status |
|---|---|---|---|---|---|---|
| MD5 | 128 bits (16 bytes) | 512 bits | 128 bits | 64 | Fastest | Broken — do not use for security |
| SHA-1 | 160 bits (20 bytes) | 512 bits | 160 bits | 80 | Fast | Broken — do not use for security |
| SHA-256 | 256 bits (32 bytes) | 512 bits | 256 bits | 64 | Moderate | Secure — current standard |
| SHA-512 | 512 bits (64 bytes) | 1024 bits | 512 bits | 80 | Moderate-fast on 64-bit | Secure — higher margin |
MD5
MD5 (Message Digest Algorithm 5) was designed by Ron Rivest in 1991 as an improvement over MD4. It produces a 128-bit digest and was the dominant hash function throughout the 1990s and early 2000s. MD5 is defined in RFC 1321.
MD5 processes 512-bit blocks through 64 rounds organized into four 16-round stages, each using a different non-linear function (F, G, H, I). The internal state is four 32-bit words.
Security status: Broken. Practical MD5 collision attacks have been known since 2004. Wang and Yu demonstrated the first collision attack in their landmark paper “How to Break MD5 and Other Hash Functions”. In 2007, Marc Stevens developed chosen-prefix collision attacks, enabling two documents with arbitrary chosen content to be crafted to have the same MD5 hash. In 2012, the Flame malware used an MD5 chosen-prefix collision to forge a Microsoft code-signing certificate, enabling it to spread as a trusted Windows Update.
MD5 is completely unsuitable for any security purpose. However, it remains fast and acceptable for non-security uses like checksums within trusted systems, cache keys, and partition keys where collision resistance is not required.
Example MD5 hash:
MD5("hello") = 5d41402abc4b2a76b9719d911017c592
SHA-1
SHA-1 (Secure Hash Algorithm 1) was designed by the NSA and published by NIST in 1995. It produces a 160-bit digest and replaced MD5 as the standard hash function through the 2000s. SHA-1 is specified in FIPS PUB 180-4.
SHA-1 processes 512-bit blocks through 80 rounds in four 20-round stages. The internal state is five 32-bit words. Its design is closely related to MD5, using similar operations but with a larger state and more rounds.
Security status: Broken. Theoretical weaknesses in SHA-1 were demonstrated by Wang, Yin, and Yu in 2005. In 2017, Google’s Security team and CWI Amsterdam published “SHAttered” — the first publicly demonstrated SHA-1 collision, producing two different PDF files with the same SHA-1 hash. The attack required approximately 9.2 × 10^18 SHA-1 computations but could be performed in a few months on Google’s compute infrastructure. In 2020, a faster chosen-prefix collision attack reduced the practical cost to approximately 900 GPU-years — within reach of a well-funded attacker.
Major browsers stopped accepting SHA-1 TLS certificates in 2017. Git, which uses SHA-1 internally for object addressing, has been migrating to SHA-256 (SHA-256 support landed in Git 2.29, with full transition ongoing). CA/Browser Forum banned SHA-1 in publicly trusted certificates in 2016.
Example SHA-1 hash:
SHA-1("hello") = aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
SHA-256
SHA-256 is part of the SHA-2 family, designed by the NSA and published by NIST in 2001. It produces a 256-bit digest and is currently the dominant general-purpose cryptographic hash function. SHA-256 is specified in FIPS PUB 180-4.
SHA-256 processes 512-bit blocks through 64 rounds. The internal state is eight 32-bit words. It uses six logical functions, bitwise operations, modular addition, and 64 round constants derived from prime numbers.
Security status: Secure. No practical attacks against SHA-256’s collision resistance or pre-image resistance are known. The best known collision attack against SHA-256 is a reduced-round theoretical attack that does not threaten the full 64-round algorithm.
SHA-256 is the workhorse of modern cryptography. It is used in TLS 1.2/1.3 certificate chains, SSH key fingerprints, code signing, Bitcoin’s proof-of-work and transaction hashing, HMAC constructions in JWT HS256 signatures, package manager integrity checks (npm, pip, apt), and much more.
Example SHA-256 hash:
SHA-256("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
SHA-512
SHA-512 is also part of the SHA-2 family, published alongside SHA-256 in 2001. It produces a 512-bit digest and processes 1024-bit blocks through 80 rounds. The internal state is eight 64-bit words.
Security status: Secure. SHA-512 has a larger security margin than SHA-256 (512-bit vs 256-bit output; 80 rounds vs 64) and is faster than SHA-256 on 64-bit processors because it operates on 64-bit words. On 32-bit systems, SHA-512 is slower.
Truncated variants: NIST also specifies SHA-512/256 and SHA-512/224, which compute SHA-512 with different initialization vectors and truncate the output. These have the same performance characteristics as SHA-512 on 64-bit hardware but are not vulnerable to length extension attacks (unlike SHA-256 and SHA-512 in their standard forms).
Example SHA-512 hash:
SHA-512("hello") = 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043
SHA-3
SHA-3 (Keccak) deserves mention even though it is less common in practice. NIST selected SHA-3 in 2012 after a five-year public competition as an alternative to SHA-2. SHA-3 is not based on Merkle-Damgard — it uses a fundamentally different design called the sponge construction, which absorbs input and squeezes out output. This makes it immune to length extension attacks by design.
SHA-3 is specified in FIPS PUB 202. The output sizes mirror SHA-2: SHA3-256, SHA3-384, SHA3-512. SHA-3 is slower than SHA-256 in software on most platforms but has a substantially different design lineage from SHA-2, providing cryptographic diversity. If SHA-2 were ever broken, SHA-3 would remain secure.
Performance Characteristics
Understanding the performance profile of each algorithm matters when hashing large volumes of data, building high-throughput APIs, or selecting an algorithm for embedded systems.
| Algorithm | Throughput (software, 64-bit) | Throughput (with hardware acceleration) | Notes |
|---|---|---|---|
| MD5 | ~500 MB/s | ~4 GB/s (AES-NI assisted) | Fastest in software |
| SHA-1 | ~350 MB/s | ~2 GB/s | Slower than MD5, broken |
| SHA-256 | ~200 MB/s | ~3–8 GB/s (Intel SHA-NI) | Hardware acceleration widely available |
| SHA-512 | ~280 MB/s | ~4 GB/s | Faster than SHA-256 on 64-bit without hardware acceleration |
| SHA3-256 | ~150 MB/s | Varies | No dedicated hardware on most CPUs yet |
| BLAKE2b | ~700 MB/s | ~700 MB/s | Fastest secure algorithm in pure software |
These numbers are approximate and vary significantly by CPU microarchitecture, message size, and implementation quality. Short messages (under 1 KB) behave differently from streaming large files because per-call overhead dominates.
Intel SHA Extensions (SHA-NI, available since Goldmont/Coffee Lake, 2016–2018) provide hardware acceleration for SHA-1 and SHA-256. On supported CPUs, SHA-256 throughput jumps from ~200 MB/s to 3–8 GB/s, making it as fast or faster than MD5 in software. Check support with cpuid or by reading /proc/cpuinfo on Linux for the sha_ni flag.
ARM SHA instructions are available on ARMv8-A and later (Cortex-A53, Apple Silicon, Graviton). SHA-256 on an Apple M-series chip reaches similar hardware-accelerated throughputs.
The practical takeaway: on modern hardware, there is rarely a measurable performance reason to prefer MD5 or SHA-1 over SHA-256 for checksumming. The security difference is dramatic; the performance difference is often negligible or nonexistent.
For applications where hashing performance genuinely matters — content-addressed file systems, deduplication pipelines, high-throughput streaming — benchmark on your specific hardware and message sizes. BLAKE2b is often the fastest secure alternative in software without hardware acceleration.
Common Use Cases
Password Storage with Salting
Storing passwords is the use case where hashing is most consequential and most frequently mishandled. The naive approach — hash the password and store the hash — is insecure for two reasons:
- Rainbow tables: An attacker can precompute hashes for millions of common passwords and look up a stolen hash in seconds.
- Identical passwords produce identical hashes: If two users have the same password, their stored hashes are identical. Cracking one cracks both.
Both problems are solved with a salt: a random value generated per-user and prepended (or appended) to the password before hashing. The salt is stored alongside the hash (it does not need to be secret — its purpose is uniqueness, not secrecy).
stored_value = hash(salt || password)
With per-user random salts, precomputed rainbow tables are useless (the attacker would need to build a separate table for each salt value), and two users with the same password have different stored hashes.
However, even salted SHA-256 is not appropriate for passwords. SHA-256 is designed to be fast — GPUs can compute billions of SHA-256 hashes per second. That speed is the enemy of password security. An attacker with a stolen hash database and a GPU cluster can try billions of password candidates per second.
Password hashing requires algorithms specifically designed to be slow and memory-hard:
- bcrypt: Designed in 1999 by Niels Provos and David Mazières. Uses Blowfish cipher internals with a configurable cost factor. The gold standard for password hashing for decades.
- scrypt: Designed in 2009 by Colin Percival. Memory-hard — requires large amounts of RAM, making GPU and ASIC attacks expensive.
- Argon2: Winner of the Password Hashing Competition in 2015. Three variants: Argon2i (side-channel resistance), Argon2d (GPU resistance), Argon2id (recommended general-purpose variant). Specified in RFC 9106.
The rule is simple: never use MD5, SHA-1, SHA-256, or SHA-512 directly for password storage. Use bcrypt, scrypt, or Argon2.
Data Integrity and Checksums
Hash functions are the standard mechanism for verifying that data has not been modified in transit or storage. The pattern:
- Compute the hash of the original data.
- Transmit or store the hash alongside (or separately from) the data.
- To verify, recompute the hash of the received data and compare to the stored hash.
If the hashes match, the data is intact. If they differ, the data has been modified — either by transmission error, storage corruption, or malicious tampering.
This is how package managers verify downloads (npm’s package-lock.json records SHA-512 hashes for every package), how operating system package repositories authenticate packages (apt uses SHA-256 for .deb packages), and how download pages publish checksums for software distributions.
For integrity checking where adversarial manipulation is a concern, you need a cryptographically secure hash like SHA-256. MD5 and SHA-1 checksums can be forged by a determined attacker who can modify both the file and its accompanying checksum.
Digital Signatures
Asymmetric cryptography (RSA, ECDSA, EdDSA) can sign and verify arbitrary data, but direct signing of large data is impractical — RSA operations are expensive, and RSA key sizes constrain the maximum signable message size. A 2048-bit RSA key can directly sign at most 245 bytes of data with PKCS#1 v1.5 padding.
The solution: sign the hash of the data, not the data itself. A 256-bit SHA-256 digest fits within any practical RSA or EC key size and can be signed in a single operation regardless of how large the underlying document is.
The signing process:
- Compute
digest = SHA-256(message). - Apply the private key operation:
signature = RSA_sign(private_key, digest).
Verification:
- Compute
digest = SHA-256(message). - Apply the public key operation:
expected_digest = RSA_verify(public_key, signature). - Compare
digest == expected_digestusing a constant-time comparison.
This is the mechanism behind TLS certificates, code signing, S/MIME email signing, SSH signatures, and GPG. The hash function used must be collision-resistant: if an attacker can find two documents with the same hash, they can get one document signed and present the signature as valid for the other.
This is precisely why SHA-1 being broken matters for signatures: the SHAttered attack produced two PDFs with the same SHA-1 hash. If a CA signed one PDF’s SHA-1 hash, that signature would verify as valid for the other PDF.
In TLS, the signature algorithm is negotiated during the handshake. TLS 1.3 (RFC 8446) removed all SHA-1 signature schemes from its supported list entirely. TLS 1.2 permits SHA-1 in legacy configurations but all major CAs stopped issuing SHA-1 certificates in 2016. The algorithm identifiers in X.509 certificates are explicit: sha256WithRSAEncryption (OID 1.2.840.113549.1.1.11) or ecdsa-with-SHA256 (OID 1.2.840.10045.4.3.2) specify both the hash and the signature algorithm, making it clear exactly which hash is being used.
Blockchain and Proof of Work
Bitcoin’s blockchain uses SHA-256 pervasively. Each block contains:
- The SHA-256 hash of the previous block’s header (creating the chain).
- A SHA-256 hash of all transactions in the block (as a Merkle tree root).
- A nonce and timestamp.
The block’s own hash must satisfy a difficulty requirement: it must be numerically less than a target value, which in practice means the hash must start with a certain number of zero bits. Miners repeatedly increment the nonce, recompute the double-SHA-256 hash of the block header (SHA-256(SHA-256(header))), and check if it satisfies the target. This is proof of work — the hash function’s unpredictability means the only way to find a valid nonce is brute force, requiring provable computational effort.
Bitcoin uses double-SHA-256 (applying SHA-256 twice) as a defense against length extension attacks and to provide an additional security margin.
Ethereum (pre-merge) used Ethash, a memory-hard algorithm based on Keccak (SHA-3), specifically designed to resist ASIC mining. Post-merge Ethereum uses SHA-256 in its BLS signature scheme and beacon chain operations.
HMAC: Keyed Hash Authentication
A plain cryptographic hash provides integrity but not authentication: anyone can compute a hash. If an attacker intercepts data and its hash, they can modify both and the recipient has no way to detect the tampering.
HMAC (Hash-based Message Authentication Code) solves this by combining a hash function with a secret key. Defined in RFC 2104, HMAC is computed as:
HMAC(K, m) = H((K' XOR opad) || H((K' XOR ipad) || m))
Where H is a hash function, K' is the key padded to the block size, opad is 0x5c repeated to the block size, and ipad is 0x36 repeated. The double nesting prevents length extension attacks on the outer hash.
HMAC is used in:
- JWT tokens with HS256 (HMAC-SHA-256) or HS512 (HMAC-SHA-512): the server signs a token with a secret key, and can verify any presented token without a database lookup.
- HTTPS request signing (AWS Signature Version 4, GitHub webhook verification).
- TLS for record authentication in cipher suites using HMAC.
- TOTP (Time-based One-Time Passwords): Google Authenticator and similar tools compute HMAC-SHA-1 of the current time interval and a shared secret.
File Deduplication
Content-addressed storage systems use hash functions to identify files by their content rather than their name. If two files have the same hash, they are identical — no need to store the data twice.
Git uses this model: every object in a Git repository (blob, tree, commit, tag) is identified by the SHA-1 (currently; SHA-256 in the new format) hash of its content plus a header. If you commit the same file twice, Git stores it once and points both commits at the same object hash. This is not just space optimization — it is the mechanism that makes Git’s integrity guarantees work. When you run git clone, Git verifies every received object against its expected hash. Any corruption or tampering is detected immediately.
Dropbox uses a variant of rolling hash functions (Rabin fingerprinting) to split files into content-defined chunks, then deduplicates chunks globally. Two users uploading the same file share the same stored chunks. This is called content-defined chunking: boundaries between chunks are determined by the file content itself, not fixed offsets, so inserting bytes at the beginning of a file does not invalidate every subsequent chunk.
Content delivery networks use MD5 or SHA hashes as ETags — cache validators that tell a client whether the cached resource matches the current server version, without downloading the full resource again. The server computes a hash of the resource and sends it as the ETag header. The client caches the resource and its ETag. On subsequent requests, the client sends the ETag in an If-None-Match header; the server checks whether the current resource hash matches and returns 304 Not Modified if so, saving the full transfer.
Key Derivation Functions
A related but distinct use of hash functions is key derivation — turning a password, passphrase, or shared secret into a cryptographic key of the required length and strength.
HKDF (HMAC-based Key Derivation Function, RFC 5869) is the standard for deriving keys from existing high-entropy key material. It has two stages: extract (condense the input key material into a pseudorandom key using HMAC) and expand (stretch the key to the desired output length). TLS 1.3 uses HKDF-SHA256 and HKDF-SHA384 to derive all session keys from the shared secret produced by the key exchange.
PBKDF2 (Password-Based Key Derivation Function 2, RFC 8018) is an older key derivation function designed to turn a low-entropy password into a key. It applies a pseudorandom function (typically HMAC-SHA256) repeatedly for a configurable number of iterations, making brute-force attacks more expensive. PBKDF2 is less recommended than Argon2 because it is not memory-hard and can be efficiently parallelized on GPUs.
Code Examples
JavaScript: Web Crypto API and Node.js
The Web Crypto API provides crypto.subtle.digest() for hashing in browsers and modern Node.js (v15+). It is asynchronous and returns an ArrayBuffer.
// Browser and Node.js (v15+): Web Crypto API
async function sha256(message) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
}
async function sha512(message) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-512", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
}
// Usage
const hash = await sha256("hello");
console.log(hash);
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
// Supported algorithms in Web Crypto:
// "SHA-1", "SHA-256", "SHA-384", "SHA-512"
// Note: SHA-1 is available but deprecated for security use
For Node.js with the built-in crypto module (synchronous API, available since Node.js 0.x):
const crypto = require("crypto");
// Synchronous hashing with Node.js crypto module
function hash(algorithm, message) {
return crypto.createHash(algorithm).update(message).digest("hex");
}
console.log(hash("md5", "hello"));
// 5d41402abc4b2a76b9719d911017c592
console.log(hash("sha1", "hello"));
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
console.log(hash("sha256", "hello"));
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
console.log(hash("sha512", "hello"));
// 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca7...
// Hashing binary data (Buffer)
const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04]);
const bufferHash = crypto.createHash("sha256").update(buffer).digest("hex");
// Streaming hash for large files
const fs = require("fs");
const stream = fs.createReadStream("largefile.dat");
const hashStream = crypto.createHash("sha256");
stream.pipe(hashStream);
hashStream.on("finish", () => {
console.log(hashStream.digest("hex"));
});
// HMAC-SHA256
const hmac = crypto.createHmac("sha256", "my-secret-key");
hmac.update("message to authenticate");
console.log(hmac.digest("hex"));
// Produces a 64-character hex string that verifies the message was signed
// with the exact key
// Listing available algorithms
const algorithms = crypto.getHashes();
// Returns: [ 'md4', 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', ... ]
Python: hashlib
Python’s hashlib module provides a unified interface to all common hash functions:
import hashlib
import hmac
# Basic hashing
message = b"hello" # hashlib works with bytes
md5_hash = hashlib.md5(message).hexdigest()
print(md5_hash)
# 5d41402abc4b2a76b9719d911017c592
sha1_hash = hashlib.sha1(message).hexdigest()
print(sha1_hash)
# aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
sha256_hash = hashlib.sha256(message).hexdigest()
print(sha256_hash)
# 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
sha512_hash = hashlib.sha512(message).hexdigest()
print(sha512_hash)
# 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca7...
# Alternatively, use hashlib.new() with algorithm name
h = hashlib.new("sha256")
h.update(b"hello ")
h.update(b"world") # Incremental update — hash is computed over "hello world"
print(h.hexdigest())
# b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576b6e60bdc42d2e173a
# Get raw bytes instead of hex string
raw_bytes = hashlib.sha256(b"hello").digest()
print(raw_bytes) # b'\x2c\xf2\x4d...' (32 bytes)
print(len(raw_bytes)) # 32
# Hashing a file efficiently (avoids loading entire file into memory)
def hash_file(filepath, algorithm="sha256"):
h = hashlib.new(algorithm)
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""):
h.update(chunk)
return h.hexdigest()
checksum = hash_file("large_download.zip")
# HMAC
key = b"my-secret-key"
message = b"message to authenticate"
mac = hmac.new(key, message, hashlib.sha256)
print(mac.hexdigest())
# Constant-time comparison (prevents timing attacks)
expected = "expected_hash_value"
computed = hashlib.sha256(b"data").hexdigest()
if hmac.compare_digest(computed, expected):
print("valid")
# Password hashing with hashlib.scrypt() (Python 3.6+)
# Only use this for understanding — use bcrypt or argon2-cffi in production
import os
salt = os.urandom(16)
dk = hashlib.scrypt(b"password", salt=salt, n=16384, r=8, p=1, dklen=32)
print(dk.hex())
# Available algorithms
print(hashlib.algorithms_available)
# {'sha256', 'sha512', 'md5', 'sha1', 'sha3_256', 'blake2b', ...}
print(hashlib.algorithms_guaranteed)
# {'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3_256', ...}
Go: crypto/sha256 and crypto/sha512
Go’s standard library includes hash implementations in the crypto package with a clean io.Writer interface:
package main
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"io"
"os"
)
func hexHash(h interface{ Sum([]byte) []byte; Write([]byte) (int, error) }, data []byte) string {
h.Write(data)
return hex.EncodeToString(h.(interface{ Sum([]byte) []byte }).Sum(nil))
}
func main() {
message := []byte("hello")
// MD5
md5Hash := md5.Sum(message) // [16]byte
fmt.Printf("MD5: %x\n", md5Hash)
// 5d41402abc4b2a76b9719d911017c592
// SHA-1
sha1Hash := sha1.Sum(message) // [20]byte
fmt.Printf("SHA-1: %x\n", sha1Hash)
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
// SHA-256
sha256Hash := sha256.Sum256(message) // [32]byte
fmt.Printf("SHA-256: %x\n", sha256Hash)
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
// SHA-512
sha512Hash := sha512.Sum512(message) // [64]byte
fmt.Printf("SHA-512: %x\n", sha512Hash)
// Incremental hashing with io.Writer interface
h := sha256.New()
h.Write([]byte("hello "))
h.Write([]byte("world"))
fmt.Printf("SHA-256(hello world): %x\n", h.Sum(nil))
// Hashing a file
f, err := os.Open("largefile.dat")
if err != nil {
panic(err)
}
defer f.Close()
fileHasher := sha256.New()
if _, err := io.Copy(fileHasher, f); err != nil {
panic(err)
}
fmt.Printf("File SHA-256: %x\n", fileHasher.Sum(nil))
// HMAC-SHA256
key := []byte("my-secret-key")
mac := hmac.New(sha256.New, key)
mac.Write([]byte("message to authenticate"))
fmt.Printf("HMAC-SHA256: %x\n", mac.Sum(nil))
// Constant-time HMAC verification
received := []byte("message to authenticate")
receivedMAC, _ := hex.DecodeString("expected_mac_hex_string")
mac2 := hmac.New(sha256.New, key)
mac2.Write(received)
if hmac.Equal(mac2.Sum(nil), receivedMAC) {
fmt.Println("HMAC valid")
}
// SHA-512/256: SHA-512 computation with 256-bit truncated output
// Immune to length extension attacks, faster than SHA-256 on 64-bit hardware
sha512_256Hash := sha512.Sum512_256(message)
fmt.Printf("SHA-512/256: %x\n", sha512_256Hash)
}
Try any of these algorithms instantly using our Hash Generator tool.
Security Best Practices
Never Use MD5 or SHA-1 for Security
MD5 and SHA-1 are broken. For any security-sensitive context — digital signatures, certificate fingerprints, HMAC, password hashing, integrity protection against adversarial modification — use SHA-256 or SHA-512.
The only legitimate uses for MD5 and SHA-1 in 2026 are:
- Legacy system compatibility (reading existing data where migration is not yet complete).
- Non-security checksums within fully trusted systems (verifying a download’s integrity against a trusted checksum source, where the attacker cannot modify the checksum).
- Cache keys, hash table keys, or partition keys where collision resistance is not a security property.
Even for these benign uses, prefer SHA-256 unless the performance difference is measurable and significant. Modern SHA-256 hardware acceleration (Intel SHA Extensions, ARM SHA instructions) makes SHA-256 competitive with MD5 in many scenarios.
Always Salt Passwords
Never hash passwords without a salt. A salt must be:
- Unique per user: The same password for two users should produce different stored hashes.
- Random: A sequential counter is not a salt. Use a cryptographically secure random number generator.
- Long enough: 16 bytes (128 bits) of random data is the recommended minimum.
The salt does not need to be secret. It can be stored in plaintext alongside the hash. Its purpose is to defeat precomputation attacks (rainbow tables) and prevent two users with the same password from having the same hash.
Use Specialized Password Hashing Algorithms
For passwords specifically, use bcrypt, scrypt, or Argon2. These algorithms have configurable cost parameters that allow you to make them slower as hardware improves.
# Python: use argon2-cffi for Argon2 (recommended)
from argon2 import PasswordHasher
ph = PasswordHasher(
time_cost=3, # Number of iterations
memory_cost=65536, # 64 MB of RAM
parallelism=4, # Number of parallel threads
)
# Hashing a password
hashed = ph.hash("user_password_here")
# '$argon2id$v=19$m=65536,t=3,p=4$...'
# Verifying a password
try:
ph.verify(hashed, "user_password_here")
# Raises VerifyMismatchError if wrong
print("password valid")
except Exception:
print("invalid password")
# Check if the hash needs rehashing (parameters changed)
if ph.check_needs_rehash(hashed):
hashed = ph.hash("user_password_here")
// Node.js: use bcrypt
const bcrypt = require("bcrypt");
const saltRounds = 12; // Higher = slower = more secure
// Hashing
const hash = await bcrypt.hash("user_password", saltRounds);
// '$2b$12$...' (60-character bcrypt hash string includes the salt)
// Verifying
const valid = await bcrypt.compare("user_password", hash);
console.log(valid); // true
The bcrypt and Argon2 libraries handle salt generation internally — you do not need to generate or store the salt separately; it is embedded in the output hash string.
Use HMAC for Message Authentication, Not Plain Hashes
A plain hash provides integrity (you can detect modification) but not authentication (anyone can compute it). If you need to verify both that data has not been tampered with AND that it was produced by someone with the secret key, use HMAC.
A common mistake is constructing a naive keyed hash as SHA-256(key || message). This is vulnerable to length extension attacks for Merkle-Damgard hash functions (SHA-256, SHA-512): an attacker who knows SHA-256(key || message) can compute SHA-256(key || message || padding || extension) without knowing the key. HMAC’s double-nested construction prevents this.
Use Constant-Time Comparison for Hash Verification
When verifying a hash, never use a standard string equality comparison. Standard equality returns as soon as it finds a differing byte, which leaks timing information an attacker can exploit to infer the correct hash byte by byte (a timing side-channel attack).
Use a constant-time comparison function:
import hmac
if hmac.compare_digest(computed_hash, expected_hash):
...
// Node.js
const crypto = require("crypto");
if (crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(expected))) {
...
}
import "crypto/subtle"
if subtle.ConstantTimeCompare(computed, expected) == 1 {
...
}
Prefer SHA-256 Over SHA-512 for Most Applications
SHA-512 has a larger security margin and is faster on 64-bit hardware, but SHA-256 is the established standard with broader library support and hardware acceleration. For most applications, SHA-256’s 128-bit security level (collision resistance requires 2^128 operations due to the birthday bound) is more than sufficient.
Use SHA-512 when you specifically need a larger security margin, are on 64-bit hardware and have throughput requirements, or when a protocol specification requires it.
Be Aware of Length Extension Attacks
SHA-256 and SHA-512 (like all Merkle-Damgard constructions) are vulnerable to length extension attacks. Given H(m), an attacker can compute H(m || padding || extension) without knowing m. This breaks naive authentication schemes like H(secret || message).
Defenses:
- Use HMAC instead of raw keyed hashing.
- Use SHA-3, which is immune to length extension by design.
- Use SHA-512/256 or SHA-512/224, which are truncated variants immune to length extension.
- Use the
H(H(key || message))double-hash construction (used by Bitcoin and some protocols), though HMAC is preferable.
Choosing the Right Algorithm for the Job
The most common mistake is using a single algorithm for every purpose. Different contexts have different requirements:
| Use case | Recommended algorithm | Do not use |
|---|---|---|
| Password storage | Argon2id, bcrypt, scrypt | Any raw hash (MD5, SHA-*) |
| Data integrity (checksums) | SHA-256 | MD5, SHA-1 |
| Digital signatures | SHA-256, SHA-384 | MD5, SHA-1 |
| HMAC / message authentication | HMAC-SHA-256, HMAC-SHA-512 | Plain hash, MD5 |
| Key derivation from high-entropy material | HKDF-SHA-256 | MD5, SHA-1 |
| Key derivation from password | PBKDF2-SHA-256, Argon2id | MD5, SHA-1 |
| Non-security checksum / cache key | SHA-256, BLAKE2, MD5 | — (any is fine) |
| Blockchain / proof of work | SHA-256 (Bitcoin), Keccak-256 (Ethereum) | — |
| Git object addressing | SHA-1 (legacy), SHA-256 (new format) | — |
The key insight in this table: password storage is special. It is the only context where the right answer is never any member of the SHA family used directly. For everything else security-sensitive, SHA-256 is a safe default.
Avoid Hash-then-MAC and MAC-then-Hash Constructions
When combining encryption and authentication, order matters. Never construct your own authenticated encryption scheme by naively combining a hash and a cipher. Use an authenticated encryption mode like AES-GCM or ChaCha20-Poly1305, which combine encryption and authentication correctly and atomically. Custom constructions like SHA-256(ciphertext) || ciphertext or encrypt(SHA-256(plaintext) || plaintext) have subtle vulnerabilities that standard authenticated encryption modes do not.
Hash Collisions Explained
A collision is when two different inputs produce the same hash output. Understanding why collisions must exist and how they are found clarifies the security properties of different algorithms.
Why Collisions Must Exist: The Pigeonhole Principle
SHA-256 produces 2^256 possible output values. The set of possible inputs is infinite. Therefore, by the pigeonhole principle, an infinite number of inputs must map to each output. Collisions are not a bug — they are mathematically unavoidable.
The question is not whether collisions exist, but whether they can be found. A hash function is collision-resistant if the best-known algorithm for finding collisions requires approximately 2^(n/2) operations, where n is the output size in bits. This is the theoretical minimum — the birthday bound — because of how collision probability scales with the number of hashes computed.
The Birthday Problem and Birthday Attacks
The birthday problem asks: how many people must be in a room before there is a 50% chance that two of them share a birthday? The answer (23) is surprisingly small because you are looking for any pair, not a specific match.
The same principle applies to hash collisions. To have a 50% chance of finding a collision among a set of randomly chosen hash values, you need only approximately 2^(n/2) values, not 2^n. This is why:
- MD5 (128-bit): 2^64 operations for a collision by birthday attack.
- SHA-1 (160-bit): 2^80 operations for a collision by birthday attack.
- SHA-256 (256-bit): 2^128 operations for a collision by birthday attack.
2^64 is approximately 18 quintillion — large, but within reach of well-funded organizations and now within reach of GPU clusters. 2^128 is approximately 3.4 × 10^38 — orders of magnitude beyond any feasible computation.
The cryptographic algorithms underlying MD5 and SHA-1 have structural weaknesses that allow collisions to be found far faster than the birthday bound suggests — the attacks are called differential cryptanalysis and find specific input differences that produce zero hash differences with high probability.
Real-World Collision Attacks
MD5 Collisions
The first MD5 collision was published by Wang and Yu in 2004, requiring approximately 2^39 computations — achievable in hours on a standard PC of that era.
By 2006, tools like md5coll and fastcoll could generate MD5 collisions in seconds. The Flame malware (2012) used a chosen-prefix collision to forge a valid Microsoft code-signing certificate — a deeply practical attack against real infrastructure.
A chosen-prefix collision allows an attacker to take two arbitrary documents with different prefixes and construct suffixes that make the documents collide. This is far more powerful than finding any two colliding messages: the attacker gets to choose the meaningful content of both messages.
Example: An attacker wants to get a CA to sign a rogue sub-CA certificate. They construct a legitimate-looking certificate signing request and a malicious sub-CA certificate such that they have the same MD5 hash. The CA signs the legitimate-looking request; that signature is valid for the malicious sub-CA certificate.
SHA-1 Collisions: SHAttered
The SHAttered attack (Stevens et al., 2017) produced the first public SHA-1 collision — two PDF files with identical SHA-1 hashes but different visual content. The attack required:
- Approximately 9.2 × 10^18 SHA-1 computations.
- Running on Google’s GPU clusters for approximately two months.
- Roughly 110 GPU years of computation.
The attack exploited differential paths through SHA-1’s compression function — specific bit patterns in the input that cancel out through the hash rounds. The researchers found a path where two messages with a specific relationship in the first block produce the same output state after that block, and then appended identical subsequent data.
The SHAttered PDFs are publicly available at shattered.io as a demonstration. Both have SHA-1 hash 38762cf7f55934b34d179ae6a4c80cadccbb7f0a.
SHA-256 Collisions
No practical collision attack against full SHA-256 is known. The best published attacks target reduced-round variants (31 or fewer rounds of the 64-round full algorithm). The full 64-round SHA-256 remains secure.
Chosen-Prefix vs. Identical-Prefix Collisions
There are two main types of collision attacks, with different practical implications:
Identical-prefix collision: The attacker finds two messages with the same prefix (often empty) that collide. They have little control over what the messages say. Useful for demonstrating that a hash function is broken, but limited practical harm.
Chosen-prefix collision: The attacker can choose arbitrary prefixes for both messages. They can craft two meaningfully different documents — a legitimate contract and a fraudulent one, a legitimate certificate and a rogue one — that collide. This is the attack that breaks real-world systems.
SHA-1 chosen-prefix collisions have been demonstrated in 2020 with approximately 900 GPU-years of computation — still expensive but within reach of national actors and well-funded criminal organizations.
Collision Resistance vs. Pre-Image Resistance: Which Matters More?
For different applications, different resistance properties are the binding constraint:
- Certificate authorities signing documents need collision resistance: if an attacker can produce two documents with the same hash, they can get a legitimate document signed and reuse that signature on the fraudulent document.
- Password verification needs pre-image resistance: an attacker with a stolen hash needs to find any input that produces that hash, not just find two colliding inputs.
- Integrity checking of known files needs second pre-image resistance: an attacker who wants to substitute a malicious file for a known file must find a second input that hashes to the same value as the original.
The birthday bound means collision resistance always breaks down faster than pre-image resistance for a given key size. A 128-bit hash provides 64-bit collision resistance but 128-bit pre-image resistance. This is why a 256-bit hash (SHA-256) is used where 128-bit hashes (MD5) once seemed sufficient — doubling the output size restores the collision resistance margin.
FAQ
Is hashing the same as encryption?
No. Encryption is a two-way process: you encrypt data with a key and can decrypt it with the key (or a related key). Hashing is one-way: you compute a hash from data, but there is no function to reverse the hash back to the original data. Encryption preserves the content; hashing discards it. Use encryption when you need to retrieve the original data. Use hashing when you need to verify data without storing or transmitting it.
Why is MD5 still used if it’s broken?
MD5 remains in use for non-security purposes where its speed is valuable and collision resistance is not required. Cache keys, content-addressed lookup tables, hash partitioning, and non-adversarial checksums are legitimate uses. The problem is that MD5 is also still used for security-sensitive purposes — legacy authentication systems, software download checksums on adversarial networks, old TLS configurations — where it has been broken for two decades. The gap between MD5’s continued use and its broken security status is one of the most persistent technical debt problems in the industry.
What is the difference between SHA-2 and SHA-3?
SHA-2 is a family of algorithms (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/256) designed by the NSA and based on the Merkle-Damgard construction. SHA-3 is a completely different algorithm (based on Keccak, using the sponge construction) designed by Bertoni, Daemen, Peeters, and Van Assche, and selected by NIST in 2012 after a public competition. SHA-2 and SHA-3 are both currently secure. SHA-3 provides cryptographic diversity: if a weakness were found in SHA-2’s construction lineage, SHA-3 would not be affected.
Can I use SHA-256 for password hashing?
Technically yes, but you should not. SHA-256 is designed to be fast — that is its primary characteristic. Fast hashing is bad for passwords: an attacker with a GPU can try billions of SHA-256 hashes per second, making brute-force attacks against stolen hashed passwords practical. Password hashing requires a slow, memory-hard algorithm like Argon2, bcrypt, or scrypt. These algorithms have configurable cost parameters so you can make them appropriately slow as hardware improves. Always use a dedicated password hashing library.
What does “128-bit security” mean for SHA-256?
When cryptographers say SHA-256 provides “128-bit security” for collision resistance, they mean the best known attack requires approximately 2^128 operations, not 2^256. This is because of the birthday bound: finding any collision requires only 2^(n/2) hashes where n is the output size. 2^128 operations is still an astronomically large number — far beyond any feasible computation. For pre-image resistance (finding an input that produces a given output), SHA-256 provides 256-bit security: the best known attack requires 2^256 operations.
How do I verify a file’s SHA-256 checksum on the command line?
Most operating systems include command-line tools for computing hash digests:
# macOS
shasum -a 256 filename.zip
md5 filename.zip
# Linux (coreutils)
sha256sum filename.zip
md5sum filename.zip
# Windows PowerShell
Get-FileHash filename.zip -Algorithm SHA256
Get-FileHash filename.zip -Algorithm MD5
# Verify against a known hash (Linux)
echo "expected_hash filename.zip" | sha256sum --check
What is a rainbow table?
A rainbow table is a precomputed table of (password, hash) pairs that trades storage space for computation time when cracking password hashes. Given a stolen hash database, an attacker looks up each hash in the table to find the corresponding password, requiring no computation at all.
Rainbow tables are defeated by salting: each password’s hash is computed with a unique random salt, so the attacker would need a separate table for each possible salt value. Modern salts (16+ bytes) make this precomputation completely infeasible.
Should I use SHA-256 or BLAKE2?
BLAKE2 (specified in RFC 7693) is a fast, secure hash function designed as a replacement for MD5 and SHA-1. BLAKE2b produces up to 512-bit digests and is faster than SHA-256 and SHA-512 in software on 64-bit platforms. BLAKE2 is secure, well-analyzed, and appropriate for general use.
Choose SHA-256 when: you need maximum ecosystem compatibility, you are implementing a protocol that specifies SHA-256 (TLS, HTTPS, Bitcoin), or you need FIPS compliance (FIPS 140-2/3 does not include BLAKE2).
Choose BLAKE2 when: you control both ends of the system, maximum throughput is a priority, and FIPS compliance is not required. Many modern systems (Zig, WireGuard, Argon2) use BLAKE2 internally.
What is a Merkle tree?
A Merkle tree is a binary tree where each leaf node contains the hash of a data block, and each internal node contains the hash of its two children. The root of the tree — the Merkle root — is a single hash that summarizes all the data blocks.
Merkle trees enable efficient verification of large datasets: to prove that a specific piece of data is included, you need only the hashes along the path from that leaf to the root (a Merkle proof), not the entire dataset. This is used in Bitcoin (transaction inclusion proofs), Git (repository integrity), and distributed file systems (BitTorrent, IPFS).
Where can I hash data interactively?
You can compute MD5, SHA-1, SHA-256, and SHA-512 hashes directly in your browser using our Hash Generator tool. The tool runs entirely client-side — your input never leaves your browser.