1(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
2Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
3
4MODULE ethMD5;  (** portable *) (* ejz  *)
5  IMPORT SYSTEM;
6
7(* todo. Use fixed size integers and sets. *)
8
9(** The MD5 Message-Digest Algorithm (RFC1321)
10
11The algorithm takes as input a message of arbitrary length and produces
12as output a 128-bit "fingerprint" or "message digest" of the input. It is
13conjectured that it is computationally infeasible to produce two messages
14having the same message digest, or to produce any message having a
15given prespecified target message digest. The MD5 algorithm is intended
16for digital signature applications, where a large file must be "compressed"
17in a secure manner before being encrypted with a private (secret) key
18under a public-key cryptosystem such as RSA. *)
19
20  TYPE
21    Context*  = POINTER TO ContextDesc;
22    ContextDesc = RECORD
23      buf: ARRAY 4 OF LONGINT;
24      bits: LONGINT;
25      in: ARRAY 64 OF CHAR
26    END;
27    Digest* = ARRAY 16 OF CHAR;
28
29(** Begin an MD5 operation, with a new context. *)
30  PROCEDURE New*(): Context;
31    VAR cont: Context;
32  BEGIN
33    NEW(cont);
34    cont.buf[0] := 00000000067452301H;
35    cont.buf[1] := 0FFFFFFFFEFCDAB89H;
36    cont.buf[2] := 0FFFFFFFF98BADCFEH;
37    cont.buf[3] := 00000000010325476H;
38    cont.bits := 0;
39    RETURN cont
40  END New;
41
42  PROCEDURE ByteReverse(VAR in: ARRAY OF SYSTEM.BYTE; VAR out: ARRAY OF LONGINT; longs: LONGINT);
43    VAR
44      adr:   SYSTEM.ADDRESS;
45      t, i:  LONGINT;
46      bytes: ARRAY 4 OF CHAR;
47  BEGIN
48    adr := SYSTEM.ADR(in[0]); i := 0;
49    WHILE i < longs DO
50      SYSTEM.MOVE(adr, SYSTEM.ADR(bytes[0]), 4);
51      t := ORD(bytes[3]);
52      t := 256*t + ORD(bytes[2]);
53      t := 256*t + ORD(bytes[1]);
54      t := 256*t + ORD(bytes[0]);
55      out[i] := t;
56      INC(adr, 4); INC(i)
57    END
58  END ByteReverse;
59
60  PROCEDURE F1(x, y, z: LONGINT): LONGINT;
61  BEGIN
62    RETURN SYSTEM.VAL(LONGINT, (SYSTEM.VAL(SET, x)*SYSTEM.VAL(SET, y)) + ((-SYSTEM.VAL(SET, x))*SYSTEM.VAL(SET, z)))
63  END F1;
64
65  PROCEDURE F2(x, y, z: LONGINT): LONGINT;
66  BEGIN
67    RETURN SYSTEM.VAL(LONGINT, (SYSTEM.VAL(SET, x)*SYSTEM.VAL(SET, z)) + (SYSTEM.VAL(SET, y)*(-SYSTEM.VAL(SET, z))))
68  END F2;
69
70  PROCEDURE F3(x, y, z: LONGINT): LONGINT;
71  BEGIN
72    RETURN SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, x) / SYSTEM.VAL(SET, y) / SYSTEM.VAL(SET, z))
73  END F3;
74
75  PROCEDURE F4(x, y, z: LONGINT): LONGINT;
76  BEGIN
77    RETURN SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, y) / (SYSTEM.VAL(SET, x)+(-SYSTEM.VAL(SET, z))))
78  END F4;
79
80  PROCEDURE STEP1(VAR w: LONGINT; x, y, z, data, s: LONGINT);
81  BEGIN
82    w := w+F1(x, y, z)+data;
83    w := SYSTEM.ROT(w, s);
84    INC(w, x)
85  END STEP1;
86
87  PROCEDURE STEP2(VAR w: LONGINT; x, y, z, data, s: LONGINT);
88  BEGIN
89    w := w+F2(x, y, z)+data;
90    w := SYSTEM.ROT(w, s);
91    INC(w, x)
92  END STEP2;
93
94  PROCEDURE STEP3(VAR w: LONGINT; x, y, z, data, s: LONGINT);
95  BEGIN
96    w := w+F3(x, y, z)+data;
97    w := SYSTEM.ROT(w, s);
98    INC(w, x)
99  END STEP3;
100
101  PROCEDURE STEP4(VAR w: LONGINT; x, y, z, data, s: LONGINT);
102  BEGIN
103    w := w+F4(x, y, z)+data;
104    w := SYSTEM.ROT(w, s);
105    INC(w, x)
106  END STEP4;
107
108  PROCEDURE Transform(VAR buf, in: ARRAY OF LONGINT);
109    VAR a, b, c, d: LONGINT;
110  BEGIN
111    a := buf[0]; b := buf[1]; c := buf[2]; d := buf[3];
112
113    STEP1(a, b, c, d, in[0]  + 0FFFFFFFFD76AA478H, 7);
114    STEP1(d, a, b, c, in[1]  + 0FFFFFFFFE8C7B756H, 12);
115    STEP1(c, d, a, b, in[2]  + 000000000242070DBH, 17);
116    STEP1(b, c, d, a, in[3]  + 0FFFFFFFFC1BDCEEEH, 22);
117    STEP1(a, b, c, d, in[4]  + 0FFFFFFFFF57C0FAFH, 7);
118    STEP1(d, a, b, c, in[5]  + 0000000004787C62AH, 12);
119    STEP1(c, d, a, b, in[6]  + 0FFFFFFFFA8304613H, 17);
120    STEP1(b, c, d, a, in[7]  + 0FFFFFFFFFD469501H, 22);
121    STEP1(a, b, c, d, in[8]  + 000000000698098D8H, 7);
122    STEP1(d, a, b, c, in[9]  + 0FFFFFFFF8B44F7AFH, 12);
123    STEP1(c, d, a, b, in[10] + 0FFFFFFFFFFFF5BB1H, 17);
124    STEP1(b, c, d, a, in[11] + 0FFFFFFFF895CD7BEH, 22);
125    STEP1(a, b, c, d, in[12] + 0000000006B901122H, 7);
126    STEP1(d, a, b, c, in[13] + 0FFFFFFFFFD987193H, 12);
127    STEP1(c, d, a, b, in[14] + 0FFFFFFFFA679438EH, 17);
128    STEP1(b, c, d, a, in[15] + 00000000049B40821H, 22);
129
130    STEP2(a, b, c, d, in[1]  + 0FFFFFFFFF61E2562H, 5);
131    STEP2(d, a, b, c, in[6]  + 0FFFFFFFFC040B340H, 9);
132    STEP2(c, d, a, b, in[11] + 000000000265E5A51H, 14);
133    STEP2(b, c, d, a, in[0]  + 0FFFFFFFFE9B6C7AAH, 20);
134    STEP2(a, b, c, d, in[5]  + 0FFFFFFFFD62F105DH, 5);
135    STEP2(d, a, b, c, in[10] + 00000000002441453H, 9);
136    STEP2(c, d, a, b, in[15] + 0FFFFFFFFD8A1E681H, 14);
137    STEP2(b, c, d, a, in[4]  + 0FFFFFFFFE7D3FBC8H, 20);
138    STEP2(a, b, c, d, in[9]  + 00000000021E1CDE6H, 5);
139    STEP2(d, a, b, c, in[14] + 0FFFFFFFFC33707D6H, 9);
140    STEP2(c, d, a, b, in[3]  + 0FFFFFFFFF4D50D87H, 14);
141    STEP2(b, c, d, a, in[8]  + 000000000455A14EDH, 20);
142    STEP2(a, b, c, d, in[13] + 0FFFFFFFFA9E3E905H, 5);
143    STEP2(d, a, b, c, in[2]  + 0FFFFFFFFFCEFA3F8H, 9);
144    STEP2(c, d, a, b, in[7]  + 000000000676F02D9H, 14);
145    STEP2(b, c, d, a, in[12] + 0FFFFFFFF8D2A4C8AH, 20);
146
147    STEP3(a, b, c, d, in[5]  + 0FFFFFFFFFFFA3942H, 4);
148    STEP3(d, a, b, c, in[8]  + 0FFFFFFFF8771F681H, 11);
149    STEP3(c, d, a, b, in[11] + 0000000006D9D6122H, 16);
150    STEP3(b, c, d, a, in[14] + 0FFFFFFFFFDE5380CH, 23);
151    STEP3(a, b, c, d, in[1]  + 0FFFFFFFFA4BEEA44H, 4);
152    STEP3(d, a, b, c, in[4]  + 0000000004BDECFA9H, 11);
153    STEP3(c, d, a, b, in[7]  + 0FFFFFFFFF6BB4B60H, 16);
154    STEP3(b, c, d, a, in[10] + 0FFFFFFFFBEBFBC70H, 23);
155    STEP3(a, b, c, d, in[13] + 000000000289B7EC6H, 4);
156    STEP3(d, a, b, c, in[0]  + 0FFFFFFFFEAA127FAH, 11);
157    STEP3(c, d, a, b, in[3]  + 0FFFFFFFFD4EF3085H, 16);
158    STEP3(b, c, d, a, in[6]  + 00000000004881D05H, 23);
159    STEP3(a, b, c, d, in[9]  + 0FFFFFFFFD9D4D039H, 4);
160    STEP3(d, a, b, c, in[12] + 0FFFFFFFFE6DB99E5H, 11);
161    STEP3(c, d, a, b, in[15] + 0000000001FA27CF8H, 16);
162    STEP3(b, c, d, a, in[2]  + 0FFFFFFFFC4AC5665H, 23);
163
164    STEP4(a, b, c, d, in[0]  + 0FFFFFFFFF4292244H, 6);
165    STEP4(d, a, b, c, in[7]  + 000000000432AFF97H, 10);
166    STEP4(c, d, a, b, in[14] + 0FFFFFFFFAB9423A7H, 15);
167    STEP4(b, c, d, a, in[5]  + 0FFFFFFFFFC93A039H, 21);
168    STEP4(a, b, c, d, in[12] + 000000000655B59C3H, 6);
169    STEP4(d, a, b, c, in[3]  + 0FFFFFFFF8F0CCC92H, 10);
170    STEP4(c, d, a, b, in[10] + 0FFFFFFFFFFEFF47DH, 15);
171    STEP4(b, c, d, a, in[1]  + 0FFFFFFFF85845DD1H, 21);
172    STEP4(a, b, c, d, in[8]  + 0000000006FA87E4FH, 6);
173    STEP4(d, a, b, c, in[15] + 0FFFFFFFFFE2CE6E0H, 10);
174    STEP4(c, d, a, b, in[6]  + 0FFFFFFFFA3014314H, 15);
175    STEP4(b, c, d, a, in[13] + 0000000004E0811A1H, 21);
176    STEP4(a, b, c, d, in[4]  + 0FFFFFFFFF7537E82H, 6);
177    STEP4(d, a, b, c, in[11] + 0FFFFFFFFBD3AF235H, 10);
178    STEP4(c, d, a, b, in[2]  + 0000000002AD7D2BBH, 15);
179    STEP4(b, c, d, a, in[9]  + 0FFFFFFFFEB86D391H, 21);
180
181    INC(buf[0], a); INC(buf[1], b);
182    INC(buf[2], c); INC(buf[3], d)
183  END Transform;
184
185(** Continues an MD5 message-digest operation, processing another
186  message block, and updating the context. *)
187  PROCEDURE Write*(context: Context; ch: CHAR);
188    VAR
189      in: ARRAY 16 OF LONGINT;
190      t, len: LONGINT;
191  BEGIN
192    t := context.bits; len := 1;
193    context.bits := t + 8;
194    t := (t DIV 8) MOD 64;
195    IF t > 0 THEN
196      t := 64-t;
197      IF 1 < t THEN
198        context.in[64-t] := ch;
199        RETURN
200      END;
201      ASSERT(len = 1);
202      context.in[64-t] := ch;
203      ByteReverse(context.in, in, 16);
204      Transform(context.buf, in);
205      DEC(len, t)
206    END;
207    IF len > 0 THEN
208      context.in[0] := ch
209    END
210  END Write;
211
212(** Continues an MD5 message-digest operation, processing another
213  message block, and updating the context. *)
214  PROCEDURE WriteBytes*(context: Context; VAR buf: ARRAY OF CHAR; len: LONGINT);
215    VAR
216      in: ARRAY 16 OF LONGINT;
217      beg, t: LONGINT;
218  BEGIN
219    beg := 0; t := context.bits;
220    context.bits := t + len*8;
221    t := (t DIV 8) MOD 64;
222    IF t > 0 THEN
223      t := 64-t;
224      IF len < t THEN
225        SYSTEM.MOVE(SYSTEM.ADR(buf[beg]), SYSTEM.ADR(context.in[64-t]), len);
226        RETURN
227      END;
228      SYSTEM.MOVE(SYSTEM.ADR(buf[beg]), SYSTEM.ADR(context.in[64-t]), t);
229      ByteReverse(context.in, in, 16);
230      Transform(context.buf, in);
231      INC(beg, t); DEC(len, t)
232    END;
233    WHILE len >= 64 DO
234      SYSTEM.MOVE(SYSTEM.ADR(buf[beg]), SYSTEM.ADR(context.in[0]), 64);
235      ByteReverse(context.in, in, 16);
236      Transform(context.buf, in);
237      INC(beg, 64); DEC(len, 64)
238    END;
239    IF len > 0 THEN
240      SYSTEM.MOVE(SYSTEM.ADR(buf[beg]), SYSTEM.ADR(context.in[0]), len)
241    END
242  END WriteBytes;
243
244(** Ends an MD5 message-digest operation, writing the message digest. *)
245  PROCEDURE Close*(context: Context; VAR digest: Digest);
246    VAR
247      in: ARRAY 16 OF LONGINT;
248      beg, i, count: LONGINT;
249  BEGIN
250    count := (context.bits DIV 8) MOD 64;
251    beg := count;
252    context.in[beg] := CHR(128); INC(beg);
253    count := 64-1-count;
254    IF count < 8 THEN
255      i := 0;
256      WHILE i < count DO
257        context.in[beg+i] := 0X; INC(i)
258      END;
259      ByteReverse(context.in, in, 16);
260      Transform(context.buf, in);
261      i := 0;
262      WHILE i < 56 DO
263        context.in[i] := 0X; INC(i)
264      END
265    ELSE
266      i := 0;
267      WHILE i < (count-8) DO
268        context.in[beg+i] := 0X; INC(i)
269      END
270    END;
271    ByteReverse(context.in, in, 14);
272    in[14] := context.bits; in[15] := 0;
273    Transform(context.buf, in);
274    ByteReverse(context.buf, in, 4);
275    SYSTEM.MOVE(SYSTEM.ADR(in[0]), SYSTEM.ADR(digest[0]), 16)
276  END Close;
277
278  PROCEDURE HexDigit(i: LONGINT): CHAR;
279  BEGIN
280    IF i < 10 THEN
281      RETURN CHR(ORD("0")+i)
282    ELSE
283      RETURN CHR(ORD("a")+i-10)
284    END
285  END HexDigit;
286
287(** Convert the digest into an hexadecimal string. *)
288  PROCEDURE ToString*(digest: Digest; VAR str: ARRAY OF CHAR);
289    VAR i: LONGINT;
290  BEGIN
291    FOR i := 0 TO 15 DO
292      str[2*i] := HexDigit(ORD(digest[i]) DIV 16);
293      str[2*i+1] := HexDigit(ORD(digest[i]) MOD 16)
294    END;
295    str[32] := 0X
296  END ToString;
297
298END ethMD5.
299