MD5.js
7.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import { Word32Array } from "./lib/Word32Array";
import { Hasher } from "./lib/algorithm/Hasher";
// Constants table
const T = [];
(function computeConstant() {
for (let i = 0; i < 64; i++) {
T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0;
}
})();
function FF(a, b, c, d, x, s, t) {
const n = a + ((b & c) | (~b & d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function GG(a, b, c, d, x, s, t) {
const n = a + ((b & d) | (c & ~d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function HH(a, b, c, d, x, s, t) {
const n = a + (b ^ c ^ d) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function II(a, b, c, d, x, s, t) {
const n = a + (c ^ (b | ~d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
/**
* MD5 hash algorithm
*/
export class MD5 extends Hasher {
constructor(props) {
super(props);
this._hash = new Word32Array([
0x67452301, 0xefcdab89,
0x98badcfe, 0x10325476
]);
if (props && typeof props.hash !== "undefined") {
this._hash = props.hash.clone();
}
}
_doReset() {
this._hash = new Word32Array([
0x67452301, 0xefcdab89,
0x98badcfe, 0x10325476
]);
}
_doProcessBlock(words, offset) {
// Swap endian
for (let i = 0; i < 16; i++) {
// Shortcuts
const offsetI = offset + i;
const wordsOffsetI = words[offsetI];
words[offsetI] = ((((wordsOffsetI << 8) | (wordsOffsetI >>> 24)) & 0x00ff00ff)
| (((wordsOffsetI << 24) | (wordsOffsetI >>> 8)) & 0xff00ff00));
}
// Shortcuts
const H = this._hash.words;
const wordOffset0 = words[offset];
const wordOffset1 = words[offset + 1];
const wordOffset2 = words[offset + 2];
const wordOffset3 = words[offset + 3];
const wordOffset4 = words[offset + 4];
const wordOffset5 = words[offset + 5];
const wordOffset6 = words[offset + 6];
const wordOffset7 = words[offset + 7];
const wordOffset8 = words[offset + 8];
const wordOffset9 = words[offset + 9];
const wordOffset10 = words[offset + 10];
const wordOffset11 = words[offset + 11];
const wordOffset12 = words[offset + 12];
const wordOffset13 = words[offset + 13];
const wordOffset14 = words[offset + 14];
const wordOffset15 = words[offset + 15];
// Working variables
let a = H[0];
let b = H[1];
let c = H[2];
let d = H[3];
// Computation
a = FF(a, b, c, d, wordOffset0, 7, T[0]);
d = FF(d, a, b, c, wordOffset1, 12, T[1]);
c = FF(c, d, a, b, wordOffset2, 17, T[2]);
b = FF(b, c, d, a, wordOffset3, 22, T[3]);
a = FF(a, b, c, d, wordOffset4, 7, T[4]);
d = FF(d, a, b, c, wordOffset5, 12, T[5]);
c = FF(c, d, a, b, wordOffset6, 17, T[6]);
b = FF(b, c, d, a, wordOffset7, 22, T[7]);
a = FF(a, b, c, d, wordOffset8, 7, T[8]);
d = FF(d, a, b, c, wordOffset9, 12, T[9]);
c = FF(c, d, a, b, wordOffset10, 17, T[10]);
b = FF(b, c, d, a, wordOffset11, 22, T[11]);
a = FF(a, b, c, d, wordOffset12, 7, T[12]);
d = FF(d, a, b, c, wordOffset13, 12, T[13]);
c = FF(c, d, a, b, wordOffset14, 17, T[14]);
b = FF(b, c, d, a, wordOffset15, 22, T[15]);
a = GG(a, b, c, d, wordOffset1, 5, T[16]);
d = GG(d, a, b, c, wordOffset6, 9, T[17]);
c = GG(c, d, a, b, wordOffset11, 14, T[18]);
b = GG(b, c, d, a, wordOffset0, 20, T[19]);
a = GG(a, b, c, d, wordOffset5, 5, T[20]);
d = GG(d, a, b, c, wordOffset10, 9, T[21]);
c = GG(c, d, a, b, wordOffset15, 14, T[22]);
b = GG(b, c, d, a, wordOffset4, 20, T[23]);
a = GG(a, b, c, d, wordOffset9, 5, T[24]);
d = GG(d, a, b, c, wordOffset14, 9, T[25]);
c = GG(c, d, a, b, wordOffset3, 14, T[26]);
b = GG(b, c, d, a, wordOffset8, 20, T[27]);
a = GG(a, b, c, d, wordOffset13, 5, T[28]);
d = GG(d, a, b, c, wordOffset2, 9, T[29]);
c = GG(c, d, a, b, wordOffset7, 14, T[30]);
b = GG(b, c, d, a, wordOffset12, 20, T[31]);
a = HH(a, b, c, d, wordOffset5, 4, T[32]);
d = HH(d, a, b, c, wordOffset8, 11, T[33]);
c = HH(c, d, a, b, wordOffset11, 16, T[34]);
b = HH(b, c, d, a, wordOffset14, 23, T[35]);
a = HH(a, b, c, d, wordOffset1, 4, T[36]);
d = HH(d, a, b, c, wordOffset4, 11, T[37]);
c = HH(c, d, a, b, wordOffset7, 16, T[38]);
b = HH(b, c, d, a, wordOffset10, 23, T[39]);
a = HH(a, b, c, d, wordOffset13, 4, T[40]);
d = HH(d, a, b, c, wordOffset0, 11, T[41]);
c = HH(c, d, a, b, wordOffset3, 16, T[42]);
b = HH(b, c, d, a, wordOffset6, 23, T[43]);
a = HH(a, b, c, d, wordOffset9, 4, T[44]);
d = HH(d, a, b, c, wordOffset12, 11, T[45]);
c = HH(c, d, a, b, wordOffset15, 16, T[46]);
b = HH(b, c, d, a, wordOffset2, 23, T[47]);
a = II(a, b, c, d, wordOffset0, 6, T[48]);
d = II(d, a, b, c, wordOffset7, 10, T[49]);
c = II(c, d, a, b, wordOffset14, 15, T[50]);
b = II(b, c, d, a, wordOffset5, 21, T[51]);
a = II(a, b, c, d, wordOffset12, 6, T[52]);
d = II(d, a, b, c, wordOffset3, 10, T[53]);
c = II(c, d, a, b, wordOffset10, 15, T[54]);
b = II(b, c, d, a, wordOffset1, 21, T[55]);
a = II(a, b, c, d, wordOffset8, 6, T[56]);
d = II(d, a, b, c, wordOffset15, 10, T[57]);
c = II(c, d, a, b, wordOffset6, 15, T[58]);
b = II(b, c, d, a, wordOffset13, 21, T[59]);
a = II(a, b, c, d, wordOffset4, 6, T[60]);
d = II(d, a, b, c, wordOffset11, 10, T[61]);
c = II(c, d, a, b, wordOffset2, 15, T[62]);
b = II(b, c, d, a, wordOffset9, 21, T[63]);
// Intermediate hash value
H[0] = (H[0] + a) | 0;
H[1] = (H[1] + b) | 0;
H[2] = (H[2] + c) | 0;
H[3] = (H[3] + d) | 0;
}
_doFinalize() {
// Shortcuts
const data = this._data;
const dataWords = data.words;
const nBitsTotal = this._nBytes * 8;
const nBitsLeft = data.nSigBytes * 8;
// Add padding
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
const nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);
const nBitsTotalL = nBitsTotal;
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = ((((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) |
(((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00));
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ((((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) |
(((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00));
data.nSigBytes = (dataWords.length + 1) * 4;
// Hash final blocks
this._process();
// Shortcuts
const hash = this._hash;
const H = hash.words;
// Swap endian
for (let i = 0; i < 4; i++) {
// Shortcut
const Hi = H[i];
H[i] = (((Hi << 8) | (Hi >>> 24)) & 0x00ff00ff)
| (((Hi << 24) | (Hi >>> 8)) & 0xff00ff00);
}
// Return final computed hash
return hash;
}
clone() {
const props = { hash: this._hash, blockSize: this._blockSize, data: this._data, nBytes: this._nBytes };
return new MD5(props);
}
static hash(message) {
return new MD5().finalize(message);
}
}