|
| 1 | +// Module that replicates the MD5 Cryptographic Hash |
| 2 | +// function in Javascript. |
| 3 | + |
| 4 | +// main variables |
| 5 | +constS=[ |
| 6 | +7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5, |
| 7 | +9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11, |
| 8 | +16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15, |
| 9 | +21 |
| 10 | +] |
| 11 | + |
| 12 | +constK=[ |
| 13 | +0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a, |
| 14 | +0xa8304613,0xfd469501,0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be, |
| 15 | +0x6b901122,0xfd987193,0xa679438e,0x49b40821,0xf61e2562,0xc040b340, |
| 16 | +0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8, |
| 17 | +0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8, |
| 18 | +0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c, |
| 19 | +0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,0x289b7ec6,0xeaa127fa, |
| 20 | +0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665, |
| 21 | +0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92, |
| 22 | +0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1, |
| 23 | +0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391 |
| 24 | +] |
| 25 | + |
| 26 | +/** |
| 27 | + * Separates an array into equal sized chunks |
| 28 | + * |
| 29 | + *@param {Array|string} array - array or string to separate into chunks |
| 30 | + *@param {number} size - number of elements wanted in each chunk |
| 31 | + *@return {Array} - array of original array split into chunks |
| 32 | + * |
| 33 | + *@example |
| 34 | + * chunkify("this is a test", 2) |
| 35 | + */ |
| 36 | +functionchunkify(array,size){ |
| 37 | +constchunks=[] |
| 38 | +for(leti=0;i<array.length;i+=size){ |
| 39 | +chunks.push(array.slice(i,i+size)) |
| 40 | +} |
| 41 | +returnchunks |
| 42 | +} |
| 43 | + |
| 44 | +/** |
| 45 | + * Rotates the bits to the left |
| 46 | + * |
| 47 | + *@param {number} bits - 32 bit number |
| 48 | + *@param {number} turns - number of rotations to make |
| 49 | + *@return {number} - number after bits rotation |
| 50 | + * |
| 51 | + *@example |
| 52 | + * rotateLeft(0b1011, 3); // 0b1011000 |
| 53 | + */ |
| 54 | +functionrotateLeft(bits,turns){ |
| 55 | +return(bits<<turns)|(bits>>>(32-turns)) |
| 56 | +} |
| 57 | + |
| 58 | +/** |
| 59 | + * Converts Uint8Array to Uint32Array |
| 60 | + * |
| 61 | + *@param {Uint8Array} u8Array Uint8Array to convert |
| 62 | + *@returns {Uint32Array} - Required Uint32Array |
| 63 | + */ |
| 64 | +functionu8ToU32(u8Array){ |
| 65 | +constuint32Array=newUint32Array(u8Array.length/4) |
| 66 | + |
| 67 | +for(leti=0;i<u8Array.length;i+=4){ |
| 68 | +uint32Array[i/4]= |
| 69 | +(u8Array[i]| |
| 70 | +(u8Array[i+1]<<8)| |
| 71 | +(u8Array[i+2]<<16)| |
| 72 | +(u8Array[i+3]<<24))>>> |
| 73 | +0 |
| 74 | +} |
| 75 | + |
| 76 | +returnuint32Array |
| 77 | +} |
| 78 | + |
| 79 | +/** |
| 80 | + * Converts Uint32Array to Uint8Array |
| 81 | + * |
| 82 | + *@param {Uint32Array} u32Array Uint32Array to convert |
| 83 | + *@returns {Uint8Array} - Required Uint8Array |
| 84 | + */ |
| 85 | +functionu32ToU8(u32Array){ |
| 86 | +constuint8Array=newUint8Array(u32Array.length*4) |
| 87 | + |
| 88 | +for(leti=0;i<u32Array.length;i++){ |
| 89 | +uint8Array[i*4]=u32Array[i]&0xff |
| 90 | +uint8Array[i*4+1]=(u32Array[i]>>8)&0xff |
| 91 | +uint8Array[i*4+2]=(u32Array[i]>>16)&0xff |
| 92 | +uint8Array[i*4+3]=(u32Array[i]>>24)&0xff |
| 93 | +} |
| 94 | + |
| 95 | +returnuint8Array |
| 96 | +} |
| 97 | + |
| 98 | +/** |
| 99 | + * Adds padding to the end of the given array |
| 100 | + * |
| 101 | + *@param {Uint8Array} u8Array Array to pad |
| 102 | + *@param {number} size Resulting size of the array |
| 103 | + */ |
| 104 | +functionpadEnd(u8Array,size){ |
| 105 | +constresult=newUint8Array(size) |
| 106 | +result.set(u8Array) |
| 107 | +result.fill(0,u8Array.length) |
| 108 | + |
| 109 | +returnresult |
| 110 | +} |
| 111 | + |
| 112 | +/** |
| 113 | + * Pre-processes message to feed the algorithm loop |
| 114 | + * |
| 115 | + *@param {Uint8Array} message - message to pre-process |
| 116 | + *@return {Uint32Array} - processed message |
| 117 | + */ |
| 118 | +functionpreProcess(message){ |
| 119 | +// Extend message by adding '0' |
| 120 | +// |
| 121 | +// message.length + 1 is for adding '1' bit |
| 122 | +// 56 - (length % 64) is for padding with '0's |
| 123 | +// 8 is for appending 64 bit message length |
| 124 | +letm=padEnd( |
| 125 | +message, |
| 126 | +message.length+1+(56-((message.length+1)%64))+8 |
| 127 | +) |
| 128 | + |
| 129 | +// Add '1' bit at the end of the message |
| 130 | +m[message.length]=1<<7 |
| 131 | + |
| 132 | +// convert message to 32 bit uint array |
| 133 | +m=u8ToU32(m) |
| 134 | + |
| 135 | +// Append the length of the message to the end |
| 136 | +// (ml / 0x100000000) | 0 is equivalent to (ml >> 32) & 0xffffffff) in other languages |
| 137 | +letml=message.length*8 |
| 138 | +m[m.length-2]=ml&0xffffffff |
| 139 | +m[m.length-1]=(ml/0x100000000)|0 |
| 140 | + |
| 141 | +returnm |
| 142 | +} |
| 143 | + |
| 144 | +/** |
| 145 | + * Hashes message using MD5 Cryptographic Hash Function |
| 146 | + * |
| 147 | + *@see |
| 148 | + * For more info: https://en.wikipedia.org/wiki/MD5 |
| 149 | + * |
| 150 | + *@param {Uint8Array} message - message to hash |
| 151 | + *@return {Uint8Array} - message digest (hash value) |
| 152 | + */ |
| 153 | +functionMD5(message){ |
| 154 | +// Initialize variables: |
| 155 | +let[a0,b0,c0,d0]=[ |
| 156 | +0x67452301>>>0, |
| 157 | +0xefcdab89>>>0, |
| 158 | +0x98badcfe>>>0, |
| 159 | +0x10325476>>>0 |
| 160 | +] |
| 161 | + |
| 162 | +// pre-process message and split into 512 bit chunks |
| 163 | +constwords=Array.from(preProcess(message)) |
| 164 | +constchunks=chunkify(words,16) |
| 165 | + |
| 166 | +chunks.forEach(function(chunk,_){ |
| 167 | +// initialize variables for this chunk |
| 168 | +let[A,B,C,D]=[a0,b0,c0,d0] |
| 169 | + |
| 170 | +for(leti=0;i<64;i++){ |
| 171 | +let[F,g]=[0,0] |
| 172 | + |
| 173 | +if(i<=15){ |
| 174 | +F=(B&C)|(~B&D) |
| 175 | +g=i |
| 176 | +}elseif(i<=31){ |
| 177 | +F=(D&B)|(~D&C) |
| 178 | +g=(5*i+1)%16 |
| 179 | +}elseif(i<=47){ |
| 180 | +F=B^C^D |
| 181 | +g=(3*i+5)%16 |
| 182 | +}else{ |
| 183 | +F=C^(B|~D) |
| 184 | +g=(7*i)%16 |
| 185 | +} |
| 186 | + |
| 187 | +F=(F+A+K[i]+chunk[g])>>>0 |
| 188 | +A=D |
| 189 | +D=C |
| 190 | +C=B |
| 191 | +B=((B+rotateLeft(F,S[i]))&0xffffffff)>>>0 |
| 192 | +} |
| 193 | + |
| 194 | +// add values for this chunk to main hash variables (unsigned) |
| 195 | +a0=(a0+A)>>>0 |
| 196 | +b0=(b0+B)>>>0 |
| 197 | +c0=(c0+C)>>>0 |
| 198 | +d0=(d0+D)>>>0 |
| 199 | +}) |
| 200 | + |
| 201 | +returnu32ToU8([a0,b0,c0,d0]) |
| 202 | +} |
| 203 | + |
| 204 | +// export MD5 function |
| 205 | +export{MD5} |