PageRenderTime 54ms CodeModel.GetById 2ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 1ms

/std/md5.d

http://github.com/jcd/phobos
D | 493 lines | 280 code | 64 blank | 149 comment | 14 complexity | 4b14071076a7ed3f28f22aed0fd927a2 MD5 | raw file
  1// Written in the D programming language.
  2
  3/* md5.d - RSA Data Security, Inc., MD5 message-digest algorithm
  4 * Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm.
  5 */
  6
  7/**
  8 * Computes MD5 digests of arbitrary data. MD5 digests are 16 byte quantities that are like a checksum or crc, but are more robust.
  9 *
 10 * There are two ways to do this. The first does it all in one function call to
 11 * sum(). The second is for when the data is buffered.
 12 *
 13 * Bugs:
 14 * MD5 digests have been demonstrated to not be unique.
 15 *
 16 * Author:
 17 * The routines and algorithms are derived from the
 18 * $(I RSA Data Security, Inc. MD5 Message-Digest Algorithm).
 19 *
 20 * References:
 21 *      $(LINK2 http://en.wikipedia.org/wiki/Md5, Wikipedia on MD5)
 22 *
 23 * Source: $(PHOBOSSRC std/_md5.d)
 24 *
 25 * Macros:
 26 *      WIKI = Phobos/StdMd5
 27 */
 28
 29/++++++++++++++++++++++++++++++++
 30 Example:
 31
 32--------------------
 33// This code is derived from the
 34// RSA Data Security, Inc. MD5 Message-Digest Algorithm.
 35
 36import std.md5;
 37import std.stdio;
 38
 39void main(string[] args)
 40{
 41    foreach (arg; args)
 42        mdFile(arg);
 43}
 44
 45/// Digests a file and prints the result.
 46void mdFile(string filename)
 47{
 48    ubyte[16] digest;
 49
 50    MD5_CTX context;
 51    context.start();
 52    foreach (buffer; File(filename).byChunk(4096 * 1024))
 53        context.update(buffer);
 54    context.finish(digest);
 55    writefln("MD5 (%s) = %s", filename, digestToString(digest));
 56}
 57--------------------
 58 +/
 59
 60/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
 61rights reserved.
 62
 63License to copy and use this software is granted provided that it
 64is identified as the "RSA Data Security, Inc. MD5 Message-Digest
 65Algorithm" in all material mentioning or referencing this software
 66or this function.
 67
 68License is also granted to make and use derivative works provided
 69that such works are identified as "derived from the RSA Data
 70Security, Inc. MD5 Message-Digest Algorithm" in all material
 71mentioning or referencing the derived work.
 72
 73RSA Data Security, Inc. makes no representations concerning either
 74the merchantability of this software or the suitability of this
 75software for any particular purpose. It is provided "as is"
 76without express or implied warranty of any kind.
 77These notices must be retained in any copies of any part of this
 78documentation and/or software.
 79 */
 80
 81module std.md5;
 82
 83//debug=md5;            // uncomment to turn on debugging printf's
 84
 85import std.ascii;
 86import std.string;
 87import std.exception;
 88debug(md5) import std.c.stdio : printf;
 89
 90/***************************************
 91 * Computes MD5 digest of several arrays of data.
 92 */
 93
 94void sum(ref ubyte[16] digest, in void[][] data...)
 95{
 96    MD5_CTX context;
 97    context.start();
 98    foreach (datum; data)
 99    {
100        context.update(datum);
101    }
102    context.finish(digest);
103}
104
105// /******************
106//  * Prints a message digest in hexadecimal to stdout.
107//  */
108// void printDigest(const ubyte digest[16])
109// {
110//     foreach (ubyte u; digest)
111//         printf("%02x", u);
112// }
113
114/****************************************
115 * Converts MD5 digest to a string.
116 */
117
118string digestToString(in ubyte[16] digest)
119{
120    auto result = new char[32];
121    int i;
122
123    foreach (ubyte u; digest)
124    {
125        result[i] = std.ascii.hexDigits[u >> 4];
126        result[i + 1] = std.ascii.hexDigits[u & 15];
127        i += 2;
128    }
129    return assumeUnique(result);
130}
131
132/**
133   Gets the digest of all $(D data) items passed in.
134
135Example:
136
137----
138string a = "Mary has ", b = "a little lamb";
139int[] c = [ 1, 2, 3, 4, 5 ];
140string d = getDigestString(a, b, c);
141----
142*/
143string getDigestString(in void[][] data...)
144{
145    MD5_CTX ctx;
146    ctx.start();
147    foreach (datum; data) {
148        ctx.update(datum);
149    }
150    ubyte[16] digest;
151    ctx.finish(digest);
152    return digestToString(digest);
153}
154
155version(unittest) import std.stdio;
156unittest
157{
158    string a = "Mary has ", b = "a little lamb";
159    int[] c = [ 1, 2, 3, 4, 5 ];
160    string d = getDigestString(a, b, c);
161    assert(d == "F36625A66B2A8D9F47270C00C8BEFD2F", d);
162}
163
164/**
165 * Holds context of MD5 computation.
166 *
167 * Used when data to be digested is buffered.
168 */
169struct MD5_CTX
170{
171    uint state[4] =                                   /* state (ABCD) */
172    /* magic initialization constants */
173    [0x67452301,0xefcdab89,0x98badcfe,0x10325476];
174
175    ulong count;        /* number of bits, modulo 2^64 */
176    ubyte buffer[64];   /* input buffer */
177
178    static ubyte[64] PADDING =
179    [
180      0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
183    ];
184
185    /* F, G, H and I are basic MD5 functions.
186     */
187    private static
188    {
189        uint F(uint x, uint y, uint z) { return (x & y) | (~x & z); }
190        uint G(uint x, uint y, uint z) { return (x & z) | (y & ~z); }
191        uint H(uint x, uint y, uint z) { return x ^ y ^ z; }
192        uint I(uint x, uint y, uint z) { return y ^ (x | ~z); }
193    }
194
195    /* ROTATE_LEFT rotates x left n bits.
196     */
197    static uint ROTATE_LEFT(uint x, uint n)
198    {
199        // With recently added optimization to DMD (commit 32ea0206 at 07/28/11), this is translated to rol.
200        // No assembler required.
201        return (x << n) | (x >> (32-n));
202    }
203
204    /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
205     * Rotation is separate from addition to prevent recomputation.
206     */
207    static void FF(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
208    {
209        a += F (b, c, d) + x + cast(uint)(ac);
210        a = ROTATE_LEFT (a, s);
211        a += b;
212    }
213
214    static void GG(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
215    {
216        a += G (b, c, d) + x + cast(uint)(ac);
217        a = ROTATE_LEFT (a, s);
218        a += b;
219    }
220
221    static void HH(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
222    {
223        a += H (b, c, d) + x + cast(uint)(ac);
224        a = ROTATE_LEFT (a, s);
225        a += b;
226    }
227
228    static void II(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
229    {
230        a += I (b, c, d) + x + cast(uint)(ac);
231        a = ROTATE_LEFT (a, s);
232        a += b;
233    }
234
235    /**
236     * MD5 initialization. Begins an MD5 operation, writing a new context.
237     */
238    void start()
239    {
240        this = MD5_CTX.init;
241    }
242
243    /** MD5 block update operation. Continues an MD5 message-digest
244      operation, processing another message block, and updating the
245      context.
246     */
247    void update(const void[] input)
248    {
249      uint i, index, partLen;
250      auto inputLen = input.length;
251
252      /* Compute number of bytes mod 64 */
253      index = (cast(uint)count >> 3) & (64 - 1);
254
255      /* Update number of bits */
256      count += inputLen * 8;
257
258      partLen = 64 - index;
259
260      /* Transform as many times as possible. */
261      if (inputLen >= partLen)
262      {
263            std.c.string.memcpy(&buffer[index], input.ptr, partLen);
264            transform (buffer.ptr);
265
266            for (i = partLen; i + 63 < inputLen; i += 64)
267               transform ((cast(ubyte[])input)[i .. i + 64].ptr);
268
269            index = 0;
270      }
271      else
272            i = 0;
273
274      /* Buffer remaining input */
275      if (inputLen - i)
276            std.c.string.memcpy(&buffer[index], &input[i], inputLen-i);
277    }
278
279    /** MD5 finalization. Ends an MD5 message-digest operation, writing the
280     * the message to digest and zeroing the context.
281     */
282    void finish(ref ubyte[16] digest)         /* message digest */
283    {
284      ubyte bits[8] = void;
285      uint index, padLen;
286
287      /* Save number of bits */
288      Encode (bits.ptr, cast(const uint*) &count, 8);
289
290      /* Pad out to 56 mod 64. */
291      index = (cast(uint)count >> 3) & (64 - 1);
292      padLen = (index < 56) ? (56 - index) : (120 - index);
293      update (PADDING[0 .. padLen]);
294
295      /* Append length (before padding) */
296      update (bits);
297
298      /* Store state in digest */
299      Encode (digest.ptr, state.ptr, 16);
300
301      /* Zeroize sensitive information. */
302      std.c.string.memset (&this, 0, MD5_CTX.sizeof);
303    }
304
305    /* MD5 basic transformation. Transforms state based on block.
306     */
307
308    /* Constants for MD5Transform routine. */
309    enum
310    {
311        S11 = 7,
312        S12 = 12,
313        S13 = 17,
314        S14 = 22,
315        S21 = 5,
316        S22 = 9,
317        S23 = 14,
318        S24 = 20,
319        S31 = 4,
320        S32 = 11,
321        S33 = 16,
322        S34 = 23,
323        S41 = 6,
324        S42 = 10,
325        S43 = 15,
326        S44 = 21,
327    }
328
329    private void transform (const ubyte* /*[64]*/ block)
330    {
331      uint a = state[0],
332           b = state[1],
333           c = state[2],
334           d = state[3];
335      uint[16] x = void;
336
337      Decode (x.ptr, block, 64);
338
339      /* Round 1 */
340      FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
341      FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
342      FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
343      FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
344      FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
345      FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
346      FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
347      FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
348      FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
349      FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
350      FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
351      FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
352      FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
353      FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
354      FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
355      FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
356
357     /* Round 2 */
358      GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
359      GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
360      GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
361      GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
362      GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
363      GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
364      GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
365      GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
366      GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
367      GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
368      GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
369      GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
370      GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
371      GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
372      GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
373      GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
374
375      /* Round 3 */
376      HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
377      HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
378      HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
379      HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
380      HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
381      HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
382      HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
383      HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
384      HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
385      HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
386      HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
387      HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
388      HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
389      HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
390      HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
391      HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
392
393      /* Round 4 */
394      II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
395      II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
396      II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
397      II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
398      II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
399      II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
400      II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
401      II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
402      II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
403      II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
404      II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
405      II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
406      II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
407      II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
408      II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
409      II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
410
411      state[0] += a;
412      state[1] += b;
413      state[2] += c;
414      state[3] += d;
415
416      /* Zeroize sensitive information. */
417      x[] = 0;
418    }
419
420    /* Encodes input (uint) into output (ubyte). Assumes len is
421      a multiple of 4.
422     */
423    private static void Encode (ubyte *output, const uint *input, uint len)
424    {
425        version (BigEndian)
426        {
427            uint i, j;
428
429            for (i = 0, j = 0; j < len; i++, j += 4)
430            {
431                *cast(uint *) &output[j] = core.bitop.bswap(input[i]);
432            }
433        }
434        else
435        {
436            (cast(uint *)output)[0..len/4] = input[0..len/4];
437        }
438    }
439
440    /* Decodes input (ubyte) into output (uint). Assumes len is
441      a multiple of 4.
442     */
443    private static void Decode (uint *output, const ubyte *input, uint len)
444    {
445        version (BigEndian)
446        {
447            uint i, j;
448
449            for (i = 0, j = 0; j < len; i++, j += 4)
450            {
451                output[i] = core.bitop.bswap(*cast(uint*)&input[j]);
452            }
453        }
454        else
455        {
456            output[0..len/4] = (cast(const uint *)input)[0..len/4];
457        }
458    }
459}
460
461unittest
462{
463    debug(md5) printf("std.md5.unittest\n");
464
465    ubyte[16] digest;
466
467    sum (digest, "");
468    assert(digest == cast(ubyte[])x"d41d8cd98f00b204e9800998ecf8427e");
469
470    sum (digest, "a");
471    assert(digest == cast(ubyte[])x"0cc175b9c0f1b6a831c399e269772661");
472
473    sum (digest, "abc");
474    assert(digest == cast(ubyte[])x"900150983cd24fb0d6963f7d28e17f72");
475
476    sum (digest, "message digest");
477    assert(digest == cast(ubyte[])x"f96b697d7cb7938d525a2f31aaf161d0");
478
479    sum (digest, "abcdefghijklmnopqrstuvwxyz");
480    assert(digest == cast(ubyte[])x"c3fcd3d76192e4007dfb496cca67e13b");
481
482    sum (digest, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
483    assert(digest == cast(ubyte[])x"d174ab98d277d9f5a5611c2c9f419d9f");
484
485    sum (digest,
486        "1234567890123456789012345678901234567890"
487        "1234567890123456789012345678901234567890");
488    assert(digest == cast(ubyte[])x"57edf4a22be3c955ac49da2e2107b67a");
489
490    assert(digestToString(cast(ubyte[16])x"c3fcd3d76192e4007dfb496cca67e13b")
491        == "C3FCD3D76192E4007DFB496CCA67E13B");
492}
493