(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich. Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *) MODULE ethMD5; (** portable *) (* ejz *) IMPORT SYSTEM; (** The MD5 Message-Digest Algorithm (RFC1321) The algorithm takes as input a message of arbitrary length and produces as output a 128-bit "fingerprint" or "message digest" of the input. It is conjectured that it is computationally infeasible to produce two messages having the same message digest, or to produce any message having a given prespecified target message digest. The MD5 algorithm is intended for digital signature applications, where a large file must be "compressed" in a secure manner before being encrypted with a private (secret) key under a public-key cryptosystem such as RSA. *) TYPE Context* = POINTER TO ContextDesc; ContextDesc = RECORD buf: ARRAY 4 OF LONGINT; bits: LONGINT; in: ARRAY 64 OF CHAR END; Digest* = ARRAY 16 OF CHAR; (** Begin an MD5 operation, with a new context. *) PROCEDURE New*(): Context; VAR cont: Context; BEGIN NEW(cont); cont.buf[0] := 067452301H; cont.buf[1] := 0EFCDAB89H; cont.buf[2] := 098BADCFEH; cont.buf[3] := 010325476H; cont.bits := 0; RETURN cont END New; PROCEDURE ByteReverse(VAR in: ARRAY OF SYSTEM.BYTE; VAR out: ARRAY OF LONGINT; longs: LONGINT); VAR adr, t, i: LONGINT; bytes: ARRAY 4 OF CHAR; BEGIN adr := SYSTEM.ADR(in[0]); i := 0; WHILE i < longs DO SYSTEM.MOVE(adr, SYSTEM.ADR(bytes[0]), 4); t := ORD(bytes[3]); t := 256*t + ORD(bytes[2]); t := 256*t + ORD(bytes[1]); t := 256*t + ORD(bytes[0]); out[i] := t; INC(adr, 4); INC(i) END END ByteReverse; PROCEDURE F1(x, y, z: LONGINT): LONGINT; BEGIN RETURN SYSTEM.VAL(LONGINT, (SYSTEM.VAL(SET, x)*SYSTEM.VAL(SET, y)) + ((-SYSTEM.VAL(SET, x))*SYSTEM.VAL(SET, z))) END F1; PROCEDURE F2(x, y, z: LONGINT): LONGINT; BEGIN RETURN SYSTEM.VAL(LONGINT, (SYSTEM.VAL(SET, x)*SYSTEM.VAL(SET, z)) + (SYSTEM.VAL(SET, y)*(-SYSTEM.VAL(SET, z)))) END F2; PROCEDURE F3(x, y, z: LONGINT): LONGINT; BEGIN RETURN SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, x) / SYSTEM.VAL(SET, y) / SYSTEM.VAL(SET, z)) END F3; PROCEDURE F4(x, y, z: LONGINT): LONGINT; BEGIN RETURN SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, y) / (SYSTEM.VAL(SET, x)+(-SYSTEM.VAL(SET, z)))) END F4; PROCEDURE STEP1(VAR w: LONGINT; x, y, z, data, s: LONGINT); BEGIN w := w+F1(x, y, z)+data; w := SYSTEM.ROT(w, s); INC(w, x) END STEP1; PROCEDURE STEP2(VAR w: LONGINT; x, y, z, data, s: LONGINT); BEGIN w := w+F2(x, y, z)+data; w := SYSTEM.ROT(w, s); INC(w, x) END STEP2; PROCEDURE STEP3(VAR w: LONGINT; x, y, z, data, s: LONGINT); BEGIN w := w+F3(x, y, z)+data; w := SYSTEM.ROT(w, s); INC(w, x) END STEP3; PROCEDURE STEP4(VAR w: LONGINT; x, y, z, data, s: LONGINT); BEGIN w := w+F4(x, y, z)+data; w := SYSTEM.ROT(w, s); INC(w, x) END STEP4; PROCEDURE Transform(VAR buf, in: ARRAY OF LONGINT); VAR a, b, c, d: LONGINT; BEGIN a := buf[0]; b := buf[1]; c := buf[2]; d := buf[3]; STEP1(a, b, c, d, in[0]+0D76AA478H, 7); STEP1(d, a, b, c, in[1]+0E8C7B756H, 12); STEP1(c, d, a, b, in[2]+0242070DBH, 17); STEP1(b, c, d, a, in[3]+0C1BDCEEEH, 22); STEP1(a, b, c, d, in[4]+0F57C0FAFH, 7); STEP1(d, a, b, c, in[5]+04787C62AH, 12); STEP1(c, d, a, b, in[6]+0A8304613H, 17); STEP1(b, c, d, a, in[7]+0FD469501H, 22); STEP1(a, b, c, d, in[8]+0698098D8H, 7); STEP1(d, a, b, c, in[9]+08B44F7AFH, 12); STEP1(c, d, a, b, in[10]+0FFFF5BB1H, 17); STEP1(b, c, d, a, in[11]+0895CD7BEH, 22); STEP1(a, b, c, d, in[12]+06B901122H, 7); STEP1(d, a, b, c, in[13]+0FD987193H, 12); STEP1(c, d, a, b, in[14]+0A679438EH, 17); STEP1(b, c, d, a, in[15]+049B40821H, 22); STEP2(a, b, c, d, in[1]+0F61E2562H, 5); STEP2(d, a, b, c, in[6]+0C040B340H, 9); STEP2(c, d, a, b, in[11]+0265E5A51H, 14); STEP2(b, c, d, a, in[0]+0E9B6C7AAH, 20); STEP2(a, b, c, d, in[5]+0D62F105DH, 5); STEP2(d, a, b, c, in[10]+02441453H, 9); STEP2(c, d, a, b, in[15]+0D8A1E681H, 14); STEP2(b, c, d, a, in[4]+0E7D3FBC8H, 20); STEP2(a, b, c, d, in[9]+021E1CDE6H, 5); STEP2(d, a, b, c, in[14]+0C33707D6H, 9); STEP2(c, d, a, b, in[3]+0F4D50D87H, 14); STEP2(b, c, d, a, in[8]+0455A14EDH, 20); STEP2(a, b, c, d, in[13]+0A9E3E905H, 5); STEP2(d, a, b, c, in[2]+0FCEFA3F8H, 9); STEP2(c, d, a, b, in[7]+0676F02D9H, 14); STEP2(b, c, d, a, in[12]+08D2A4C8AH, 20); STEP3(a, b, c, d, in[5]+0FFFA3942H, 4); STEP3(d, a, b, c, in[8]+08771F681H, 11); STEP3(c, d, a, b, in[11]+06D9D6122H, 16); STEP3(b, c, d, a, in[14]+0FDE5380CH, 23); STEP3(a, b, c, d, in[1]+0A4BEEA44H, 4); STEP3(d, a, b, c, in[4]+04BDECFA9H, 11); STEP3(c, d, a, b, in[7]+0F6BB4B60H, 16); STEP3(b, c, d, a, in[10]+0BEBFBC70H, 23); STEP3(a, b, c, d, in[13]+0289B7EC6H, 4); STEP3(d, a, b, c, in[0]+0EAA127FAH, 11); STEP3(c, d, a, b, in[3]+0D4EF3085H, 16); STEP3(b, c, d, a, in[6]+04881D05H, 23); STEP3(a, b, c, d, in[9]+0D9D4D039H, 4); STEP3(d, a, b, c, in[12]+0E6DB99E5H, 11); STEP3(c, d, a, b, in[15]+01FA27CF8H, 16); STEP3(b, c, d, a, in[2]+0C4AC5665H, 23); STEP4(a, b, c, d, in[0]+0F4292244H, 6); STEP4(d, a, b, c, in[7]+0432AFF97H, 10); STEP4(c, d, a, b, in[14]+0AB9423A7H, 15); STEP4(b, c, d, a, in[5]+0FC93A039H, 21); STEP4(a, b, c, d, in[12]+0655B59C3H, 6); STEP4(d, a, b, c, in[3]+08F0CCC92H, 10); STEP4(c, d, a, b, in[10]+0FFEFF47DH, 15); STEP4(b, c, d, a, in[1]+085845DD1H, 21); STEP4(a, b, c, d, in[8]+06FA87E4FH, 6); STEP4(d, a, b, c, in[15]+0FE2CE6E0H, 10); STEP4(c, d, a, b, in[6]+0A3014314H, 15); STEP4(b, c, d, a, in[13]+04E0811A1H, 21); STEP4(a, b, c, d, in[4]+0F7537E82H, 6); STEP4(d, a, b, c, in[11]+ 0BD3AF235H, 10); STEP4(c, d, a, b, in[2]+02AD7D2BBH, 15); STEP4(b, c, d, a, in[9]+0EB86D391H, 21); INC(buf[0], a); INC(buf[1], b); INC(buf[2], c); INC(buf[3], d) END Transform; (** Continues an MD5 message-digest operation, processing another message block, and updating the context. *) PROCEDURE Write*(context: Context; ch: CHAR); VAR in: ARRAY 16 OF LONGINT; t, len: LONGINT; BEGIN t := context.bits; len := 1; context.bits := t + 8; t := (t DIV 8) MOD 64; IF t > 0 THEN t := 64-t; IF 1 < t THEN context.in[64-t] := ch; RETURN END; ASSERT(len = 1); context.in[64-t] := ch; ByteReverse(context.in, in, 16); Transform(context.buf, in); DEC(len, t) END; IF len > 0 THEN context.in[0] := ch END END Write; (** Continues an MD5 message-digest operation, processing another message block, and updating the context. *) PROCEDURE WriteBytes*(context: Context; VAR buf: ARRAY OF CHAR; len: LONGINT); VAR in: ARRAY 16 OF LONGINT; beg, t: LONGINT; BEGIN beg := 0; t := context.bits; context.bits := t + len*8; t := (t DIV 8) MOD 64; IF t > 0 THEN t := 64-t; IF len < t THEN SYSTEM.MOVE(SYSTEM.ADR(buf[beg]), SYSTEM.ADR(context.in[64-t]), len); RETURN END; SYSTEM.MOVE(SYSTEM.ADR(buf[beg]), SYSTEM.ADR(context.in[64-t]), t); ByteReverse(context.in, in, 16); Transform(context.buf, in); INC(beg, t); DEC(len, t) END; WHILE len >= 64 DO SYSTEM.MOVE(SYSTEM.ADR(buf[beg]), SYSTEM.ADR(context.in[0]), 64); ByteReverse(context.in, in, 16); Transform(context.buf, in); INC(beg, 64); DEC(len, 64) END; IF len > 0 THEN SYSTEM.MOVE(SYSTEM.ADR(buf[beg]), SYSTEM.ADR(context.in[0]), len) END END WriteBytes; (** Ends an MD5 message-digest operation, writing the message digest. *) PROCEDURE Close*(context: Context; VAR digest: Digest); VAR in: ARRAY 16 OF LONGINT; beg, i, count: LONGINT; BEGIN count := (context.bits DIV 8) MOD 64; beg := count; context.in[beg] := CHR(128); INC(beg); count := 64-1-count; IF count < 8 THEN i := 0; WHILE i < count DO context.in[beg+i] := 0X; INC(i) END; ByteReverse(context.in, in, 16); Transform(context.buf, in); i := 0; WHILE i < 56 DO context.in[i] := 0X; INC(i) END ELSE i := 0; WHILE i < (count-8) DO context.in[beg+i] := 0X; INC(i) END END; ByteReverse(context.in, in, 14); in[14] := context.bits; in[15] := 0; Transform(context.buf, in); ByteReverse(context.buf, in, 4); SYSTEM.MOVE(SYSTEM.ADR(in[0]), SYSTEM.ADR(digest[0]), 16) END Close; PROCEDURE HexDigit(i: LONGINT): CHAR; BEGIN IF i < 10 THEN RETURN CHR(ORD("0")+i) ELSE RETURN CHR(ORD("a")+i-10) END END HexDigit; (** Convert the digest into an hexadecimal string. *) PROCEDURE ToString*(digest: Digest; VAR str: ARRAY OF CHAR); VAR i: LONGINT; BEGIN FOR i := 0 TO 15 DO str[2*i] := HexDigit(ORD(digest[i]) DIV 16); str[2*i+1] := HexDigit(ORD(digest[i]) MOD 16) END; str[32] := 0X END ToString; END ethMD5.