/**************************************************************************//*! @file md5.d @version $Id: md5.d,v 1.1 2005/05/16 16:10:53 bcwhite Exp $ @author Brian White @copyright Public Domain @brief Message-Digest #5 generation. This code implements the MD5 message-digest algorithm. The algorithm is due to Ron Rivest. The main class structure was taken from the C++ "Star" library (written and copyright by myself) [http://sf.net/projects/starlib]. The "transform" code was written by Colin Plumb in 1993, no copyright is claimed. Many changes were made to use the "D" language but the essential algorithm remains unchanged. The original version was placed in the public domain and found on the web at [http://www.l2tpd.org/cxref/md5.c.src.html]. Equivalent code is available from RSA Data Security, Inc. This code has been tested against that, and is equivalent, except that you don't need to include two pages of legalese with every copy. *//***************************************************************************/ class Md5 { public: const int BLOCKSIZE = 64; // [bytes] size of block that MD5 operates on const int SUMSIZE = 16; // [bytes] size of MD5 digest private: const int LENGTHSIZE = 8; // [bytes] size of embedded length value ubyte Buffer[BLOCKSIZE]; ubyte Sum[SUMSIZE]; uint BytesAdded; uint BufferUsed; version(LittleEndian) { static const ubyte Init[SUMSIZE] = [ 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 ]; } version(BigEndian) { static const ubyte Init[SUMSIZE] = [ 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89, 0x98, 0xBA, 0xDC, 0xFE, 0x10, 0x32, 0x54, 0x76 ]; } void byteSwap(uint* words, uint count) { version(LittleEndian) { // nothing to do on little-endian architectures } version(LitteEndian) { uint t; while (count--) { t = *words; t = (t >> 24) & 0x000000FF | (t >> 8) & 0x0000FF00 | (t << 8) & 0x00FF0000 | (t << 24) & 0xFF000000; *words++ = t; } } } void transformBlock(ubyte /*const*/* buf) { // // Note: In the original MD5 algorithm, #define's are used to merage all // the operations in to an in-line block. D doesn't support that, so it // was either do all the pre-processing manually or write it as functions // and hope that the compiler will do the in-lining automatically. While // in most cases, I believe this to be the correct philosophy, in cases // like MD5 that are highly computationally intensive and make extensive // use of sub-functions like the F1 to F4 macros, it would be best to // have some way to force the compiler to do what I want. If it leaves // them as functions and calls them as such, performance will suffer // significantly! // /* The four core functions - F1 is optimized somewhat */ static uint f1(uint x, uint y, uint z) { return (z ^ (x & (y ^ z))); } // F1(x, y, z) (x & y | ~x & z) static uint f2(uint x, uint y, uint z) { return f1(z, x, y); } // F2(x, y, z) (z & x | ~z & y) static uint f3(uint x, uint y, uint z) { return (x ^ y ^ z); } // F3(x, y, z) (x ^ y ^ z) static uint f4(uint x, uint y, uint z) { return (y ^ (x | ~z)); } // F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ // #define MD5STEP(f, w, x, y, z, data, s) // ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) static void step(uint function(uint,uint,uint) f, inout uint w, uint x, uint y, uint z, uint d, uint s) { w += f(x,y,z) + d; w = (w << s) | (w >>> (32-s)); // rotate w += x; } /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. This function blocks * the data and converts bytes into longwords for this routine. */ uint a = (cast(uint*)Sum)[0]; uint b = (cast(uint*)Sum)[1]; uint c = (cast(uint*)Sum)[2]; uint d = (cast(uint*)Sum)[3]; uint /*const*/* block = cast(uint*)buf; version(BigEndian) { uint[BLOCKSIZE/4] localblock = block[0..BLOCKSIZE/4]; byteSwap(localblock,localblock.length); block = localblock; } step (&f1, a, b, c, d, block[0] + 0xD76AA478, 7); step (&f1, d, a, b, c, block[1] + 0xE8C7B756, 12); step (&f1, c, d, a, b, block[2] + 0x242070DB, 17); step (&f1, b, c, d, a, block[3] + 0xC1BDCEEE, 22); step (&f1, a, b, c, d, block[4] + 0xF57C0FAF, 7); step (&f1, d, a, b, c, block[5] + 0x4787C62A, 12); step (&f1, c, d, a, b, block[6] + 0xA8304613, 17); step (&f1, b, c, d, a, block[7] + 0xFD469501, 22); step (&f1, a, b, c, d, block[8] + 0x698098D8, 7); step (&f1, d, a, b, c, block[9] + 0x8B44F7AF, 12); step (&f1, c, d, a, b, block[10] + 0xFFFF5BB1, 17); step (&f1, b, c, d, a, block[11] + 0x895CD7BE, 22); step (&f1, a, b, c, d, block[12] + 0x6B901122, 7); step (&f1, d, a, b, c, block[13] + 0xFD987193, 12); step (&f1, c, d, a, b, block[14] + 0xA679438E, 17); step (&f1, b, c, d, a, block[15] + 0x49B40821, 22); step (&f2, a, b, c, d, block[1] + 0xF61E2562, 5); step (&f2, d, a, b, c, block[6] + 0xC040B340, 9); step (&f2, c, d, a, b, block[11] + 0x265E5A51, 14); step (&f2, b, c, d, a, block[0] + 0xE9B6C7AA, 20); step (&f2, a, b, c, d, block[5] + 0xD62F105D, 5); step (&f2, d, a, b, c, block[10] + 0x02441453, 9); step (&f2, c, d, a, b, block[15] + 0xD8A1E681, 14); step (&f2, b, c, d, a, block[4] + 0xE7D3FBC8, 20); step (&f2, a, b, c, d, block[9] + 0x21E1CDE6, 5); step (&f2, d, a, b, c, block[14] + 0xC33707D6, 9); step (&f2, c, d, a, b, block[3] + 0xF4D50D87, 14); step (&f2, b, c, d, a, block[8] + 0x455A14ED, 20); step (&f2, a, b, c, d, block[13] + 0xA9E3E905, 5); step (&f2, d, a, b, c, block[2] + 0xFCEFA3F8, 9); step (&f2, c, d, a, b, block[7] + 0x676F02D9, 14); step (&f2, b, c, d, a, block[12] + 0x8D2A4C8A, 20); step (&f3, a, b, c, d, block[5] + 0xFFFA3942, 4); step (&f3, d, a, b, c, block[8] + 0x8771F681, 11); step (&f3, c, d, a, b, block[11] + 0x6D9D6122, 16); step (&f3, b, c, d, a, block[14] + 0xFDE5380C, 23); step (&f3, a, b, c, d, block[1] + 0xA4BEEA44, 4); step (&f3, d, a, b, c, block[4] + 0x4BDECFA9, 11); step (&f3, c, d, a, b, block[7] + 0xF6BB4B60, 16); step (&f3, b, c, d, a, block[10] + 0xBEBFBC70, 23); step (&f3, a, b, c, d, block[13] + 0x289B7EC6, 4); step (&f3, d, a, b, c, block[0] + 0xEAA127FA, 11); step (&f3, c, d, a, b, block[3] + 0xD4EF3085, 16); step (&f3, b, c, d, a, block[6] + 0x04881D05, 23); step (&f3, a, b, c, d, block[9] + 0xD9D4D039, 4); step (&f3, d, a, b, c, block[12] + 0xE6DB99E5, 11); step (&f3, c, d, a, b, block[15] + 0x1FA27CF8, 16); step (&f3, b, c, d, a, block[2] + 0xC4AC5665, 23); step (&f4, a, b, c, d, block[0] + 0xF4292244, 6); step (&f4, d, a, b, c, block[7] + 0x432AFF97, 10); step (&f4, c, d, a, b, block[14] + 0xAB9423A7, 15); step (&f4, b, c, d, a, block[5] + 0xFC93A039, 21); step (&f4, a, b, c, d, block[12] + 0x655B59C3, 6); step (&f4, d, a, b, c, block[3] + 0x8F0CCC92, 10); step (&f4, c, d, a, b, block[10] + 0xFFEFF47D, 15); step (&f4, b, c, d, a, block[1] + 0x85845DD1, 21); step (&f4, a, b, c, d, block[8] + 0x6FA87E4F, 6); step (&f4, d, a, b, c, block[15] + 0xFE2CE6E0, 10); step (&f4, c, d, a, b, block[6] + 0xA3014314, 15); step (&f4, b, c, d, a, block[13] + 0x4E0811A1, 21); step (&f4, a, b, c, d, block[4] + 0xF7537E82, 6); step (&f4, d, a, b, c, block[11] + 0xBD3AF235, 10); step (&f4, c, d, a, b, block[2] + 0x2AD7D2BB, 15); step (&f4, b, c, d, a, block[9] + 0xEB86D391, 21); (cast(uint*)Sum)[0] += a; (cast(uint*)Sum)[1] += b; (cast(uint*)Sum)[2] += c; (cast(uint*)Sum)[3] += d; } unittest { struct testcase { char[] message; char[SUMSIZE] digest; }; static testcase testcases[] = [ { "", [ 0xD4,0x1D,0x8C,0xD9,0x8F,0x00,0xB2,0x04,0xE9,0x80,0x09,0x98,0xEC,0xF8,0x42,0x7E ] }, { "a", [ 0x0C,0xC1,0x75,0xB9,0xC0,0xF1,0xB6,0xA8,0x31,0xC3,0x99,0xE2,0x69,0x77,0x26,0x61 ] }, { "abc", [ 0x90,0x01,0x50,0x98,0x3C,0xD2,0x4F,0xB0,0xD6,0x96,0x3F,0x7D,0x28,0xE1,0x7F,0x72 ] }, { "message digest", [ 0xF9,0x6B,0x69,0x7D,0x7C,0xB7,0x93,0x8D,0x52,0x5A,0x2F,0x31,0xAA,0xF1,0x61,0xD0 ] }, { "abcdefghijklmnopqrstuvwxyz", [ 0xC3,0xFC,0xD3,0xD7,0x61,0x92,0xE4,0x00,0x7D,0xFB,0x49,0x6C,0xCA,0x67,0xE1,0x3B ] }, { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", [ 0xD1,0x74,0xAB,0x98,0xD2,0x77,0xD9,0xF5,0xA5,0x61,0x1C,0x2C,0x9F,0x41,0x9D,0x9F ] }, { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", [ 0x57,0xED,0xF4,0xA2,0x2B,0xE3,0xC9,0x55,0xAC,0x49,0xDA,0x2E,0x21,0x07,0xB6,0x7A ] } ]; Md5 md5 = new Md5; uint testno; foreach (testcase test; testcases) { md5.reset(); md5.add(test.message,test.message.length); for (uint i=0; i < SUMSIZE; ++i) { assert (md5.sum()[i] == test.digest[i]); } } } public: this(ubyte /*const*/* seed=null, uint added=0) { reset(seed,added); } void reset(ubyte /*const*/* seed=null, uint added=0) { if (seed == null) seed = Init; Sum[] = seed[0..SUMSIZE]; BytesAdded = added; BufferUsed = 0; } void add(void /*const*/* mem, uint size, bool done=true) { ubyte* msg = cast(ubyte*)mem; uint amount; /* add to total */ BytesAdded += size; /* complete any previous blocks */ if (BufferUsed != 0) { if (size < BLOCKSIZE-BufferUsed) { Buffer[BufferUsed..BufferUsed+size] = msg[0..size]; BufferUsed += size; } else { amount = BLOCKSIZE - BufferUsed; Buffer[BufferUsed..BLOCKSIZE] = msg[0..amount]; transformBlock(Buffer); BufferUsed = 0; size -= amount; } } /* do the calculation on each full block (aligned or misaligned) */ for (; size >= BLOCKSIZE; size-=BLOCKSIZE,msg+=BLOCKSIZE) { transformBlock(msg); } /* copy any remaining data to holding buffer */ if (size != 0) { Buffer[0..size] = msg[0..size]; BufferUsed = size; } /* if we're all done: add padding and length field */ if (done) { /* add terminating character (buffer always has at least one byte free) */ Buffer[BufferUsed++] = 0x80; if (BufferUsed > BLOCKSIZE - LENGTHSIZE) { /* need an extra block for size */ Buffer[BufferUsed..BLOCKSIZE] = 0; transformBlock(Buffer); BufferUsed = 0; } /* add padding and length */ Buffer[BufferUsed..BLOCKSIZE] = 0; /* add little-endian length (in bits) */ Buffer[BLOCKSIZE-LENGTHSIZE+0] = BytesAdded << 3; Buffer[BLOCKSIZE-LENGTHSIZE+1] = BytesAdded >> ( 8 - 3); Buffer[BLOCKSIZE-LENGTHSIZE+2] = BytesAdded >> (16 - 3); Buffer[BLOCKSIZE-LENGTHSIZE+3] = BytesAdded >> (24 - 3); Buffer[BLOCKSIZE-LENGTHSIZE+4] = BytesAdded >> (32 - 3); // Buffer[BLOCKSIZE-LENGTHSIZE+5] = 0; // Buffer[BLOCKSIZE-LENGTHSIZE+6] = 0; // already zero'd // Buffer[BLOCKSIZE-LENGTHSIZE+7] = 0; /* include final block */ transformBlock(Buffer); /* clear any sensitive information */ Buffer[] = 0; } } ubyte[] sum() { version(LittleEndian) { return Sum[]; } version(BigEndian) { ubyte[SUMSIZE] localsum = Sum[]; byteSwap(cast(uint*)localsum,SUMSIZE/4); return localsum; } } }