Skip to content

Commit 889d9c3

Browse files
feat: Added MD5 hashing algorithm (TheAlgorithms#1519)
* feat: Added MD5 hashing algorithm * Added wiki link * Remove spam? * Fix extend towards end * Return Uint32Array in MD5 function * Preprocess function now works on uint arrays * chunkify U32Array instead of string * Remove all string related functions * Replace typed arrays with named variables * Fix "Replace typed arrays with named variables" * Return Uint8 Array in MD5 function The MD5 function now returns a uint8 array with the correct endianness. * Add tests * Fix docstrings * Introduce hexMD5 function * Change test string * Format test file
1 parent aebd52f commit 889d9c3

File tree

2 files changed

+243
-0
lines changed

2 files changed

+243
-0
lines changed

‎Hashes/MD5.js‎

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
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}

‎Hashes/tests/MD5.test.js‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import{MD5}from'../MD5'
2+
3+
/**
4+
* Returns the MD5 hash of the given message as a hexadecimal string
5+
*
6+
* @param{Uint8Array} message - message to hash
7+
* @return{string} - hash as a hexadecimal string
8+
*/
9+
functionhexMD5(message){
10+
returnArray.from(MD5(message),(byte)=>
11+
byte.toString(16).padStart(2,'0')
12+
).join('')
13+
}
14+
15+
describe('Testing MD5 function',()=>{
16+
it('should return the correct hash for "The quick brown fox jumps over the lazy dog"',()=>{
17+
constinput=newTextEncoder().encode(
18+
'The quick brown fox jumps over the lazy dog'
19+
)
20+
consthash=hexMD5(input)
21+
22+
expect(hash).toBe('9e107d9d372bb6826bd81d3542a419d6')
23+
})
24+
25+
it('should return the correct hash for "JavaScript!"',()=>{
26+
constinput=newTextEncoder().encode('JavaScript!')
27+
consthash=hexMD5(input)
28+
29+
expect(hash).toBe('209eddd6b61af0643907a8e069a08fb8')
30+
})
31+
32+
it('should correctly hash an empty string',()=>{
33+
constinput=newTextEncoder().encode('')
34+
consthash=hexMD5(input)
35+
36+
expect(hash).toBe('d41d8cd98f00b204e9800998ecf8427e')
37+
})
38+
})

0 commit comments

Comments
(0)