-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
feat: Added MD5 hashing algorithm #1519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
cb4603c
feat: Added MD5 hashing algorithm
ManpreetXSingh 03466f2
Added wiki link
ManpreetXSingh 39bd146
Remove spam?
ManpreetXSingh 8244cf8
Fix extend towards end
ManpreetXSingh 6226cf4
Return Uint32Array in MD5 function
ManpreetXSingh f35a935
Preprocess function now works on uint arrays
ManpreetXSingh d33e1c4
chunkify U32Array instead of string
ManpreetXSingh b9201b2
Remove all string related functions
ManpreetXSingh 2a8daf7
Replace typed arrays with named variables
ManpreetXSingh d42ae6e
Fix "Replace typed arrays with named variables"
ManpreetXSingh af2684d
Return Uint8 Array in MD5 function
ManpreetXSingh 4a6ff69
Add tests
ManpreetXSingh c8d97df
Fix docstrings
ManpreetXSingh 62682ce
Introduce hexMD5 function
ManpreetXSingh efbd5cc
Change test string
ManpreetXSingh 5fdf178
Format test file
ManpreetXSingh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
// Module that replicates the MD5 Cryptographic Hash | ||
// function in Javascript. | ||
|
||
// main variables | ||
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 | ||
] | ||
|
||
/** | ||
* Separates an array into equal sized 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) | ||
*/ | ||
function chunkify(array, size) { | ||
const chunks = [] | ||
for (let i = 0; i < array.length; i += size) { | ||
chunks.push(array.slice(i, i + size)) | ||
} | ||
return chunks | ||
} | ||
|
||
/** | ||
* Rotates the bits to the left | ||
* | ||
* @param {number} bits - 32 bit number | ||
* @param {number} turns - number of rotations to make | ||
* @return {number} - number after bits rotation | ||
* | ||
* @example | ||
* rotateLeft(0b1011, 3); // 0b1011000 | ||
*/ | ||
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 | ||
} | ||
|
||
/** | ||
* 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 | ||
* | ||
* @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 {Uint8Array} message - message to pre-process | ||
* @return {Uint32Array} - processed message | ||
*/ | ||
function preProcess(message) { | ||
// 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 | ||
) | ||
|
||
// Add '1' bit at the end of the message | ||
m[message.length] = 1 << 7 | ||
|
||
// 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 | ||
} | ||
|
||
/** | ||
* Hashes message using MD5 Cryptographic Hash Function | ||
* | ||
* @see | ||
* For more info: https://en.wikipedia.org/wiki/MD5 | ||
* | ||
* @param {Uint8Array} message - message to hash | ||
* @return {Uint8Array} - message digest (hash value) | ||
*/ | ||
function MD5(message) { | ||
// Initialize variables: | ||
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)) | ||
const chunks = chunkify(words, 16) | ||
|
||
chunks.forEach(function (chunk, _) { | ||
// initialize variables for this chunk | ||
let [A, B, C, D] = [a0, b0, c0, d0] | ||
|
||
for (let i = 0; i < 64; i++) { | ||
let [F, g] = [0, 0] | ||
|
||
if (i <= 15) { | ||
F = (B & C) | (~B & D) | ||
g = i | ||
} else if (i <= 31) { | ||
F = (D & B) | (~D & C) | ||
g = (5 * i + 1) % 16 | ||
} else if (i <= 47) { | ||
F = B ^ C ^ D | ||
g = (3 * i + 5) % 16 | ||
} else { | ||
F = C ^ (B | ~D) | ||
g = (7 * i) % 16 | ||
} | ||
|
||
F = (F + A + K[i] + chunk[g]) >>> 0 | ||
A = D | ||
D = C | ||
C = B | ||
B = ((B + rotateLeft(F, S[i])) & 0xffffffff) >>> 0 | ||
} | ||
|
||
// add values for this chunk to main hash variables (unsigned) | ||
ManpreetXSingh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
a0 = (a0 + A) >>> 0 | ||
b0 = (b0 + B) >>> 0 | ||
c0 = (c0 + C) >>> 0 | ||
d0 = (d0 + D) >>> 0 | ||
}) | ||
|
||
return u32ToU8([a0, b0, c0, d0]) | ||
} | ||
|
||
// export MD5 function | ||
export { MD5 } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
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 = hexMD5(input) | ||
|
||
expect(hash).toBe('9e107d9d372bb6826bd81d3542a419d6') | ||
}) | ||
|
||
it('should return the correct hash for "JavaScript!"', () => { | ||
const input = new TextEncoder().encode('JavaScript!') | ||
const hash = hexMD5(input) | ||
|
||
expect(hash).toBe('209eddd6b61af0643907a8e069a08fb8') | ||
}) | ||
|
||
it('should correctly hash an empty string', () => { | ||
const input = new TextEncoder().encode('') | ||
const hash = hexMD5(input) | ||
|
||
expect(hash).toBe('d41d8cd98f00b204e9800998ecf8427e') | ||
}) | ||
}) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.