import sequtils const ChunkSize = 512 div 8 SumSize = 128 div 8 proc extractChunk(msg : seq[uint8], chunk: var openarray[uint32], offset: int) = var srcIndex = offset for dstIndex in 0 .. < 16: chunk[dstIndex] = 0 for ii in 0 .. < 4: chunk[dstIndex] = chunk[dstIndex] shr 8 chunk[dstIndex] = chunk[dstIndex] or (msg[srcIndex].uint32 shl 24) srcIndex.inc proc leftRotate(val: uint32, shift: int) : uint32 = result = (val shl shift) or (val shr (32 - shift)) proc md5Sum(msg : seq[uint8]) : array[SumSize, uint8] = const s : array[ChunkSize, int] = [ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 ] K : array[ChunkSize, uint32] = [ 0xd76aa478'u32, 0xe8c7b756'u32, 0x242070db'u32, 0xc1bdceee'u32, 0xf57c0faf'u32, 0x4787c62a'u32, 0xa8304613'u32, 0xfd469501'u32, 0x698098d8'u32, 0x8b44f7af'u32, 0xffff5bb1'u32, 0x895cd7be'u32, 0x6b901122'u32, 0xfd987193'u32, 0xa679438e'u32, 0x49b40821'u32, 0xf61e2562'u32, 0xc040b340'u32, 0x265e5a51'u32, 0xe9b6c7aa'u32, 0xd62f105d'u32, 0x02441453'u32, 0xd8a1e681'u32, 0xe7d3fbc8'u32, 0x21e1cde6'u32, 0xc33707d6'u32, 0xf4d50d87'u32, 0x455a14ed'u32, 0xa9e3e905'u32, 0xfcefa3f8'u32, 0x676f02d9'u32, 0x8d2a4c8a'u32, 0xfffa3942'u32, 0x8771f681'u32, 0x6d9d6122'u32, 0xfde5380c'u32, 0xa4beea44'u32, 0x4bdecfa9'u32, 0xf6bb4b60'u32, 0xbebfbc70'u32, 0x289b7ec6'u32, 0xeaa127fa'u32, 0xd4ef3085'u32, 0x04881d05'u32, 0xd9d4d039'u32, 0xe6db99e5'u32, 0x1fa27cf8'u32, 0xc4ac5665'u32, 0xf4292244'u32, 0x432aff97'u32, 0xab9423a7'u32, 0xfc93a039'u32, 0x655b59c3'u32, 0x8f0ccc92'u32, 0xffeff47d'u32, 0x85845dd1'u32, 0x6fa87e4f'u32, 0xfe2ce6e0'u32, 0xa3014314'u32, 0x4e0811a1'u32, 0xf7537e82'u32, 0xbd3af235'u32, 0x2ad7d2bb'u32, 0xeb86d391'u32 ] # Pad with 1-bit, and fill with 0's up to 448 bits mod 512 var paddedMsgSize = msg.len + 1 var remain = (msg.len + 1) mod ChunkSize if remain > (448 div 8): paddedMsgSize += ChunkSize - remain + (448 div 8) else: paddedMsgSize += (448 div 8) - remain var paddingSize = paddedMsgSize - msg.len var padding = newSeq[uint8](paddingSize) padding[0] = 0x80 # Pad with number of *bits* in original message, little-endian var sizePadding = newSeq[uint8](8) var size = msg.len * 8 for ii in 0 .. < 4: sizePadding[ii] = uint8(size and 0xff) size = size shr 8 var paddedMsg = concat(msg, padding, sizePadding) var accum = [ 0x67452301'u32, 0xefcdab89'u32, 0x98badcfe'u32, 0x10325476'u32 ] for offset in countup(0, paddedMsg.len - 1, ChunkSize): var A = accum[0] var B = accum[1] var C = accum[2] var D = accum[3] var F : uint32 var g : int var M : array[16, uint32] var dTemp : uint32 extractChunk(paddedMsg, M, offset) # This is pretty much the same as Wikipedia's MD5 entry for ii in 0 .. 63: if ii <= 15: F = (B and C) or ((not B) and D) g = ii elif ii <= 31: F = (D and B) or ((not D) and C) g = (5 * ii + 1) mod 16 elif ii <= 47: F = B xor C xor D g = (3 * ii + 5) mod 16 else: F = C xor (B or (not D)) g = (7 * ii) mod 16 dTemp = D D = C C = B B = B + leftRotate((A + F + K[ii] + M[g]), s[ii]) A = dTemp accum[0] += A accum[1] += B accum[2] += C accum[3] += D # Convert four 32-bit accumulators to 16 byte array, little-endian var dstIdx : int for acc in accum: var tmp = acc for ii in 0 .. < 4: result[dstIdx] = uint8(tmp and 0xff) tmp = tmp shr 8 dstIdx.inc # Only needed to convert from string to uint8 sequence iterator items * (str : string) : uint8 = for ii in 0 .. < len(str): yield str[ii].uint8 proc main = var msg = "" var sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0xD4'u8, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E ] ) msg = "The quick brown fox jumps over the lazy dog" sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0x9E'u8, 0x10, 0x7D, 0x9D, 0x37, 0x2B, 0xB6, 0x82, 0x6B, 0xD8, 0x1D, 0x35, 0x42, 0xA4, 0x19, 0xD6 ] ) msg = "The quick brown fox jumps over the lazy dog." sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0xE4'u8, 0xD9, 0x09, 0xC2, 0x90, 0xD0, 0xFB, 0x1C, 0xA0, 0x68, 0xFF, 0xAD, 0xDF, 0x22, 0xCB, 0xD0 ]) # Message size around magic 512 bits msg = "01234567890123456789012345678901234567890123456789012345678901234" sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0xBE'u8, 0xB9, 0xF4, 0x8B, 0xC8, 0x02, 0xCA, 0x5C, 0xA0, 0x43, 0xBC, 0xC1, 0x5E, 0x21, 0x9A, 0x5A ]) msg = "0123456789012345678901234567890123456789012345678901234567890123" sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0x7F'u8, 0x7B, 0xFD, 0x34, 0x87, 0x09, 0xDE, 0xEA, 0xAC, 0xE1, 0x9E, 0x3F, 0x53, 0x5F, 0x8C, 0x54 ]) msg = "012345678901234567890123456789012345678901234567890123456789012" sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0xC5'u8, 0xE2, 0x56, 0x43, 0x7E, 0x75, 0x80, 0x92, 0xDB, 0xFE, 0x06, 0x28, 0x3E, 0x48, 0x90, 0x19 ]) # Message size around magic 448 bits msg = "01234567890123456789012345678901234567890123456789012345" sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0x8A'u8, 0xF2, 0x70, 0xB2, 0x84, 0x76, 0x10, 0xE7, 0x42, 0xB0, 0x79, 0x1B, 0x53, 0x64, 0x8C, 0x09 ]) msg = "0123456789012345678901234567890123456789012345678901234" sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0x6E'u8, 0x7A, 0x4F, 0xC9, 0x2E, 0xB1, 0xC3, 0xF6, 0xE6, 0x52, 0x42, 0x5B, 0xCC, 0x8D, 0x44, 0xB5 ]) msg = "012345678901234567890123456789012345678901234567890123" sum = md5Sum(toSeq(msg.items())) assert(sum == [ 0x3D'u8, 0xFF, 0x83, 0xC8, 0xFA, 0xDD, 0x26, 0x37, 0x0D, 0x5B, 0x09, 0x84, 0x09, 0x64, 0x44, 0x57 ]) main()