MD5 en Python, d'après le pseudo-code de Wikipedia

In [1]:
from struct import  pack, unpack # pour gérer le little-endian
from math import sin

s = [ 7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,
      5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,
      4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,
      6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21 ]

K = [0]*64
for i in range(64): K[i] = int(2**32 * abs(sin(i + 1.)))
In [2]:
print map(hex,K)
['0xd76aa478', '0xe8c7b756', '0x242070db', '0xc1bdceee', '0xf57c0faf', '0x4787c62a', '0xa8304613', '0xfd469501', '0x698098d8', '0x8b44f7af', '0xffff5bb1', '0x895cd7be', '0x6b901122', '0xfd987193', '0xa679438e', '0x49b40821', '0xf61e2562', '0xc040b340', '0x265e5a51', '0xe9b6c7aa', '0xd62f105d', '0x2441453', '0xd8a1e681', '0xe7d3fbc8', '0x21e1cde6', '0xc33707d6', '0xf4d50d87', '0x455a14ed', '0xa9e3e905', '0xfcefa3f8', '0x676f02d9', '0x8d2a4c8a', '0xfffa3942', '0x8771f681', '0x6d9d6122', '0xfde5380c', '0xa4beea44', '0x4bdecfa9', '0xf6bb4b60', '0xbebfbc70', '0x289b7ec6', '0xeaa127fa', '0xd4ef3085', '0x4881d05', '0xd9d4d039', '0xe6db99e5', '0x1fa27cf8', '0xc4ac5665', '0xf4292244', '0x432aff97', '0xab9423a7', '0xfc93a039', '0x655b59c3', '0x8f0ccc92', '0xffeff47d', '0x85845dd1', '0x6fa87e4f', '0xfe2ce6e0', '0xa3014314', '0x4e0811a1', '0xf7537e82', '0xbd3af235', '0x2ad7d2bb', '0xeb86d391']

In [3]:
#Padding. On doit arriver à un multiple de 512 bits
def pad(x):
    m = len (x)*8
    d = (m%512) - 448 # On concatène un bit 1 suivi de d-1 bits 0
    if d<0: d = -d    # pour arriver à un nombre congru à 448 modulo 512
    else: d = 512 - d
    p = ('%X' % int('1'+'0'*(d-1),2)).decode('hex')
    y = x+p
    n = pack('<Q',(m % 2**64) ) # les 64 derniers codent m en little-endian 
    return y+n

def split(x):        # On découpe x en blocs de 64 octets = 512 bits
    assert len(x) % 64 == 0
    return [x[i*64:(i+1)*64] for i in range(len(x)/64)]

def leftrotate(x,c): # La fonction définie dans la spécification; calculs modulo 2**32
    x &= 0xffffffff
    return  (x <<c ) | (x >>(32-c)) & 0xffffffff
In [4]:
def md5sum(x):
    a0 = 0x67452301   #A
    b0 = 0xefcdab89   #B
    c0 = 0x98badcfe   #C
    d0 = 0x10325476   #D

    y = pad(x)
 
    chunks = split(y)
    for chunk in chunks:
        M = unpack('<16I',chunk) # On décode en 16 "unsigned int", little-endian
        A,B,C,D = a0,b0,c0,d0
        for i in range(64):
            if i in range(16):
                F = (B&C) | ((~B)&D)
                g = i
            elif i in range(16,32):
                F = (D&B) | ((~D)&C)
                g = (5*i+1) % 16
            elif i in range(32,48):
                F = B^C^D
                g = (3*i+5) % 16
            else:
                F = C^(B|(~D))
                g = (7*i) % 16
            dTemp = D
            D = C
            C = B
            B = (B + leftrotate((A + F + K[i] + M[g]), s[i])) & 0xffffffff
            A = dTemp
        a0 = (a0+A)  & 0xffffffff
        b0 = (b0+B)  & 0xffffffff
        c0 = (c0+C)  & 0xffffffff
        d0 = (d0+D)  & 0xffffffff
    digest = (a0,b0,c0,d0)
    return pack('<4I',*digest).encode('hex')
In [5]:
md5sum('')
Out[5]:
'd41d8cd98f00b204e9800998ecf8427e'
In [6]:
from hashlib import md5
md5('').hexdigest()
Out[6]:
'd41d8cd98f00b204e9800998ecf8427e'
In [7]:
print md5sum('abracadabra'*666)
print md5('abracadabra'*666).hexdigest()
534d72625c1975d7bd66885ed7238c29
534d72625c1975d7bd66885ed7238c29

In [7]:
 
In [7]: