/* * md5.i - $Id$ * routines to compute MD5 checksums * entirely in interpreted code, so they are slow, * on the order of a few seconds per megabyte * original C source from L. Peter Deutsch ghost@aladdin.com * adapted by David H. Munro */ func md5sum (file, hex=) /* DOCUMENT digest = md5sum(filename) * digest = md5sum(filename, hex=1) * compute MD5 digest of a file, an array of 16 char * with the hex=1 keyword, returned digest is a string (in hex) * * SEE ALSO: md5 */ { if (!is_stream(file)) file = open(file, "rb"); nbyte = sizeof(file); if (!nbyte) return md5(md5()); nchunk = 1000000; /* process file in megabyte chunks */ buf = array(char, min(nbyte,nchunk)); for (state=[],n=0 ; n<nbyte ; n+=nchunk) { if (n+nchunk > nbyte) { nchunk = nbyte - n; if (sizeof(buf) > nchunk) buf = array(char, nchunk); } if (_read(file,n,buf) < nchunk) error, "i/o error reading file"; state = md5(buf, state); } return md5(state, hex=hex); } func md5 (data, state, hex=) /* DOCUMENT state = md5(data) * state = md5(data, state) * digest = md5(state) * digest = md5(state, hex=1) * compute MD5 digest of data, an array of 16 char * with the hex=1 keyword, returned digest is a string (in hex) * * SEE ALSO: md5sum */ { local count, digest; type = structof(data); if (is_void(state)) { if (type == pointer) { /* no more data, return MD5 digest */ count = *data(1); /* makes a copy of count */ _md5_increment, count, sizeof(*data(3)); /* add pending */ /* number of additional bytes to reach 56 mod 64 */ nbyte = ((55-(count(1)>>3))&63) + 1; pad = grow(array(char, nbyte), _md5_number(count)); pad(1) = '\x80'; state = md5(pad, data); return _md5_hex(_md5_number(*state(2)), hex=hex); } /* create a new state, this is first call for this digest */ state = [&[0, 0], &[0x67452301, ~0x10325476, ~0x67452301, 0x10325476], &[]]; if (is_void(data)) return state; } if (type != char) error, "md5 requires char input data"; data = grow(*state(3), data(*)); /* prepend any leftover data */ nbyte = sizeof(data); next = nbyte & 63; state(3) = next? &data(1-next:0) : &[]; nbyte -= next; if (nbyte) { eq_nocopy, count, *state(1); _md5_increment, count, nbyte; nword = nbyte>>6; tmp = array(char, 4, 16, nword); tmp(*) = data(1:-next); tmp = long(tmp); data = tmp(1,,) | (tmp(2,,)<<8) | (tmp(3,,)<<16) | (tmp(4,,)<<24); tmp = []; eq_nocopy, digest, *state(2); a0 = digest(1); b0 = digest(2); c0 = digest(3); d0 = digest(4); for (i=1 ; i<=nword ; ++i) { x = data(_md5_order,i) + _md5_t; a = a0; b = b0; c = c0; d = d0; /* round 1 */ a = a + ((b & c) | ((~b) & d)) + x(1); a = ((a << 7) | ((a >> 25)&0x0000007f)) + b; d = d + ((a & b) | ((~a) & c)) + x(2); d = ((d << 12) | ((d >> 20)&0x00000fff)) + a; c = c + ((d & a) | ((~d) & b)) + x(3); c = ((c << 17) | ((c >> 15)&0x0001ffff)) + d; b = b + ((c & d) | ((~c) & a)) + x(4); b = ((b << 22) | ((b >> 10)&0x003fffff)) + c; a = a + ((b & c) | ((~b) & d)) + x(5); a = ((a << 7) | ((a >> 25)&0x0000007f)) + b; d = d + ((a & b) | ((~a) & c)) + x(6); d = ((d << 12) | ((d >> 20)&0x00000fff)) + a; c = c + ((d & a) | ((~d) & b)) + x(7); c = ((c << 17) | ((c >> 15)&0x0001ffff)) + d; b = b + ((c & d) | ((~c) & a)) + x(8); b = ((b << 22) | ((b >> 10)&0x003fffff)) + c; a = a + ((b & c) | ((~b) & d)) + x(9); a = ((a << 7) | ((a >> 25)&0x0000007f)) + b; d = d + ((a & b) | ((~a) & c)) + x(10); d = ((d << 12) | ((d >> 20)&0x00000fff)) + a; c = c + ((d & a) | ((~d) & b)) + x(11); c = ((c << 17) | ((c >> 15)&0x0001ffff)) + d; b = b + ((c & d) | ((~c) & a)) + x(12); b = ((b << 22) | ((b >> 10)&0x003fffff)) + c; a = a + ((b & c) | ((~b) & d)) + x(13); a = ((a << 7) | ((a >> 25)&0x0000007f)) + b; d = d + ((a & b) | ((~a) & c)) + x(14); d = ((d << 12) | ((d >> 20)&0x00000fff)) + a; c = c + ((d & a) | ((~d) & b)) + x(15); c = ((c << 17) | ((c >> 15)&0x0001ffff)) + d; b = b + ((c & d) | ((~c) & a)) + x(16); b = ((b << 22) | ((b >> 10)&0x003fffff)) + c; /* round 2 */ a = a + ((b & d) | ((~d) & c)) + x(17); a = ((a << 5) | ((a >> 27)&0x0000001f)) + b; d = d + ((a & c) | ((~c) & b)) + x(18); d = ((d << 9) | ((d >> 23)&0x000001ff)) + a; c = c + ((d & b) | ((~b) & a)) + x(19); c = ((c << 14) | ((c >> 18)&0x00003fff)) + d; b = b + ((c & a) | ((~a) & d)) + x(20); b = ((b << 20) | ((b >> 12)&0x000fffff)) + c; a = a + ((b & d) | ((~d) & c)) + x(21); a = ((a << 5) | ((a >> 27)&0x0000001f)) + b; d = d + ((a & c) | ((~c) & b)) + x(22); d = ((d << 9) | ((d >> 23)&0x000001ff)) + a; c = c + ((d & b) | ((~b) & a)) + x(23); c = ((c << 14) | ((c >> 18)&0x00003fff)) + d; b = b + ((c & a) | ((~a) & d)) + x(24); b = ((b << 20) | ((b >> 12)&0x000fffff)) + c; a = a + ((b & d) | ((~d) & c)) + x(25); a = ((a << 5) | ((a >> 27)&0x0000001f)) + b; d = d + ((a & c) | ((~c) & b)) + x(26); d = ((d << 9) | ((d >> 23)&0x000001ff)) + a; c = c + ((d & b) | ((~b) & a)) + x(27); c = ((c << 14) | ((c >> 18)&0x00003fff)) + d; b = b + ((c & a) | ((~a) & d)) + x(28); b = ((b << 20) | ((b >> 12)&0x000fffff)) + c; a = a + ((b & d) | ((~d) & c)) + x(29); a = ((a << 5) | ((a >> 27)&0x0000001f)) + b; d = d + ((a & c) | ((~c) & b)) + x(30); d = ((d << 9) | ((d >> 23)&0x000001ff)) + a; c = c + ((d & b) | ((~b) & a)) + x(31); c = ((c << 14) | ((c >> 18)&0x00003fff)) + d; b = b + ((c & a) | ((~a) & d)) + x(32); b = ((b << 20) | ((b >> 12)&0x000fffff)) + c; /* round 3 */ a = a + (b ~ c ~ d) + x(33); a = ((a << 4) | ((a >> 28)&0x0000000f)) + b; d = d + (a ~ b ~ c) + x(34); d = ((d << 11) | ((d >> 21)&0x000007ff)) + a; c = c + (d ~ a ~ b) + x(35); c = ((c << 16) | ((c >> 16)&0x0000ffff)) + d; b = b + (c ~ d ~ a) + x(36); b = ((b << 23) | ((b >> 9)&0x007fffff)) + c; a = a + (b ~ c ~ d) + x(37); a = ((a << 4) | ((a >> 28)&0x0000000f)) + b; d = d + (a ~ b ~ c) + x(38); d = ((d << 11) | ((d >> 21)&0x000007ff)) + a; c = c + (d ~ a ~ b) + x(39); c = ((c << 16) | ((c >> 16)&0x0000ffff)) + d; b = b + (c ~ d ~ a) + x(40); b = ((b << 23) | ((b >> 9)&0x007fffff)) + c; a = a + (b ~ c ~ d) + x(41); a = ((a << 4) | ((a >> 28)&0x0000000f)) + b; d = d + (a ~ b ~ c) + x(42); d = ((d << 11) | ((d >> 21)&0x000007ff)) + a; c = c + (d ~ a ~ b) + x(43); c = ((c << 16) | ((c >> 16)&0x0000ffff)) + d; b = b + (c ~ d ~ a) + x(44); b = ((b << 23) | ((b >> 9)&0x007fffff)) + c; a = a + (b ~ c ~ d) + x(45); a = ((a << 4) | ((a >> 28)&0x0000000f)) + b; d = d + (a ~ b ~ c) + x(46); d = ((d << 11) | ((d >> 21)&0x000007ff)) + a; c = c + (d ~ a ~ b) + x(47); c = ((c << 16) | ((c >> 16)&0x0000ffff)) + d; b = b + (c ~ d ~ a) + x(48); b = ((b << 23) | ((b >> 9)&0x007fffff)) + c; /* round 4 */ a = a + (c ~ ((~d) | b)) + x(49); a = ((a << 6) | ((a >> 26)&0x0000003f)) + b; d = d + (b ~ ((~c) | a)) + x(50); d = ((d << 10) | ((d >> 22)&0x000003ff)) + a; c = c + (a ~ ((~b) | d)) + x(51); c = ((c << 15) | ((c >> 17)&0x00007fff)) + d; b = b + (d ~ ((~a) | c)) + x(52); b = ((b << 21) | ((b >> 11)&0x001fffff)) + c; a = a + (c ~ ((~d) | b)) + x(53); a = ((a << 6) | ((a >> 26)&0x0000003f)) + b; d = d + (b ~ ((~c) | a)) + x(54); d = ((d << 10) | ((d >> 22)&0x000003ff)) + a; c = c + (a ~ ((~b) | d)) + x(55); c = ((c << 15) | ((c >> 17)&0x00007fff)) + d; b = b + (d ~ ((~a) | c)) + x(56); b = ((b << 21) | ((b >> 11)&0x001fffff)) + c; a = a + (c ~ ((~d) | b)) + x(57); a = ((a << 6) | ((a >> 26)&0x0000003f)) + b; d = d + (b ~ ((~c) | a)) + x(58); d = ((d << 10) | ((d >> 22)&0x000003ff)) + a; c = c + (a ~ ((~b) | d)) + x(59); c = ((c << 15) | ((c >> 17)&0x00007fff)) + d; b = b + (d ~ ((~a) | c)) + x(60); b = ((b << 21) | ((b >> 11)&0x001fffff)) + c; a = a + (c ~ ((~d) | b)) + x(61); a = ((a << 6) | ((a >> 26)&0x0000003f)) + b; d = d + (b ~ ((~c) | a)) + x(62); d = ((d << 10) | ((d >> 22)&0x000003ff)) + a; c = c + (a ~ ((~b) | d)) + x(63); c = ((c << 15) | ((c >> 17)&0x00007fff)) + d; b = b + (d ~ ((~a) | c)) + x(64); b = ((b << 21) | ((b >> 11)&0x001fffff)) + c; a0 += a; b0 += b; c0 += c; d0 += d; } digest(1) = a0; digest(2) = b0; digest(3) = c0; digest(4) = d0; } return state; } func _md5_increment(count, nbyte) { /* add number of bits to count, with carry if overflows 32 bits */ count(2) += nbyte >> 29; old = count(1); nlo = (nbyte << 3) & 0xffffffff; count(1) = new = (old + nlo) & 0xffffffff; if ((new&0x80000000)? (old & nlo & 0x80000000) : ((old | nlo)&0x80000000)) count(1) += 1; } func _md5_number(w) { return char( w(-,) >> [0,8,16,24] )(*); } _md5_order = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 2,7,12,1,6,11,16,5,10,15,4,9,14,3,8,13, 6,9,12,15,2,5,8,11,14,1,4,7,10,13,16,3, 1,8,15,6,13,4,11,2,9,16,7,14,5,12,3,10]; _md5_t = [~0x28955b87, ~0x173848a9, 0x242070db, ~0x3e423111, ~0x0a83f050, 0x4787c62a, ~0x57cfb9ec, ~0x02b96afe, 0x698098d8, ~0x74bb0850, ~0x0000a44e, ~0x76a32841, 0x6b901122, ~0x02678e6c, ~0x5986bc71, 0x49b40821, ~0x09e1da9d, ~0x3fbf4cbf, 0x265e5a51, ~0x16493855, ~0x29d0efa2, 0x02441453, ~0x275e197e, ~0x182c0437, 0x21e1cde6, ~0x3cc8f829, ~0x0b2af278, 0x455a14ed, ~0x561c16fa, ~0x03105c07, 0x676f02d9, ~0x72d5b375, ~0x0005c6bd, ~0x788e097e, 0x6d9d6122, ~0x021ac7f3, ~0x5b4115bb, 0x4bdecfa9, ~0x0944b49f, ~0x4140438f, 0x289b7ec6, ~0x155ed805, ~0x2b10cf7a, 0x04881d05, ~0x262b2fc6, ~0x1924661a, 0x1fa27cf8, ~0x3b53a99a, ~0x0bd6ddbb, 0x432aff97, ~0x546bdc58, ~0x036c5fc6, 0x655b59c3, ~0x70f3336d, ~0x00100b82, ~0x7a7ba22e, 0x6fa87e4f, ~0x01d3191f, ~0x5cfebceb, 0x4e0811a1, ~0x08ac817d, ~0x42c50dca, 0x2ad7d2bb, ~0x14792c6e]; func md5_test { test = ["", "a", "abc", "message digest", "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "12345678901234567890123456789012345678901234567890123456789"+ "012345678901234567890"]; answ = ["d41d8cd98f00b204e9800998ecf8427e", "0cc175b9c0f1b6a831c399e269772661", "900150983cd24fb0d6963f7d28e17f72", "f96b697d7cb7938d525a2f31aaf161d0", "c3fcd3d76192e4007dfb496cca67e13b", "d174ab98d277d9f5a5611c2c9f419d9f", "57edf4a22be3c955ac49da2e2107b67a"]; correct = [[0xd4,0x1d,0x8c,0xd9,0x8f,0x00,0xb2,0x04, 0xe9,0x80,0x09,0x98,0xec,0xf8,0x42,0x7e], [0x0c,0xc1,0x75,0xb9,0xc0,0xf1,0xb6,0xa8, 0x31,0xc3,0x99,0xe2,0x69,0x77,0x26,0x61], [0x90,0x01,0x50,0x98,0x3c,0xd2,0x4f,0xb0, 0xd6,0x96,0x3f,0x7d,0x28,0xe1,0x7f,0x72], [0xf9,0x6b,0x69,0x7d,0x7c,0xb7,0x93,0x8d, 0x52,0x5a,0x2f,0x31,0xaa,0xf1,0x61,0xd0], [0xc3,0xfc,0xd3,0xd7,0x61,0x92,0xe4,0x00, 0x7d,0xfb,0x49,0x6c,0xca,0x67,0xe1,0x3b], [0xd1,0x74,0xab,0x98,0xd2,0x77,0xd9,0xf5, 0xa5,0x61,0x1c,0x2c,0x9f,0x41,0x9d,0x9f], [0x57,0xed,0xf4,0xa2,0x2b,0xe3,0xc9,0x55, 0xac,0x49,0xda,0x2e,0x21,0x07,0xb6,0x7a]]; actual = array(char, 16, numberof(answ)); for (i=1 ; i<=numberof(answ) ; ++i) { data = *pointer(test(i)); actual(,i) = md5(md5(((numberof(data)>1)?data(1:-1):[]))); } if (anyof(actual!=correct)) error, "md5 function flunked test"; } func _md5_hex(digest, hex=) { if (!hex) return digest; s = swrite(format="%02x",digest); s = s(1:16:2) + s(2:16:2); s = s(1:8:2) + s(2:8:2); s = s(1:4:2) + s(2:4:2); return s(1) + s(2); }