Mercurial > projects > dcrypt
diff dcrypt/crypto/hashes/MD4.d @ 26:176c933827a8
Implemented MD4. Refactored MD5. Replaced all instances of 'version (UnitTest)' with 'debug (UnitTest)'.
author | Thomas Dixon <reikon@reikon.us> |
---|---|
date | Sun, 01 Mar 2009 13:06:48 -0500 |
parents | |
children | 8b5eaf3c2979 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dcrypt/crypto/hashes/MD4.d Sun Mar 01 13:06:48 2009 -0500 @@ -0,0 +1,219 @@ +/** + * This file is part of the dcrypt project. + * + * Copyright: Copyright (C) dcrypt contributors 2009. All rights reserved. + * License: MIT + * Authors: Thomas Dixon + */ + +module dcrypt.crypto.hashes.MD4; + +public import dcrypt.crypto.Hash; + +/** + * Implementation of Ron Rivest's MD4. + * + * Conforms: RFC 1320 + * References: http://www.faqs.org/rfcs/rfc1320.html + * Bugs: MD4 is not cryptographically secure. + */ +class MD4 : Hash { + private uint h0, h1, h2, h3; + + // Shift amounts + private enum { + S11 = 3, + S12 = 7, + S13 = 11, + S14 = 19, + + S21 = 3, + S22 = 5, + S23 = 9, + S24 = 13, + + S31 = 3, + S32 = 9, + S33 = 11, + S34 = 15 + }; + + this (void[] input_=null) { + reset(); + super(input_); + } + + uint blockSize() { + return 64; + } + + uint digestSize() { + return 16; + } + + char[] name() { + return "MD4"; + } + + void transform(ubyte[] input) { + uint[] w = new uint[16]; + + for (int i = 0, j = 0; i < 16; i++,j+=int.sizeof) + w[i] = ByteConverter.LittleEndian.to!(uint)(input[j..j+int.sizeof]); + + uint a = h0, + b = h1, + c = h2, + d = h3; + + // Round 1 + ff(a, b, c, d, w[ 0], S11); + ff(d, a, b, c, w[ 1], S12); + ff(c, d, a, b, w[ 2], S13); + ff(b, c, d, a, w[ 3], S14); + ff(a, b, c, d, w[ 4], S11); + ff(d, a, b, c, w[ 5], S12); + ff(c, d, a, b, w[ 6], S13); + ff(b, c, d, a, w[ 7], S14); + ff(a, b, c, d, w[ 8], S11); + ff(d, a, b, c, w[ 9], S12); + ff(c, d, a, b, w[10], S13); + ff(b, c, d, a, w[11], S14); + ff(a, b, c, d, w[12], S11); + ff(d, a, b, c, w[13], S12); + ff(c, d, a, b, w[14], S13); + ff(b, c, d, a, w[15], S14); + + // Round 2 + gg(a, b, c, d, w[ 0], S21); + gg(d, a, b, c, w[ 4], S22); + gg(c, d, a, b, w[ 8], S23); + gg(b, c, d, a, w[12], S24); + gg(a, b, c, d, w[ 1], S21); + gg(d, a, b, c, w[ 5], S22); + gg(c, d, a, b, w[ 9], S23); + gg(b, c, d, a, w[13], S24); + gg(a, b, c, d, w[ 2], S21); + gg(d, a, b, c, w[ 6], S22); + gg(c, d, a, b, w[10], S23); + gg(b, c, d, a, w[14], S24); + gg(a, b, c, d, w[ 3], S21); + gg(d, a, b, c, w[ 7], S22); + gg(c, d, a, b, w[11], S23); + gg(b, c, d, a, w[15], S24); + + // Round 3 + hh(a, b, c, d, w[ 0], S31); + hh(d, a, b, c, w[ 8], S32); + hh(c, d, a, b, w[ 4], S33); + hh(b, c, d, a, w[12], S34); + hh(a, b, c, d, w[ 2], S31); + hh(d, a, b, c, w[10], S32); + hh(c, d, a, b, w[ 6], S33); + hh(b, c, d, a, w[14], S34); + hh(a, b, c, d, w[ 1], S31); + hh(d, a, b, c, w[ 9], S32); + hh(c, d, a, b, w[ 5], S33); + hh(b, c, d, a, w[13], S34); + hh(a, b, c, d, w[ 3], S31); + hh(d, a, b, c, w[11], S32); + hh(c, d, a, b, w[ 7], S33); + hh(b, c, d, a, w[15], S34); + + h0 += a; + h1 += b; + h2 += c; + h3 += d; + } + + private uint f(uint x, uint y, uint z) { + return (x&y)|(~x&z); + } + + private uint h(uint x, uint y, uint z) { + return x^y^z; + } + + private uint g(uint x, uint y, uint z) { + return (x&y)|(x&z)|(y&z); + } + + private void ff(ref uint a, uint b, uint c, uint d, uint x, uint s) { + a += f(b, c, d) + x; + a = Bitwise.rotateLeft(a, s); + } + + private void gg(ref uint a, uint b, uint c, uint d, uint x, uint s) { + a += g(b, c, d) + x + 0x5a827999u; + a = Bitwise.rotateLeft(a, s); + } + + private void hh(ref uint a, uint b, uint c, uint d, uint x, uint s) { + a += h(b, c, d) + x + 0x6ed9eba1u; + a = Bitwise.rotateLeft(a, s); + } + + ubyte[] digest() { + padMessage(MODE_MD); + ubyte[] result = new ubyte[digestSize]; + + result[0..4] = ByteConverter.LittleEndian.from!(uint)(h0); + result[4..8] = ByteConverter.LittleEndian.from!(uint)(h1); + result[8..12] = ByteConverter.LittleEndian.from!(uint)(h2); + result[12..16] = ByteConverter.LittleEndian.from!(uint)(h3); + + reset(); + return result; + } + + void reset() { + super.reset(); + h0 = 0x67452301u; + h1 = 0xefcdab89u; + h2 = 0x98badcfeu; + h3 = 0x10325476u; + } + + MD4 copy() { + MD4 h = new MD4(buffer[0..index]); + h.bytes = bytes; + h.h0 = h0; + h.h1 = h1; + h.h2 = h2; + h.h3 = h3; + return h; + } + + debug (UnitTest) { + // Found in Tango <3 + unittest { + static const char[][] test_inputs = [ + "", + "a", + "abc", + "message digest", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" + ]; + + static const char[][] test_results = [ + "31d6cfe0d16ae931b73c59d7e0c089c0", + "bde52cb31de33e46245e05fbdbd6fb24", + "a448017aaf21d8525fc10ae87aa6729d", + "d9130a8164549fe818874806e1c7014b", + "d79e1c308aa5bbcdeea8ed63df412da9", + "043f8582f241db351ce627e153e7f0e4", + "e33b4ddc9c38f2199c3e7b164fcc0536" + ]; + + MD4 h = new MD4(); + foreach (uint i, char[] input; test_inputs) { + h.update(input); + char[] digest = h.hexDigest(); + assert(digest == test_results[i], + h.name~": ("~digest~") != ("~test_results[i]~")"); + } + } + } +}