MODULE MD5; IMPORT Word, Text, Fmt; CONST S11 = 7; S12 = 12; S13 = 17; S14 = 22; S21 = 5; S22 = 9; S23 = 14; S24 = 20; S31 = 4; S32 = 11; S33 = 16; S34 = 23; S41 = 6; S42 = 10; S43 = 15; S44 = 21; pad1 = "\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; pad2 = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; pad3 = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; pad4 = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; padding = pad1 & pad2 & pad3 & pad4; PROCEDURE Init(VAR md5ctx: T) = BEGIN <*ASSERT Word.Size = 32*> md5ctx.count[0] := 0; md5ctx.count[1] := 0; md5ctx.state[0] := 16_67452301; md5ctx.state[1] := 16_efcdab89; md5ctx.state[2] := 16_98badcfe; md5ctx.state[3] := 16_10325476; END Init; PROCEDURE Transform(VAR state: ARRAY [0..3] OF Word.T; VAR input: Buffer) = VAR a, b, c, d: INTEGER; x: ARRAY [0..15] OF INTEGER; PROCEDURE Decode(VAR x: ARRAY [0..15] OF INTEGER; VAR input: Buffer) = BEGIN FOR i := 0 TO 15 DO x[i] := Word.Insert(x[i], ORD(input[4*i+0]), 0, 8); x[i] := Word.Insert(x[i], ORD(input[4*i+1]), 8, 8); x[i] := Word.Insert(x[i], ORD(input[4*i+2]), 16, 8); x[i] := Word.Insert(x[i], ORD(input[4*i+3]), 24, 8); END; END Decode; PROCEDURE FF(VAR a: INTEGER; b, c, d, x, s, ac: INTEGER) = PROCEDURE F(x, y, z: INTEGER): INTEGER = BEGIN RETURN Word.Or(Word.And(x, y), Word.And(Word.Not(x), z)); END F; BEGIN a := b + Word.Rotate(a + F(b, c, d) + x + ac, s); END FF; PROCEDURE GG(VAR a: INTEGER; b, c, d, x, s, ac: INTEGER) = PROCEDURE G(x, y, z: INTEGER): INTEGER = BEGIN RETURN Word.Or(Word.And(x, z), Word.And(y, Word.Not(z))); END G; BEGIN a := b + Word.Rotate(a + G(b, c, d) + x + ac, s); END GG; PROCEDURE HH(VAR a: INTEGER; b, c, d, x, s, ac: INTEGER) = PROCEDURE H(x, y, z: INTEGER): INTEGER = BEGIN RETURN Word.Xor(x, Word.Xor(y,z)); END H; BEGIN a := b + Word.Rotate(a + H(b, c, d) + x + ac, s); END HH; PROCEDURE II(VAR a: INTEGER; b, c, d, x, s, ac: INTEGER) = PROCEDURE I(x, y, z: INTEGER): INTEGER = BEGIN RETURN Word.Xor(y, Word.Or(x, Word.Not(z))) END I; BEGIN a := b + Word.Rotate(a + I(b, c, d) + x + ac, s) END II; BEGIN Decode(x, input); a := state[0]; b := state[1]; c := state[2]; d := state[3]; (* Round 1 *) FF(a, b, c, d, x[ 0], S11, 16_d76aa478); (* 1 *) FF(d, a, b, c, x[ 1], S12, 16_e8c7b756); (* 2 *) FF(c, d, a, b, x[ 2], S13, 16_242070db); (* 3 *) FF(b, c, d, a, x[ 3], S14, 16_c1bdceee); (* 4 *) FF(a, b, c, d, x[ 4], S11, 16_f57c0faf); (* 5 *) FF(d, a, b, c, x[ 5], S12, 16_4787c62a); (* 6 *) FF(c, d, a, b, x[ 6], S13, 16_a8304613); (* 7 *) FF(b, c, d, a, x[ 7], S14, 16_fd469501); (* 8 *) FF(a, b, c, d, x[ 8], S11, 16_698098d8); (* 9 *) FF(d, a, b, c, x[ 9], S12, 16_8b44f7af); (* 10 *) FF(c, d, a, b, x[10], S13, 16_ffff5bb1); (* 11 *) FF(b, c, d, a, x[11], S14, 16_895cd7be); (* 12 *) FF(a, b, c, d, x[12], S11, 16_6b901122); (* 13 *) FF(d, a, b, c, x[13], S12, 16_fd987193); (* 14 *) FF(c, d, a, b, x[14], S13, 16_a679438e); (* 15 *) FF(b, c, d, a, x[15], S14, 16_49b40821); (* 16 *) (* Round 2 *) GG(a, b, c, d, x[ 1], S21, 16_f61e2562); (* 17 *) GG(d, a, b, c, x[ 6], S22, 16_c040b340); (* 18 *) GG(c, d, a, b, x[11], S23, 16_265e5a51); (* 19 *) GG(b, c, d, a, x[ 0], S24, 16_e9b6c7aa); (* 20 *) GG(a, b, c, d, x[ 5], S21, 16_d62f105d); (* 21 *) GG(d, a, b, c, x[10], S22, 16_02441453); (* 22 *) GG(c, d, a, b, x[15], S23, 16_d8a1e681); (* 23 *) GG(b, c, d, a, x[ 4], S24, 16_e7d3fbc8); (* 24 *) GG(a, b, c, d, x[ 9], S21, 16_21e1cde6); (* 25 *) GG(d, a, b, c, x[14], S22, 16_c33707d6); (* 26 *) GG(c, d, a, b, x[ 3], S23, 16_f4d50d87); (* 27 *) GG(b, c, d, a, x[ 8], S24, 16_455a14ed); (* 28 *) GG(a, b, c, d, x[13], S21, 16_a9e3e905); (* 29 *) GG(d, a, b, c, x[ 2], S22, 16_fcefa3f8); (* 30 *) GG(c, d, a, b, x[ 7], S23, 16_676f02d9); (* 31 *) GG(b, c, d, a, x[12], S24, 16_8d2a4c8a); (* 32 *) (* Round 3 *) HH(a, b, c, d, x[ 5], S31, 16_fffa3942); (* 33 *) HH(d, a, b, c, x[ 8], S32, 16_8771f681); (* 34 *) HH(c, d, a, b, x[11], S33, 16_6d9d6122); (* 35 *) HH(b, c, d, a, x[14], S34, 16_fde5380c); (* 36 *) HH(a, b, c, d, x[ 1], S31, 16_a4beea44); (* 37 *) HH(d, a, b, c, x[ 4], S32, 16_4bdecfa9); (* 38 *) HH(c, d, a, b, x[ 7], S33, 16_f6bb4b60); (* 39 *) HH(b, c, d, a, x[10], S34, 16_bebfbc70); (* 40 *) HH(a, b, c, d, x[13], S31, 16_289b7ec6); (* 41 *) HH(d, a, b, c, x[ 0], S32, 16_eaa127fa); (* 42 *) HH(c, d, a, b, x[ 3], S33, 16_d4ef3085); (* 43 *) HH(b, c, d, a, x[ 6], S34, 16_04881d05); (* 44 *) HH(a, b, c, d, x[ 9], S31, 16_d9d4d039); (* 45 *) HH(d, a, b, c, x[12], S32, 16_e6db99e5); (* 46 *) HH(c, d, a, b, x[15], S33, 16_1fa27cf8); (* 47 *) HH(b, c, d, a, x[ 2], S34, 16_c4ac5665); (* 48 *) (* Round 4 *) II(a, b, c, d, x[ 0], S41, 16_f4292244); (* 49 *) II(d, a, b, c, x[ 7], S42, 16_432aff97); (* 50 *) II(c, d, a, b, x[14], S43, 16_ab9423a7); (* 51 *) II(b, c, d, a, x[ 5], S44, 16_fc93a039); (* 52 *) II(a, b, c, d, x[12], S41, 16_655b59c3); (* 53 *) II(d, a, b, c, x[ 3], S42, 16_8f0ccc92); (* 54 *) II(c, d, a, b, x[10], S43, 16_ffeff47d); (* 55 *) II(b, c, d, a, x[ 1], S44, 16_85845dd1); (* 56 *) II(a, b, c, d, x[ 8], S41, 16_6fa87e4f); (* 57 *) II(d, a, b, c, x[15], S42, 16_fe2ce6e0); (* 58 *) II(c, d, a, b, x[ 6], S43, 16_a3014314); (* 59 *) II(b, c, d, a, x[13], S44, 16_4e0811a1); (* 60 *) II(a, b, c, d, x[ 4], S41, 16_f7537e82); (* 61 *) II(d, a, b, c, x[11], S42, 16_bd3af235); (* 62 *) II(c, d, a, b, x[ 2], S43, 16_2ad7d2bb); (* 63 *) II(b, c, d, a, x[ 9], S44, 16_eb86d391); (* 64 *) state[0] := Word.Plus(state[0], a); state[1] := Word.Plus(state[1], b); state[2] := Word.Plus(state[2], c); state[3] := Word.Plus(state[3], d); END Transform; PROCEDURE Update(VAR md5ctx: T; input: TEXT) = VAR index, i, j, partLen: Word.T; locbuff: Buffer; BEGIN index := Word.And(Word.Shift(md5ctx.count[0], -3), 16_3F); md5ctx.count[0] := Word.Plus(md5ctx.count[0], Word.Shift(Text.Length(input), 3)); IF md5ctx.count[0] < Text.Length(input) THEN INC(md5ctx.count[1]); END; md5ctx.count[1] := md5ctx.count[1] + Word.Shift(Text.Length(input), -29); partLen := 64 - index; IF Text.Length(input) >= partLen THEN FOR i := index TO 63 DO md5ctx.buffer[i] := Text.GetChar(input, i-index); END; Transform(md5ctx.state, md5ctx.buffer); i := partLen; WHILE (i + 63) < Text.Length(input) DO FOR j := 0 TO 63 DO locbuff[j] := Text.GetChar(input, i+j); END; Transform(md5ctx.state, locbuff); INC(i, 64); END; index := 0; ELSE i := 0; END; j := 0; WHILE i+j < Text.Length(input) DO md5ctx.buffer[j+index] := Text.GetChar(input, i+j); INC(j); END; END Update; PROCEDURE Final(VAR md5ctx: T): Digest= VAR bits: ARRAY [0..7] OF CHAR; index, padLen: INTEGER; digest: Digest; PROCEDURE Encode(VAR output: ARRAY OF CHAR; VAR input: ARRAY OF Word.T; count: INTEGER) = BEGIN FOR i := 0 TO count DO output[i*4+0] := VAL(Word.Extract(input[i], 0, 8), CHAR); output[i*4+1] := VAL(Word.Extract(input[i], 8, 8), CHAR); output[i*4+2] := VAL(Word.Extract(input[i], 16, 8), CHAR); output[i*4+3] := VAL(Word.Extract(input[i], 24, 8), CHAR) END; END Encode; BEGIN Encode(bits, md5ctx.count, 1); index := Word.And(Word.Shift(md5ctx.count[0], -3), 16_3F); IF index < 56 THEN padLen := 56 - index; ELSE padLen := 120 - index; END; Update(md5ctx, Text.Sub(padding, 0, padLen)); Update(md5ctx, Text.FromChars(bits)); Encode(digest, md5ctx.state, 3); RETURN digest; END Final; PROCEDURE ToText(hash: Digest): TEXT = VAR buf: TEXT := ""; BEGIN FOR i := 0 TO 15 DO buf := buf & Fmt.Pad(Fmt.Int(ORD(hash[i]), 16), 2, '0'); END; RETURN buf; END ToText; BEGIN END MD5.