MD5 Algorithm Explained Step by Step, With a Worked Example

MD5 turns any input into 128 bits through four simple steps: pad the message, set up a four-word state, mix every 512-bit block into that state with 64 operations, and print the state. Each operation uses only addition, bitwise logic and rotation of 32-bit words. This article goes through each step with the real numbers for the input `abc`, using a short Python implementation that we checked against `hashlib` on several inputs. It ends with the reason why this neat design is no longer secure.

Overview: what MD5 does

MD5 was published by Ron Rivest in 1992 as RFC 1321. It is a Merkle–Damgård hash: the message is cut into 512-bit (64-byte) blocks, and a compression function folds each block into a 128-bit running state. After the last block, the state is the digest. All arithmetic is on 32-bit unsigned words and wraps modulo 2³²; words are read and written in little-endian byte order, which matters when you check results by hand.

The whole algorithm fits on a page, so here it is first. The sections below explain each part with the abc example:

import math, struct

S = [7, 12, 17, 22] * 4 + [5, 9, 14, 20] * 4 + [4, 11, 16, 23] * 4 + [6, 10, 15, 21] * 4
K = [int(abs(math.sin(i + 1)) * 2**32) & 0xFFFFFFFF for i in range(64)]

def rotl(x, c):
    return ((x << c) | (x >> (32 - c))) & 0xFFFFFFFF

def md5(message: bytes) -> str:
    bit_len = (8 * len(message)) & 0xFFFFFFFFFFFFFFFF
    message += b"\x80"
    message += b"\x00" * ((56 - len(message) % 64) % 64)
    message += struct.pack("<Q", bit_len)

    a0, b0, c0, d0 = 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476

    for off in range(0, len(message), 64):
        M = struct.unpack("<16I", message[off:off + 64])
        A, B, C, D = a0, b0, c0, d0
        for i in range(64):
            if i < 16:   F, g = (B & C) | (~B & D), i
            elif i < 32: F, g = (D & B) | (~D & C), (5 * i + 1) % 16
            elif i < 48: F, g = B ^ C ^ D, (3 * i + 5) % 16
            else:        F, g = C ^ (B | ~D), (7 * i) % 16
            F = (F + A + K[i] + M[g]) & 0xFFFFFFFF
            A, D, C, B = D, C, B, (B + rotl(F, S[i])) & 0xFFFFFFFF
        a0, b0, c0, d0 = [(x + y) & 0xFFFFFFFF for x, y in zip((a0, b0, c0, d0), (A, B, C, D))]

    return struct.pack("<4I", a0, b0, c0, d0).hex()

md5(b"abc")   # '900150983cd24fb0d6963f7d28e17f72'  — same as hashlib.md5(b"abc").hexdigest()

Step 1: padding the message

The input must become a whole number of 512-bit blocks, and the padding must be unambiguous, so that two different messages never pad to the same bytes. MD5 appends one 0x80 byte (a single 1 bit followed by seven 0 bits), then zero bytes until the length is 8 bytes short of a multiple of 64, and finally the original length in bits as a 64-bit little-endian integer.

For abc (3 bytes, 24 bits) that gives exactly one 64-byte block:

61 62 63 80 00 00 … 00 18 00 00 00 00 00 00 00
a  b  c  │  └ 52 zero bytes ┘ └ 24 bits, little-endian ┘
         └ the single 1 bit

The block is then read as sixteen 32-bit little-endian words M[0]…M[15]. M[0] is 0x80636261, the bytes 61 62 63 80 reversed, and M[14] is 0x18 = 24. Because the length field always needs 8 bytes, a 56-byte message does not fit in one block: 55 bytes pad to 64, but 56 bytes pad to 128.

Step 2: the initial state and the constants

The state is four 32-bit words with fixed starting values. Written as bytes in memory they are simply 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10:

A = 0x67452301    B = 0xEFCDAB89    C = 0x98BADCFE    D = 0x10325476

K[i] = floor(|sin(i + 1)| × 2^32)      for i = 0 … 63
K[0] = 0xd76aa478   K[1] = 0xe8c7b756   K[2] = 0x242070db   …   K[63] = 0xeb86d391

The 64 additive constants K come from the sine function, with the argument in radians. Using a public formula instead of arbitrary numbers is a "nothing up my sleeve" choice: it shows the designer did not pick values with a hidden weakness. The table S holds the left-rotation amounts, four distinct values per round, for example 7, 12, 17 and 22 in the first round.

Step 3: four rounds of 16 operations

Each 512-bit block is processed in 64 steps, grouped into four rounds of 16. Every step computes one nonlinear function of B, C and D, adds A, a constant K[i] and one message word M[g], rotates the result left by S[i] bits, adds B, and then rotates the four registers. The rounds differ only in the function and in the order in which the message words are used:

round 1 (i 0–15)    F = (B AND C) OR (NOT B AND D)    g = i
round 2 (i 16–31)   G = (D AND B) OR (NOT D AND C)    g = (5i + 1) mod 16
round 3 (i 32–47)   H = B XOR C XOR D                 g = (3i + 5) mod 16
round 4 (i 48–63)   I = C XOR (B OR NOT D)            g = 7i mod 16

each step:  B' = B + ((A + f(B,C,D) + K[i] + M[g]) <<< S[i])
            (A, B, C, D) ← (D, B', B, C)

Here is the very first step for abc, with all values in hex. F of the initial B, C and D is 98badcfe. Adding A, K[0] and M[0] modulo 2³² gives 57ce06d8. Rotating that left by 7 and adding B gives the new B, d6d117b4. The registers then shift, so after step 1 the state is A = 10325476, B = d6d117b4, C = efcdab89, D = 98badcfe. Sixty-three more steps like it follow, each one mixing another message word into the state.

After the 64th step the four registers are added, word by word, to the values they had before the block. This feed-forward makes the compression function hard to run backwards. With more blocks, the result becomes the starting state for the next block.

Step 4: producing the digest

The final A, B, C and D are written out as 16 bytes, each word in little-endian order, and those bytes are what you print in hex. For abc the first word ends as 0x98500190, which is written as the bytes 90 01 50 98, and that is why the familiar digest begins 90015098:

>>> import hashlib
>>> hashlib.md5(b"abc").hexdigest()
'900150983cd24fb0d6963f7d28e17f72'

Forgetting the byte order here, printing each word as a hex number instead, is the most common bug in classroom implementations. It produces a 32-character value that looks plausible and is wrong. RFC 1321 includes test values for "", "a", "abc" and longer strings; check an implementation against all of them, and compare a few random inputs with a known MD5 hash generator.

Why MD5 is broken despite all this mixing

The output is 128 bits, so a generic birthday attack needs about 2⁶⁴ hashes to find a collision, which was already considered too low a margin in the 1990s. The real damage came from analysis of the round structure. In 2004, Xiaoyun Wang and colleagues published practical collisions, and later work made chosen-prefix collisions cheap enough to forge an X.509 certificate signed by a real certificate authority, shown in 2008. The Flame malware of 2012 used an MD5 collision to forge a Microsoft code-signing certificate.

Collisions are the broken property. Finding an input for a given MD5 digest (a preimage) is still not practical in general, but short or common inputs such as passwords can be recovered by brute force or lookup tables, because MD5 is extremely fast. RFC 6151 therefore says MD5 must not be used where collision resistance is required. It remains acceptable for non-security checksums and cache keys; for anything else use SHA-256, and for passwords a slow password hash such as Argon2 or bcrypt. The MD5 hash length article covers the output format in more detail.

Sources and further reading

RFC 1321 (The MD5 Message-Digest Algorithm) contains the full specification, reference C code and test vectors. RFC 6151 updates its security considerations. Wang and Yu, "How to Break MD5 and Other Hash Functions" (EUROCRYPT 2005), describes the first practical collisions, and Stevens, Lenstra and de Weger's chosen-prefix work, including the 2008 rogue CA certificate, shows the practical impact. The Python hashlib and struct documentation covers the functions used in the code.

Frequently asked questions

What are the steps of the MD5 algorithm?

Pad the message to a multiple of 512 bits and append its length, initialise four 32-bit state words, process each 512-bit block with 64 operations in four rounds, and output the final state as 16 little-endian bytes.

How many rounds does MD5 have?

Four rounds of 16 operations each, 64 operations per 512-bit block. Each round uses a different nonlinear function and a different order of the sixteen message words.

How does MD5 padding work?

It appends a 0x80 byte, then zero bytes until the length is 56 modulo 64, then the original message length in bits as a 64-bit little-endian number. Every message gets at least 9 bytes of padding.

Where do the MD5 constants come from?

The 64 constants are K[i] = floor(|sin(i + 1)| × 2^32), with i from 0 to 63 and the sine in radians. The initial state words are the bytes 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10.

What is the MD5 hash of abc?

900150983cd24fb0d6963f7d28e17f72. It is one of the test vectors in RFC 1321 and a quick check for any implementation.

Why is MD5 no longer secure?

Practical collision attacks have existed since 2004, and chosen-prefix collisions have been used to forge certificates. MD5 is also fast enough to brute-force short inputs, so it must not be used for signatures or passwords.