From cb4603c3188a592f8c179242e4396e214b229137 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Fri, 13 Oct 2023 03:08:29 +0530 Subject: [PATCH 01/16] feat: Added MD5 hashing algorithm --- Hashes/MD5.js | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 Hashes/MD5.js diff --git a/Hashes/MD5.js b/Hashes/MD5.js new file mode 100644 index 0000000000..463f1855ea --- /dev/null +++ b/Hashes/MD5.js @@ -0,0 +1,218 @@ +//= =============================================================== +// MD5.js +// +// Module that replicates the MD5 Cryptographic Hash +// function in Javascript. +//= =============================================================== + +// main variables +const CHAR_SIZE = 8 + +const S = [ + 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 +] + +const K = [ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, + 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, + 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, + 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, + 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, + 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +] + +/** + * Converts the string to little endian in groups of 8 + * + * @param {string} str - 32 character string representation (binary) + * @returns {string} - little endian string + * + * @example + * toLittleEndian("11111111111110100011100101000010"); // "01000010001110011111101011111111" + */ +function toLittleEndian(str) { + return ( + str.slice(24, 32) + str.slice(16, 24) + str.slice(8, 16) + str.slice(0, 8) + ) +} + +/** + * Converts a positive integer to a hexadecimal string representation + * + * @param {number} num - A 32 bit positive integer + * @returns {string} - hex representation of the number + * + * @example + * toHex(255); // "ff000000" + */ +function toHex(num) { + let str = pad(num.toString(16), 8) + return str.slice(6, 8) + str.slice(4, 6) + str.slice(2, 4) + str.slice(0, 2) +} + +/** + * Adds padding to binary/hex string representation + * + * @param {string} str - string representation (binary/hex) + * @param {int} bits - total number of bits wanted + * @return {string} - string representation padding with empty (0) bits + * + * @example + * pad("10011", 8); // "00010011" + */ +function pad(str, bits) { + let res = str + while (res.length % bits !== 0) { + res = '0' + res + } + return res +} + +/** + * Separates string into chunks of the same size + * + * @param {string} str - string to separate into chunks + * @param {int} size - number of characters wanted in each chunk + * @return {array} - array of original string split into chunks + * + * @example + * chunkify("this is a test", 2) + */ +function chunkify(str, size) { + const chunks = [] + for (let i = 0; i < str.length; i += size) { + chunks.push(str.slice(i, i + size)) + } + return chunks +} + +/** + * Rotates the bits to the left + * + * @param {number} bits - 32 bit number + * @param {int} turns - number of rotations to make + * @return {string} - number after bits rotation + * + * @example + * rotateLeft("1011", 3); // "1101" + */ +function rotateLeft(bits, turns) { + return (bits << turns) | (bits >>> (32 - turns)) +} + +/** + * Pre-processes message to feed the algorithm loop + * + * @param {string} message - message to pre-process + * @return {string} - processed message + */ +function preProcess(message) { + // convert message to binary representation padded to + // 8 bits, and add 1 + let m = + message + .split('') + .map((e) => e.charCodeAt(0)) + .map((e) => e.toString(2)) + .map((e) => pad(e, 8)) + .join('') + '1' + + // extend message by adding empty bits (0) + while (m.length % 512 !== 448) { + m += '0' + } + + m = chunkify(m, 32) + .map((e) => toLittleEndian(e)) + .join('') + + // length of message in binary, padded, and extended + // to a 64 bit representation + let ml = (message.length * CHAR_SIZE).toString(2) + ml = pad(ml, 64) + + return m + ml.slice(32) + ml.slice(0, 32) +} + +/** + * Hashes message using MD5 Cryptographic Hash Function + * + * @param {string} message - message to hash + * @return {string} - message digest (hash value) + */ +function MD5(message) { + // Initialize variables: + const [a0, b0, c0, d0] = [0, 1, 2, 3] + const hashes = Uint32Array.from([ + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 + ]) + + // pre-process message and split into 512 bit chunks + const bits = preProcess(message) + const chunks = chunkify(bits, 512) + + chunks.forEach(function (chunk, _) { + // break each chunk into 16 32-bit words + const words = Uint32Array.from( + chunkify(chunk, 32).map((e) => parseInt(e, 2)) + ) + + // initialize variables for this chunk + const [A, B, C, D] = [0, 1, 2, 3] + const abcd = Uint32Array.from([ + hashes[a0], + hashes[b0], + hashes[c0], + hashes[d0] + ]) + + for (let i = 0; i < 64; i++) { + const [F, g] = [0, 1] + const fg = Uint32Array.from([0, 0]) + + if (i <= 15) { + fg[F] = (abcd[B] & abcd[C]) | (~abcd[B] & abcd[D]) + fg[g] = i + } else if (i <= 31) { + fg[F] = (abcd[D] & abcd[B]) | (~abcd[D] & abcd[C]) + fg[g] = (5 * i + 1) % 16 + } else if (i <= 47) { + fg[F] = abcd[B] ^ abcd[C] ^ abcd[D] + fg[g] = (3 * i + 5) % 16 + } else { + fg[F] = abcd[C] ^ (abcd[B] | ~abcd[D]) + fg[g] = (7 * i) % 16 + } + + fg[F] = fg[F] + abcd[A] + K[i] + words[fg[g]] + abcd[A] = abcd[D] + abcd[D] = abcd[C] + abcd[C] = abcd[B] + abcd[B] = abcd[B] + rotateLeft(fg[F], S[i]) + } + + // add values for this chunk to main hash variables (unsigned) + hashes[a0] = hashes[a0] + abcd[A] + hashes[b0] = hashes[b0] + abcd[B] + hashes[c0] = hashes[c0] + abcd[C] + hashes[d0] = hashes[d0] + abcd[D] + }) + + // combine hash values of main hash variables and return + const HH = [hashes[a0], hashes[b0], hashes[c0], hashes[d0]] + .map((e) => toHex(e)) + .join('') + + return HH +} + +// export MD5 function +export { MD5 } From 03466f23b6be6f65a149b84cf13c9cb80befdb20 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Fri, 13 Oct 2023 03:21:41 +0530 Subject: [PATCH 02/16] Added wiki link --- Hashes/MD5.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 463f1855ea..42da23282e 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -145,6 +145,9 @@ function preProcess(message) { /** * Hashes message using MD5 Cryptographic Hash Function * + * @see + * For more info: https://en.wikipedia.org/wiki/MD5 + * * @param {string} message - message to hash * @return {string} - message digest (hash value) */ From 39bd146781a07b2156171180a649ce3bd2d1a82f Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 21 Oct 2023 02:58:08 +0530 Subject: [PATCH 03/16] Remove spam? --- Hashes/MD5.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 42da23282e..ace0afe16e 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -1,9 +1,5 @@ -//= =============================================================== -// MD5.js -// // Module that replicates the MD5 Cryptographic Hash // function in Javascript. -//= =============================================================== // main variables const CHAR_SIZE = 8 From 8244cf8f7dc628d632065b9325de645bec3fa7f5 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 21 Oct 2023 03:05:31 +0530 Subject: [PATCH 04/16] Fix extend towards end --- Hashes/MD5.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index ace0afe16e..7408e83435 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -122,9 +122,7 @@ function preProcess(message) { .join('') + '1' // extend message by adding empty bits (0) - while (m.length % 512 !== 448) { - m += '0' - } + m += '0'.repeat(448 - m.length % 512) m = chunkify(m, 32) .map((e) => toLittleEndian(e)) From 6226cf4230f485ee23cde720f0334dbe5cdb02b6 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 21 Oct 2023 16:58:00 +0530 Subject: [PATCH 05/16] Return Uint32Array in MD5 function --- Hashes/MD5.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 7408e83435..4b9cafba91 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -132,8 +132,9 @@ function preProcess(message) { // to a 64 bit representation let ml = (message.length * CHAR_SIZE).toString(2) ml = pad(ml, 64) + ml = ml.slice(32) + ml.slice(0, 32) - return m + ml.slice(32) + ml.slice(0, 32) + return m + ml } /** @@ -143,7 +144,7 @@ function preProcess(message) { * For more info: https://en.wikipedia.org/wiki/MD5 * * @param {string} message - message to hash - * @return {string} - message digest (hash value) + * @return {Uint32Array} - message digest (hash value) */ function MD5(message) { // Initialize variables: @@ -203,12 +204,7 @@ function MD5(message) { hashes[d0] = hashes[d0] + abcd[D] }) - // combine hash values of main hash variables and return - const HH = [hashes[a0], hashes[b0], hashes[c0], hashes[d0]] - .map((e) => toHex(e)) - .join('') - - return HH + return hashes } // export MD5 function From f35a9356d9095cd99c2a5a6b763fa090292e3c80 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:06:07 +0530 Subject: [PATCH 06/16] Preprocess function now works on uint arrays --- Hashes/MD5.js | 91 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 4b9cafba91..38019837a3 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -104,37 +104,71 @@ function rotateLeft(bits, turns) { return (bits << turns) | (bits >>> (32 - turns)) } +/** + * Converts Uint8Array to Uint32Array + * + * @param {Uint8Array} u8Array Uint8Array to convert + * @returns {Uint32Array} - Required Uint32Array + */ +function u8ToU32(u8Array) { + const uint32Array = new Uint32Array(u8Array.length / 4) + + for (let i = 0; i < u8Array.length; i += 4) { + uint32Array[i / 4] = + (u8Array[i] | + (u8Array[i + 1] << 8) | + (u8Array[i + 2] << 16) | + (u8Array[i + 3] << 24)) >>> + 0 + } + + return uint32Array +} + +/** + * Adds padding to the end of the given array + * + * @param {Uint8Array} u8Array Array to pad + * @param {Number} size Resulting size of the array + */ +function padEnd(u8Array, size) { + const result = new Uint8Array(size) + result.set(u8Array) + result.fill(0, u8Array.length) + + return result +} + /** * Pre-processes message to feed the algorithm loop * - * @param {string} message - message to pre-process - * @return {string} - processed message + * @param {Uint8Array} message - message to pre-process + * @return {Uint32Array} - processed message */ function preProcess(message) { - // convert message to binary representation padded to - // 8 bits, and add 1 - let m = - message - .split('') - .map((e) => e.charCodeAt(0)) - .map((e) => e.toString(2)) - .map((e) => pad(e, 8)) - .join('') + '1' - - // extend message by adding empty bits (0) - m += '0'.repeat(448 - m.length % 512) - - m = chunkify(m, 32) - .map((e) => toLittleEndian(e)) - .join('') + // Extend message by adding '0' + // + // message.length + 1 is for adding '1' bit + // 56 - (length % 64) is for padding with '0's + // 8 is for appending 64 bit message length + let m = padEnd( + message, + message.length + 1 + (56 - ((message.length + 1) % 64)) + 8 + ) - // length of message in binary, padded, and extended - // to a 64 bit representation - let ml = (message.length * CHAR_SIZE).toString(2) - ml = pad(ml, 64) - ml = ml.slice(32) + ml.slice(0, 32) + // Add '1' bit at the end of the message + m[message.length] = 1 << 7 - return m + ml + // convert message to 32 bit uint array + m = u8ToU32(m) + + // Append the length of the message to the end + // (ml / 0x100000000) | 0 is equivalent to (ml >> 32) & 0xffffffff) in other languages + let ml = message.length * 8 + m[m.length - 2] = ml & 0xffffffff + m[m.length - 1] = (ml / 0x100000000) | 0 + + return m } /** @@ -142,8 +176,8 @@ function preProcess(message) { * * @see * For more info: https://en.wikipedia.org/wiki/MD5 - * - * @param {string} message - message to hash + * + * @param {Uint8Array} message - message to hash * @return {Uint32Array} - message digest (hash value) */ function MD5(message) { @@ -154,7 +188,10 @@ function MD5(message) { ]) // pre-process message and split into 512 bit chunks - const bits = preProcess(message) + const bits = Array.from(preProcess(message)) + .map((e) => e.toString(2)) + .map((e) => pad(e, 32)) + .join('') const chunks = chunkify(bits, 512) chunks.forEach(function (chunk, _) { From d33e1c4a900368475831808405688958f2d8e3ea Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:18:32 +0530 Subject: [PATCH 07/16] chunkify U32Array instead of string --- Hashes/MD5.js | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 38019837a3..178aa9990d 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -73,19 +73,19 @@ function pad(str, bits) { } /** - * Separates string into chunks of the same size + * Separates an array into equal sized chunks * - * @param {string} str - string to separate into chunks - * @param {int} size - number of characters wanted in each chunk - * @return {array} - array of original string split into chunks + * @param {Array} array - array to separate into chunks + * @param {int} size - number of elements wanted in each chunk + * @return {array} - array of original array split into chunks * * @example * chunkify("this is a test", 2) */ -function chunkify(str, size) { +function chunkify(array, size) { const chunks = [] - for (let i = 0; i < str.length; i += size) { - chunks.push(str.slice(i, i + size)) + for (let i = 0; i < array.length; i += size) { + chunks.push(array.slice(i, i + size)) } return chunks } @@ -188,18 +188,10 @@ function MD5(message) { ]) // pre-process message and split into 512 bit chunks - const bits = Array.from(preProcess(message)) - .map((e) => e.toString(2)) - .map((e) => pad(e, 32)) - .join('') - const chunks = chunkify(bits, 512) + const words = Array.from(preProcess(message)) + const chunks = chunkify(words, 16) chunks.forEach(function (chunk, _) { - // break each chunk into 16 32-bit words - const words = Uint32Array.from( - chunkify(chunk, 32).map((e) => parseInt(e, 2)) - ) - // initialize variables for this chunk const [A, B, C, D] = [0, 1, 2, 3] const abcd = Uint32Array.from([ @@ -227,7 +219,7 @@ function MD5(message) { fg[g] = (7 * i) % 16 } - fg[F] = fg[F] + abcd[A] + K[i] + words[fg[g]] + fg[F] = fg[F] + abcd[A] + K[i] + chunk[fg[g]] abcd[A] = abcd[D] abcd[D] = abcd[C] abcd[C] = abcd[B] From b9201b21408bf2101119f322a62c9c5b282845aa Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:22:02 +0530 Subject: [PATCH 08/16] Remove all string related functions --- Hashes/MD5.js | 47 ----------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 178aa9990d..92c33a1a73 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -25,53 +25,6 @@ const K = [ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 ] -/** - * Converts the string to little endian in groups of 8 - * - * @param {string} str - 32 character string representation (binary) - * @returns {string} - little endian string - * - * @example - * toLittleEndian("11111111111110100011100101000010"); // "01000010001110011111101011111111" - */ -function toLittleEndian(str) { - return ( - str.slice(24, 32) + str.slice(16, 24) + str.slice(8, 16) + str.slice(0, 8) - ) -} - -/** - * Converts a positive integer to a hexadecimal string representation - * - * @param {number} num - A 32 bit positive integer - * @returns {string} - hex representation of the number - * - * @example - * toHex(255); // "ff000000" - */ -function toHex(num) { - let str = pad(num.toString(16), 8) - return str.slice(6, 8) + str.slice(4, 6) + str.slice(2, 4) + str.slice(0, 2) -} - -/** - * Adds padding to binary/hex string representation - * - * @param {string} str - string representation (binary/hex) - * @param {int} bits - total number of bits wanted - * @return {string} - string representation padding with empty (0) bits - * - * @example - * pad("10011", 8); // "00010011" - */ -function pad(str, bits) { - let res = str - while (res.length % bits !== 0) { - res = '0' + res - } - return res -} - /** * Separates an array into equal sized chunks * From 2a8daf721e75eda1f744e770a24a4fcde61f7086 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 21 Oct 2023 20:20:55 +0530 Subject: [PATCH 09/16] Replace typed arrays with named variables --- Hashes/MD5.js | 54 ++++++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 92c33a1a73..e5b6486996 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -2,8 +2,6 @@ // function in Javascript. // main variables -const CHAR_SIZE = 8 - const S = [ 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, @@ -135,10 +133,7 @@ function preProcess(message) { */ function MD5(message) { // Initialize variables: - const [a0, b0, c0, d0] = [0, 1, 2, 3] - const hashes = Uint32Array.from([ - 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 - ]) + let [a0, b0, c0, d0] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] // pre-process message and split into 512 bit chunks const words = Array.from(preProcess(message)) @@ -146,47 +141,40 @@ function MD5(message) { chunks.forEach(function (chunk, _) { // initialize variables for this chunk - const [A, B, C, D] = [0, 1, 2, 3] - const abcd = Uint32Array.from([ - hashes[a0], - hashes[b0], - hashes[c0], - hashes[d0] - ]) + let [A, B, C, D] = [a0, b0, c0, d0] for (let i = 0; i < 64; i++) { - const [F, g] = [0, 1] - const fg = Uint32Array.from([0, 0]) + let [F, g] = [0, 0] if (i <= 15) { - fg[F] = (abcd[B] & abcd[C]) | (~abcd[B] & abcd[D]) - fg[g] = i + F = (B & C) | (~B & D) + g = i } else if (i <= 31) { - fg[F] = (abcd[D] & abcd[B]) | (~abcd[D] & abcd[C]) - fg[g] = (5 * i + 1) % 16 + F = (D & B) | (~D & C) + g = (5 * i + 1) % 16 } else if (i <= 47) { - fg[F] = abcd[B] ^ abcd[C] ^ abcd[D] - fg[g] = (3 * i + 5) % 16 + F = B ^ C ^ D + g = (3 * i + 5) % 16 } else { - fg[F] = abcd[C] ^ (abcd[B] | ~abcd[D]) - fg[g] = (7 * i) % 16 + F = C ^ (B | ~D) + g = (7 * i) % 16 } - fg[F] = fg[F] + abcd[A] + K[i] + chunk[fg[g]] - abcd[A] = abcd[D] - abcd[D] = abcd[C] - abcd[C] = abcd[B] - abcd[B] = abcd[B] + rotateLeft(fg[F], S[i]) + F = F + A + K[i] + chunk[g] + A = D + D = C + C = B + B = (B + (rotateLeft(F, S[i]) % 0xFFFFFFFF)) >>> 0 } // add values for this chunk to main hash variables (unsigned) - hashes[a0] = hashes[a0] + abcd[A] - hashes[b0] = hashes[b0] + abcd[B] - hashes[c0] = hashes[c0] + abcd[C] - hashes[d0] = hashes[d0] + abcd[D] + a0 = a0 + A + b0 = b0 + B + c0 = c0 + C + d0 = d0 + D }) - return hashes + return [a0, b0, c0, d0] } // export MD5 function From d42ae6e848a09c1525eb16d0ac3147bbfccc2c6b Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 28 Oct 2023 19:02:08 +0530 Subject: [PATCH 10/16] Fix "Replace typed arrays with named variables" --- Hashes/MD5.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index e5b6486996..1c4b1bfc66 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -133,7 +133,12 @@ function preProcess(message) { */ function MD5(message) { // Initialize variables: - let [a0, b0, c0, d0] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] + let [a0, b0, c0, d0] = [ + 0x67452301 >>> 0, + 0xefcdab89 >>> 0, + 0x98badcfe >>> 0, + 0x10325476 >>> 0 + ] // pre-process message and split into 512 bit chunks const words = Array.from(preProcess(message)) @@ -160,21 +165,21 @@ function MD5(message) { g = (7 * i) % 16 } - F = F + A + K[i] + chunk[g] + F = (F + A + K[i] + chunk[g]) >>> 0 A = D D = C C = B - B = (B + (rotateLeft(F, S[i]) % 0xFFFFFFFF)) >>> 0 + B = ((B + rotateLeft(F, S[i])) & 0xffffffff) >>> 0 } // add values for this chunk to main hash variables (unsigned) - a0 = a0 + A - b0 = b0 + B - c0 = c0 + C - d0 = d0 + D + a0 = (a0 + A) >>> 0 + b0 = (b0 + B) >>> 0 + c0 = (c0 + C) >>> 0 + d0 = (d0 + D) >>> 0 }) - return [a0, b0, c0, d0] + return new Uint32Array([a0, b0, c0, d0]) } // export MD5 function From af2684d4946ad2ecd904b62dc3db7e3a69d316ad Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 28 Oct 2023 19:18:13 +0530 Subject: [PATCH 11/16] Return Uint8 Array in MD5 function The MD5 function now returns a uint8 array with the correct endianness. --- Hashes/MD5.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 1c4b1bfc66..6b266560df 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -76,6 +76,25 @@ function u8ToU32(u8Array) { return uint32Array } +/** + * Converts Uint32Array to Uint8Array + * + * @param {Uint32Array} u32Array Uint32Array to convert + * @returns {Uint8Array} - Required Uint8Array + */ +function u32ToU8(u32Array) { + const uint8Array = new Uint8Array(u32Array.length * 4) + + for (let i = 0; i < u32Array.length; i++) { + uint8Array[i * 4] = u32Array[i] & 0xff + uint8Array[i * 4 + 1] = (u32Array[i] >> 8) & 0xff + uint8Array[i * 4 + 2] = (u32Array[i] >> 16) & 0xff + uint8Array[i * 4 + 3] = (u32Array[i] >> 24) & 0xff + } + + return uint8Array +} + /** * Adds padding to the end of the given array * @@ -129,7 +148,7 @@ function preProcess(message) { * For more info: https://en.wikipedia.org/wiki/MD5 * * @param {Uint8Array} message - message to hash - * @return {Uint32Array} - message digest (hash value) + * @return {Uint8Array} - message digest (hash value) */ function MD5(message) { // Initialize variables: @@ -179,7 +198,7 @@ function MD5(message) { d0 = (d0 + D) >>> 0 }) - return new Uint32Array([a0, b0, c0, d0]) + return u32ToU8([a0, b0, c0, d0]) } // export MD5 function From 4a6ff6955a65f6a382602593055a73d3913fb6a2 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 28 Oct 2023 19:36:20 +0530 Subject: [PATCH 12/16] Add tests --- Hashes/tests/MD5.test.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Hashes/tests/MD5.test.js diff --git a/Hashes/tests/MD5.test.js b/Hashes/tests/MD5.test.js new file mode 100644 index 0000000000..0cc08702c2 --- /dev/null +++ b/Hashes/tests/MD5.test.js @@ -0,0 +1,37 @@ +import { MD5 } from '../MD5' + +describe('Testing MD5 function', () => { + it('should return the correct hash for "The quick brown fox jumps over the lazy dog"', () => { + const input = new TextEncoder().encode( + 'The quick brown fox jumps over the lazy dog' + ) + + const hash = Array.from(MD5(input), (byte) => + byte.toString(16).padStart(2, '0') + ).join('') + + expect(hash).toBe('9e107d9d372bb6826bd81d3542a419d6') + }) + + it('should return the correct hash for "The quick brown fox jumps over the lazy dog."', () => { + const input = new TextEncoder().encode( + 'The quick brown fox jumps over the lazy dog.' + ) + + const hash = Array.from(MD5(input), (byte) => + byte.toString(16).padStart(2, '0') + ).join('') + + expect(hash).toBe('e4d909c290d0fb1ca068ffaddf22cbd0') + }) + + it('should correctly hash an empty string', () => { + const input = new TextEncoder().encode('') + + const hash = Array.from(MD5(input), (byte) => + byte.toString(16).padStart(2, '0') + ).join('') + + expect(hash).toBe('d41d8cd98f00b204e9800998ecf8427e') + }) +}) From c8d97df329911e7dafb78718e70f7e6b61a47ac1 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 28 Oct 2023 19:57:05 +0530 Subject: [PATCH 13/16] Fix docstrings --- Hashes/MD5.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Hashes/MD5.js b/Hashes/MD5.js index 6b266560df..42bef45cc5 100644 --- a/Hashes/MD5.js +++ b/Hashes/MD5.js @@ -26,9 +26,9 @@ const K = [ /** * Separates an array into equal sized chunks * - * @param {Array} array - array to separate into chunks - * @param {int} size - number of elements wanted in each chunk - * @return {array} - array of original array split into chunks + * @param {Array|string} array - array or string to separate into chunks + * @param {number} size - number of elements wanted in each chunk + * @return {Array} - array of original array split into chunks * * @example * chunkify("this is a test", 2) @@ -45,11 +45,11 @@ function chunkify(array, size) { * Rotates the bits to the left * * @param {number} bits - 32 bit number - * @param {int} turns - number of rotations to make - * @return {string} - number after bits rotation + * @param {number} turns - number of rotations to make + * @return {number} - number after bits rotation * * @example - * rotateLeft("1011", 3); // "1101" + * rotateLeft(0b1011, 3); // 0b1011000 */ function rotateLeft(bits, turns) { return (bits << turns) | (bits >>> (32 - turns)) @@ -99,7 +99,7 @@ function u32ToU8(u32Array) { * Adds padding to the end of the given array * * @param {Uint8Array} u8Array Array to pad - * @param {Number} size Resulting size of the array + * @param {number} size Resulting size of the array */ function padEnd(u8Array, size) { const result = new Uint8Array(size) From 62682cec765c5c4ac649f5604a42e82bcd0bc462 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 29 Oct 2023 15:59:05 +0530 Subject: [PATCH 14/16] Introduce hexMD5 function --- Hashes/tests/MD5.test.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Hashes/tests/MD5.test.js b/Hashes/tests/MD5.test.js index 0cc08702c2..584c348916 100644 --- a/Hashes/tests/MD5.test.js +++ b/Hashes/tests/MD5.test.js @@ -1,14 +1,23 @@ import { MD5 } from '../MD5' +/** + * Returns the MD5 hash of the given message as a hexadecimal string + * + * @param {Uint8Array} message - message to hash + * @return {string} - hash as a hexadecimal string + */ +function hexMD5(message) { + return Array.from(MD5(message), (byte) => + byte.toString(16).padStart(2, '0') + ).join('') +} + describe('Testing MD5 function', () => { it('should return the correct hash for "The quick brown fox jumps over the lazy dog"', () => { const input = new TextEncoder().encode( 'The quick brown fox jumps over the lazy dog' ) - - const hash = Array.from(MD5(input), (byte) => - byte.toString(16).padStart(2, '0') - ).join('') + const hash = hexMD5(input) expect(hash).toBe('9e107d9d372bb6826bd81d3542a419d6') }) @@ -17,20 +26,14 @@ describe('Testing MD5 function', () => { const input = new TextEncoder().encode( 'The quick brown fox jumps over the lazy dog.' ) - - const hash = Array.from(MD5(input), (byte) => - byte.toString(16).padStart(2, '0') - ).join('') + const hash = hexMD5(input) expect(hash).toBe('e4d909c290d0fb1ca068ffaddf22cbd0') }) it('should correctly hash an empty string', () => { const input = new TextEncoder().encode('') - - const hash = Array.from(MD5(input), (byte) => - byte.toString(16).padStart(2, '0') - ).join('') + const hash = hexMD5(input) expect(hash).toBe('d41d8cd98f00b204e9800998ecf8427e') }) From efbd5ccb7a4c718bf7fce0eab84ce65ea1129a64 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:43:56 +0530 Subject: [PATCH 15/16] Change test string --- Hashes/tests/MD5.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Hashes/tests/MD5.test.js b/Hashes/tests/MD5.test.js index 584c348916..5a831dccf6 100644 --- a/Hashes/tests/MD5.test.js +++ b/Hashes/tests/MD5.test.js @@ -22,13 +22,13 @@ describe('Testing MD5 function', () => { expect(hash).toBe('9e107d9d372bb6826bd81d3542a419d6') }) - it('should return the correct hash for "The quick brown fox jumps over the lazy dog."', () => { + it('should return the correct hash for "JavaScript!"', () => { const input = new TextEncoder().encode( - 'The quick brown fox jumps over the lazy dog.' + 'JavaScript!' ) const hash = hexMD5(input) - expect(hash).toBe('e4d909c290d0fb1ca068ffaddf22cbd0') + expect(hash).toBe('209eddd6b61af0643907a8e069a08fb8') }) it('should correctly hash an empty string', () => { From 5fdf178c127f2226d1f67c97c0b5a12a2013a728 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:50:28 +0530 Subject: [PATCH 16/16] Format test file --- Hashes/tests/MD5.test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Hashes/tests/MD5.test.js b/Hashes/tests/MD5.test.js index 5a831dccf6..5c44c7a57c 100644 --- a/Hashes/tests/MD5.test.js +++ b/Hashes/tests/MD5.test.js @@ -23,9 +23,7 @@ describe('Testing MD5 function', () => { }) it('should return the correct hash for "JavaScript!"', () => { - const input = new TextEncoder().encode( - 'JavaScript!' - ) + const input = new TextEncoder().encode('JavaScript!') const hash = hexMD5(input) expect(hash).toBe('209eddd6b61af0643907a8e069a08fb8')