Cryptographic Hash Functions: SHA, MD5, and Applications in Digital Signatures and Integrity

Cryptographic Hash Functions

When I first started digging into cryptography, hash functions were the topic that made everything else click into place. Once I understood how a hash function takes an input of any size and produces a fixed-size fingerprint, concepts like digital signatures, blockchain, and password storage suddenly made a lot more sense. In this article, I want to walk you through everything I know about cryptographic hash functions — from the basic definition all the way to the mathematics behind SHA-256, the failures of MD5, and how these functions power real-world security systems.

What Is a Cryptographic Hash Function?

A cryptographic hash function is a mathematical algorithm that maps data of arbitrary size to a fixed-size output, called a hash value, digest, or checksum. I like to describe it as a one-way blender: you can put anything in — a single character, a whole movie file, a database — and you always get out a fixed-length string, like 256 bits for SHA-256. But you can never run the blender backward and reconstruct the original ingredients.

Formally, a hash function is defined as:

$$H: {0,1}^* \rightarrow {0,1}^n$$

This notation means that H takes an input of any length (denoted by the asterisk) and produces an output of fixed length n bits.

The Core Properties I Look For in a Secure Hash Function

Not every function that shrinks data down is cryptographically secure. When I evaluate a hash function, I check it against these four properties:

  1. Pre-image resistance — given a hash value h, it should be computationally infeasible to find any input m such that H(m) = h.
  2. Second pre-image resistance — given an input m1, it should be infeasible to find a different input m2 such that H(m1) = H(m2).
  3. Collision resistance — it should be infeasible to find any two distinct inputs m1 and m2 such that H(m1) = H(m2).
  4. Avalanche effect — a tiny change in input, even a single bit, should produce a drastically different, unpredictable output.

I find the avalanche effect the easiest to demonstrate. If I hash “hello” and then hash “hollo”, the resulting digests look nothing alike, even though only one letter changed.

Table: Hash Security Properties at a Glance

PropertyWhat It PreventsReal-World Relevance
Pre-image resistanceReversing a hash to find original dataProtects stored password hashes
Second pre-image resistanceSwapping one file for another with the same hashProtects file integrity checks
Collision resistanceTwo different inputs producing the same hashProtects digital signatures
Avalanche effectPredictable output patternsPrevents statistical attacks

The Birthday Paradox and Collision Probability

I can’t talk about collision resistance without explaining the birthday paradox, because it’s the mathematical backbone of why hash output length matters so much. The paradox asks: how many people need to be in a room before there’s a 50% chance two share a birthday? The surprising answer is just 23, not 183.

For a hash function with an n-bit output, the number of hashes I need to compute before finding a collision with reasonable probability is approximately:

$$2^{n/2}$$

This is why a hash function is generally considered to offer “n/2 bits of collision resistance” rather than n bits. For SHA-256, with n = 256, the effective collision resistance is:

$$2^{128}$$

That number is so large that even with all the computing power on Earth running continuously, finding a collision through brute force would take longer than the age of the universe.

How MD5 Works Internally

MD5 (Message Digest Algorithm 5), designed by Ronald Rivest in 1991, was for years the most widely used hash function on the internet. I still see it referenced constantly in older systems, so understanding its internals is useful even though I would never recommend it for security today.

MD5 produces a 128-bit digest and processes data in 512-bit blocks using the Merkle–Damgård construction. Here’s the general internal flow I follow when explaining it:

  1. Padding — the message is padded so its length is congruent to 448 mod 512, then a 64-bit representation of the original message length is appended.
  2. Initialization — four 32-bit registers (A, B, C, D) are set to fixed initial values.
  3. Processing in blocks — each 512-bit block goes through 64 rounds of operations, divided into four rounds of 16 operations each, using nonlinear functions F, G, H, and I.
  4. Output — the final values of A, B, C, D are concatenated to form the 128-bit digest.

The core per-round operation looks like this:

$$A = B + ((A + F(B,C,D) + M_i + K_i) \lll s)$$

Where F is a round-dependent nonlinear function, M_i is a message block, K_i is a constant derived from the sine function, and the lll symbol represents a left bitwise rotation by s bits.

Why MD5 Is Broken

I want to be direct about this: MD5 should never be used for any security-critical purpose today. In 2004, researchers Xiaoyun Wang and colleagues demonstrated practical collision attacks against MD5. By 2008, researchers had used these collisions to forge a rogue Certificate Authority certificate that browsers would trust. Modern hardware can now generate an MD5 collision in seconds.

The core weaknesses I point to when explaining MD5’s failure are:

  • Insufficient output length (128 bits gives only 2^64 effective collision resistance, now within reach of dedicated hardware).
  • Structural weaknesses in its compression function that allow differential cryptanalysis to find collisions far faster than brute force.
  • Its Merkle–Damgård construction makes it vulnerable to length-extension attacks.

How the SHA Family Works

SHA stands for Secure Hash Algorithm, and it’s actually a family of related but distinct algorithms standardized by NIST.

SHA-1

SHA-1 produces a 160-bit digest and was widely used from the mid-1990s through the 2010s. In 2017, Google and CWI Amsterdam demonstrated the “SHAttered” attack, producing two distinct PDF files with identical SHA-1 hashes. Since then, I treat SHA-1 the same way I treat MD5: deprecated and unsuitable for security purposes, though still occasionally seen in legacy version-control and integrity-checking contexts.

SHA-2 (SHA-256 and SHA-512)

SHA-2 is the workhorse of modern cryptography. I use SHA-256 constantly — it appears in TLS certificates, Bitcoin’s proof-of-work, and countless integrity checks. Here’s how it processes data internally:

  1. Padding and parsing — the message is padded to a multiple of 512 bits (for SHA-256) and split into 512-bit blocks.
  2. Message schedule expansion — each 512-bit block is expanded into sixty-four 32-bit words using bitwise rotations and XOR operations.
  3. Compression function — eight working variables (a through h) are initialized from constants derived from the fractional parts of the square roots of the first eight primes. Each block goes through 64 rounds of mixing using logical functions Ch, Maj, and two sigma functions.
  4. Output — the eight 32-bit words are concatenated into the final 256-bit digest.

The compression round can be expressed as:

$$T_1 = h + \Sigma_1(e) + Ch(e,f,g) + K_t + W_t$$

$$T_2 = \Sigma_0(a) + Maj(a,b,c)$$

Where Ch and Maj are defined as:

$$Ch(e,f,g) = (e \land f) \oplus (\lnot e \land g)$$

$$Maj(a,b,c) = (a \land b) \oplus (a \land c) \oplus (b \land c)$$

SHA-3

SHA-3, standardized by NIST in 2015 (FIPS 202), is structurally different from SHA-1 and SHA-2. Instead of the Merkle–Damgård construction, it uses the Keccak sponge construction, which absorbs input data into a large internal state and then squeezes out the digest. I find this design elegant because it eliminates the length-extension vulnerability that affects SHA-2.

The sponge construction can be described in two phases:

$$\text{Absorb: } S_{i+1} = f(S_i \oplus P_i)$$

$$\text{Squeeze: } Z = \text{truncate}(S_{final})$$

Where f is the Keccak-f permutation function applied to the internal state S, and P_i represents the padded input blocks.

Table: Comparing Common Hash Algorithms

AlgorithmOutput SizeStatusCommon Use Today
MD5128 bitsBrokenNon-security checksums only
SHA-1160 bitsBrokenLegacy systems, deprecated
SHA-256256 bitsSecureTLS, digital signatures, blockchain
SHA-512512 bitsSecureHigh-security applications
SHA-3-256256 bitsSecureNext-gen protocols, IoT
BLAKE2Up to 512 bitsSecureFast hashing, password systems

Digital Signatures and How Hashing Fits In

I always explain digital signatures as a two-step dance between hashing and asymmetric encryption. Signing an entire large document with a private key directly would be slow and impractical, so instead:

  1. The sender hashes the document using SHA-256, producing a fixed-size digest.
  2. The sender encrypts that digest with their private key, creating the digital signature.
  3. The recipient hashes the received document independently and decrypts the signature using the sender’s public key.
  4. If the two digests match, the recipient knows the document hasn’t been altered and genuinely came from the sender.

This process relies entirely on collision resistance. If an attacker could find two documents with the same hash, they could get a legitimate signature on a harmless document and then claim it applies to a malicious one.

Integrity Verification in Practice

Beyond signatures, I use hash functions constantly for straightforward integrity checks. When I download a Linux ISO, the distribution’s website usually publishes a SHA-256 checksum next to the download link. After downloading, I run:

sha256sum ubuntu-22.04.iso

If the output matches the published hash, I know the file wasn’t corrupted or tampered with during transfer. This same principle underlies Git’s commit hashing, package managers verifying downloaded dependencies, and forensic investigators proving evidence hasn’t been altered.

Password Storage: Why Plain Hashing Isn’t Enough

A mistake I see constantly is developers hashing passwords with a single pass of SHA-256 and calling it secure. The problem is that SHA-256 is designed to be fast, and speed is the enemy of password security — it means attackers can try billions of guesses per second using GPUs.

This is why I recommend purpose-built password hashing functions instead:

  • bcrypt — incorporates a configurable work factor and built-in salting.
  • scrypt — adds memory-hardness to resist GPU and ASIC attacks.
  • Argon2 — the winner of the Password Hashing Competition, tunable for both memory and time cost.

The formula for a basic salted hash looks like this:

$$H(password | salt) = digest$$

The salt ensures that even identical passwords produce different hashes, defeating precomputed rainbow table attacks.

Attacks Against Hash Functions

When I assess the security of a hash function, I think about several categories of attacks:

  • Brute-force pre-image attacks — trying every possible input until a match is found; infeasible against SHA-256 due to the sheer size of the search space.
  • Collision attacks — exploiting mathematical weaknesses to find two inputs with the same hash faster than the birthday bound predicts, as happened with MD5 and SHA-1.
  • Length-extension attacks — appending data to a message and computing a valid hash without knowing the original input, possible against Merkle–Damgård-based hashes like MD5, SHA-1, and SHA-2 when used naively (mitigated by SHA-3’s sponge construction or by using HMAC).
  • Rainbow table attacks — using precomputed hash tables to reverse unsalted password hashes quickly.

Best Practices I Follow

  • Always use SHA-256 or stronger for any integrity or signature-related purpose.
  • Never use MD5 or SHA-1 for anything security-critical, even though they remain fine for non-adversarial checksums like detecting accidental file corruption.
  • Use HMAC-SHA256 rather than raw hashing when authenticating messages with a shared secret.
  • Use Argon2 or bcrypt for password storage, never a raw hash function.
  • Always salt hashes when storing any sensitive value that could be guessed from a limited set of possibilities.

Performance Considerations

SHA-256 processes data at roughly several hundred megabytes per second on modern CPUs, and hardware acceleration (Intel SHA extensions) pushes this even higher. SHA-3, while more resistant to certain theoretical attacks, tends to be somewhat slower in pure software implementations than SHA-2, though hardware implementations close this gap significantly. I choose SHA-3 when I specifically want the sponge construction’s resistance to length-extension attacks, and SHA-256 for everything else due to its ubiquity and hardware support.

Common Mistakes I See

  • Treating a hash as encryption — hashing is one-way and cannot be “decrypted.”
  • Using a fast, general-purpose hash for passwords instead of a dedicated password-hashing algorithm.
  • Forgetting to salt password hashes.
  • Assuming a hash algorithm is secure forever — cryptanalysis techniques improve over time, which is exactly what happened to MD5 and SHA-1.
  • Relying on truncated hash outputs without recalculating the effective security level.

Frequently Asked Questions

Is MD5 completely useless today? Not entirely — it’s still fine for non-security purposes like detecting accidental data corruption or generating cache keys. But for anything involving an adversary, it should never be used.

Can a hash function be reversed? No. A properly designed cryptographic hash function is a one-way function. The only way to “reverse” it is to guess inputs and check whether they produce the matching hash, which is what brute-force and dictionary attacks do.

What’s the difference between hashing and encryption? Encryption is reversible with the correct key; hashing is one-way by design and never intended to be reversed.

Why do SHA-256 and SHA-512 use different word sizes? SHA-256 operates on 32-bit words while SHA-512 operates on 64-bit words, giving SHA-512 better performance on 64-bit hardware and a larger security margin at the cost of more computation.

Is SHA-3 better than SHA-2? SHA-3 isn’t necessarily “better” in raw security terms — SHA-256 remains secure — but SHA-3’s sponge construction offers structural advantages, like resistance to length-extension attacks, and serves as a hedge in case an unforeseen weakness is ever found in SHA-2’s design.

Summary

Cryptographic hash functions are the quiet workhorses behind digital signatures, password storage, blockchain, and everyday file integrity checks. I’ve learned that the strength of a hash function comes down to four properties: pre-image resistance, second pre-image resistance, collision resistance, and the avalanche effect. MD5 and SHA-1, once industry standards, have both been broken by advances in cryptanalysis and should be retired from any security context. SHA-2 (particularly SHA-256) remains the practical standard today, while SHA-3 offers a structurally different, forward-looking alternative. Whatever the specific algorithm, the underlying mathematics — especially the birthday paradox — determines just how much security a given output length actually provides.

References

  • NIST FIPS 180-4, Secure Hash Standard (SHS)
  • NIST FIPS 202, SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions
  • RFC 1321, The MD5 Message-Digest Algorithm
  • RFC 6234, US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)
  • Wang, X., Yin, Y. L., & Yu, H. (2005). Finding Collisions in the Full SHA-1. CRYPTO 2005.
  • Stevens, M., Bursztein, E., Karpman, P., Albertini, A., & Markov, Y. (2017). The First Collision for Full SHA-1. Google/CWI Amsterdam.
  • NIST Special Publication 800-63B, Digital Identity Guidelines: Authentication and Lifecycle Management
Total
0
Shares

Leave a Reply

Previous Post
Symmetric Key Encryption

Symmetric Key Encryption: Algorithms, AES, DES, and Secure Key Management Explained

Next Post
Types of Keys in Cryptography

Types of Keys in Cryptography: Symmetric, Asymmetric, Session, and Master Keys Explained

Related Posts