מכינים חוצץ באורך 4 מילים המסומנות formula_14 כל מילה מאוחסנת באוגר באורך 32 סיביות. בסך הכול נפח הזיכרון הפנימי הוא 128 סיביות. אתחול האוגרים מתבצע על ידי וקטור האתחול המכיל 4 מילים קבועות formula_15. המכילות את מחרוזת הספרות של הבסיס ההקסדצימלי: "0123456789abcdef" ישר והפוך. לאחר חלוקה למילים לפי סדר בתים קטן מתקבל:
4. עיבוד הקלט.
תחילה מכינים ארבע פונקציות עזר פנימיות המקבלות כקלט שלוש מילים באורך 32 סיביות ומחזירות פלט באורך מילה אחת. הפונקציות הן:
הסימן formula_17 מייצג וגם, הסימן formula_18 מייצג את אופרטור השלילה, הסימן formula_19 מייצג או לוגי והסימן formula_20 מייצג XOR. בכל פוזיציה של הקלט הפונקציה formula_21 מתנהגת כפסוק לוגי: "אם formula_22 אז formula_23 אחרת formula_24". אפשר היה להחליף את האופרטור formula_19 בחיבור (+) בגלל שסיביות formula_26 לעולם לא יהיו '1' באותן פוזיציות ולכן לא תהיה בעיה עם הנשא. אפשר לראות שאם סיביות הקלט formula_22, formula_23 ו-formula_24 בלתי מוטות ובלתי תלויות זו בזו, תוצאת הפונקציה תהיה בלתי תלויה גם היא. יתר הפונקציות דומות לפונקציה formula_21 בכך שהן מייצרות במקביל פלט בלתי תלוי הדדית. הפונקציה formula_31 היא פונקציית הזוגיות (XOR) של הקלט.
בנוסף לפונקציות יש להכין טבלת קבועים formula_32 המכילה 64 אלמנטים. עבור כל אינדקס formula_33 כאשר formula_34 האלמנט formula_35 מכיל את החלק השלם של השבר formula_36 (הפונקציה sin היא הסינוס של formula_37 ברדיאנים). אין צורך לחשב את הטבלה בכל פעם שצריכים לחשב תמצית, נהוג לחשבה מראש. להלן ערכי הקבועים בבסיס הקסדצימלי:
במהלך חישוב התמצית, כל בלוק קלט formula_38 מועתק למשתנה מקומי formula_39 לצורך עיבוד. formula_39 מכיל 16 מילים באורך 32 סיביות שהם 512 סיביות. כל חישובי הפונקציות מבוצעים עם מילים מתוך formula_39 לפי סדר מסוים. סדר עיבוד המילים נקבע לפי הטבלה הבאה בהתאם למספר הסבב:
ב-16 הסבבים הראשונים מעבדים את מילות הבלוק לפי הסדר מהמילה הראשונה עד המילה ה-16. בסבב ה-17 לפי הטבלה מתחילים מהמילה השנייה formula_42 במקום הראשונה לאחר מכן מדלגים למילה השביעית וכן הלאה (יש לזכור שהספירה מתחילה תמיד מאפס).
בכל סבב מתבצעת הזזה מעגלית במספר הפוזיציות שונה בהתאם למספר הסבב. הטבלה הבאה מכילה את מספרי ההזזות עבור כל הסבבים:
הטבלה formula_43 מכילה 64 ערכים כשכל ערך מייצג את מספר הפוזיציות שיש להזיז את המשתנה המקומי formula_44 המכיל את פלט הפונקציות האמורות ומהווה חלק מערך הביניים של האלגוריתם. למשל בסבב הראשון formula_45 הזזה של שבע סיביות, לדוגמה formula_46.
פונקציית התמצות הפנימית של אלגוריתם MD5 מבצעת על כל בלוק קלט 64 פעולות כאשר בסופן היא מעדכנת את המצב הפנימי שהופך לקלט עבור הבלוק הבא. פלט פונקציית הגיבוב יהיה המצב הפנימי האחרון לאחר סיום כל הבלוקים. להלן פסאודו קוד המתאר את המתרחש בלב האלגוריתם:
הביטוי formula_47 פירושו העתקת הערכים מהאגף הימני לאגף השמאלי לפי סדר זה formula_48, formula_49 וכן הלאה. הסימן formula_50 מייצג הזזה מעגלית לשמאל לפי מספר הפוזיציות המופיע לימין הסימן בגבולות מילה באורך 32 סיביות. הזזה מעגלית ניתנת למימוש בתוכנה או בחומרה. למשל בשפת C ממשים הזזה מעגלית כך: (value « count) | (value » (32 - count)) כאשר האופרטורים "«" ו-"»" הם אופרטורי ההזזה המובנים בכל שפות התכנות. הפעולה העיקרית באלגוריתם היא המשפט formula_51, המשתנה המקומי formula_44 מכיל את תוצאת הפונקציה על המצב הפנימי יחד עם חלק מבלוק הקלט והקבועים, כאשר formula_21 מוחלפת בפונקציות האחרות לפי הסדר. הפעולה מומחשת בתרשים למעלה.
5. פלט.
הפלט הוא שרשור ערכי הביניים מהסבב האחרון: formula_54
קוד לדוגמה.
להלן קוד שלם בשפת C++ הממחיש את האלגוריתם. הקוד ממוטב חלקית ומיישם מספר אופטימיזציות כדי להשיג ביצועים טובים, ביניהם אפשר למנות; שימוש באופרטורים כמו XOR ו-AND במקום כפל וחיבור, פקודות מאקרו, פתיחת לולאות (loop unrolling) ומיעוט קריאות לפונקציות.
typedef unsigned int uint;
typedef unsigned char byte;
typedef struct {
uint state[4];
uint count[2];
byte buffer[64];
} MD5_CTX;
class MD5
#define ROTATE_LEFT(x, n) (((x) « (n)) | ((x) » (32-(n))))
#define F(x, y, z) (((x) &amp; (y)) | ((~x) &amp; (z)))
#define G(x, y, z) (((x) &amp; (z)) | ((y) &amp; (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
byte PADDING[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + (uint)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
#define GG(a, b, c, d, x, s, ac) { \
(a) += G ((b), (c), (d)) + (x) + (uint)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
#define HH(a, b, c, d, x, s, ac) { \
(a) += H ((b), (c), (d)) + (x) + (uint)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
#define II(a, b, c, d, x, s, ac) { \
(a) += I ((b), (c), (d)) + (x) + (uint)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
static void MD5Transform(uint state[4], byte block[64])
uint a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode(x, block, 64);
/* Round 1 */
FF(a, b, c, d, x[0], 7, 0xd76aa478);
FF(d, a, b, c, x[1], 12, 0xe8c7b756);
FF(c, d, a, b, x[2], 17, 0x242070db);
FF(b, c, d, a, x[3], 22, 0xc1bdceee);
FF(a, b, c, d, x[4], 7, 0xf57c0faf);
FF(d, a, b, c, x[5], 12, 0x4787c62a);
FF(c, d, a, b, x[6], 17, 0xa8304613);
FF(b, c, d, a, x[7], 22, 0xfd469501);
FF(a, b, c, d, x[8], 7, 0x698098d8);
FF(d, a, b, c, x[9], 12, 0x8b44f7af);
FF(c, d, a, b, x[10], 17, 0xffff5bb1);
FF(b, c, d, a, x[11], 22, 0x895cd7be);
FF(a, b, c, d, x[12], 7, 0x6b901122);
FF(d, a, b, c, x[13], 12, 0xfd987193);
FF(c, d, a, b, x[14], 17, 0xa679438e);
FF(b, c, d, a, x[15], 22, 0x49b40821);
/* Round 2 */
GG(a, b, c, d, x[1], 5, 0xf61e2562);
GG(d, a, b, c, x[6], 9, 0xc040b340);
GG(c, d, a, b, x[11], 14, 0x265e5a51);
GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);
GG(a, b, c, d, x[5], 5, 0xd62f105d);
GG(d, a, b, c, x[10], 9, 0x2441453);
GG(c, d, a, b, x[15], 14, 0xd8a1e681);
GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);
GG(a, b, c, d, x[9], 5, 0x21e1cde6);
GG(d, a, b, c, x[14], 9, 0xc33707d6);
GG(c, d, a, b, x[3], 14, 0xf4d50d87);
GG(b, c, d, a, x[8], 20, 0x455a14ed);
GG(a, b, c, d, x[13], 5, 0xa9e3e905);
GG(d, a, b, c, x[2], 9, 0xfcefa3f8);
GG(c, d, a, b, x[7], 14, 0x676f02d9);
GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
/* Round 3 */
HH(a, b, c, d, x[5], 4, 0xfffa3942);
HH(d, a, b, c, x[8], 11, 0x8771f681);
HH(c, d, a, b, x[11], 16, 0x6d9d6122);
HH(b, c, d, a, x[14], 23, 0xfde5380c);
HH(a, b, c, d, x[1], 4, 0xa4beea44);
HH(d, a, b, c, x[4], 11, 0x4bdecfa9);
HH(c, d, a, b, x[7], 16, 0xf6bb4b60);
HH(b, c, d, a, x[10], 23, 0xbebfbc70);
HH(a, b, c, d, x[13], 4, 0x289b7ec6);
HH(d, a, b, c, x[0], 11, 0xeaa127fa);
HH(c, d, a, b, x[3], 16, 0xd4ef3085);
HH(b, c, d, a, x[6], 23, 0x4881d05);
HH(a, b, c, d, x[9], 4, 0xd9d4d039);
HH(d, a, b, c, x[12], 11, 0xe6db99e5);
HH(c, d, a, b, x[15], 16, 0x1fa27cf8);
HH(b, c, d, a, x[2], 23, 0xc4ac5665);
/* Round 4 */
II(a, b, c, d, x[0], 6, 0xf4292244);
II(d, a, b, c, x[7], 10, 0x432aff97);
II(c, d, a, b, x[14], 15, 0xab9423a7);
II(b, c, d, a, x[5], 21, 0xfc93a039);
II(a, b, c, d, x[12], 6, 0x655b59c3);
II(d, a, b, c, x[3], 10, 0x8f0ccc92);
II(c, d, a, b, x[10], 15, 0xffeff47d);
II(b, c, d, a, x[1], 21, 0x85845dd1);
II(a, b, c, d, x[8], 6, 0x6fa87e4f);
II(d, a, b, c, x[15], 10, 0xfe2ce6e0);
II(c, d, a, b, x[6], 15, 0xa3014314);
II(b, c, d, a, x[13], 21, 0x4e0811a1);
II(a, b, c, d, x[4], 6, 0xf7537e82);
II(d, a, b, c, x[11], 10, 0xbd3af235);
II(c, d, a, b, x[2], 15, 0x2ad7d2bb);
II(b, c, d, a, x[9], 21, 0xeb86d391);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
memset((byte *)x, 0, sizeof(x));
static void Decode(uint *output, byte *input, uint len)
uint i, j;
for (i = 0, j = 0; j &lt; len; i++, j += 4)
output[i] = ((uint)input[j]) | (((uint)input[j + 1]) « 8) |
(((uint)input[j + 2]) « 16) | (((uint)input[j + 3]) « 24);
static void Encode(byte *output, uint *input, uint len)
for (uint i = 0, j = 0; j &lt; len; i++, j += 4) {
output[j] = (byte)(input[i] &amp; 0xff);
output[j + 1] = (byte)((input[i] » 8) &amp; 0xff);
output[j + 2] = (byte)((input[i] » 16) &amp; 0xff);
output[j + 3] = (byte)((input[i] » 24) &amp; 0xff);
public:
void Init(MD5_CTX *context)
context-&gt;count[0] = context-&gt;count[1] = 0;
context-&gt;state[0] = 0x67452301;
context-&gt;state[1] = 0xefcdab89;