UNPKG

7.98 kBJavaScriptView Raw
1/**
2 * Message Digest Algorithm 5 with 128-bit digest (MD5) implementation.
3 *
4 * @author Dave Longley
5 *
6 * Copyright (c) 2010-2014 Digital Bazaar, Inc.
7 */
8var forge = require('./forge');
9require('./md');
10require('./util');
11
12var md5 = module.exports = forge.md5 = forge.md5 || {};
13forge.md.md5 = forge.md.algorithms.md5 = md5;
14
15/**
16 * Creates an MD5 message digest object.
17 *
18 * @return a message digest object.
19 */
20md5.create = function() {
21 // do initialization as necessary
22 if(!_initialized) {
23 _init();
24 }
25
26 // MD5 state contains four 32-bit integers
27 var _state = null;
28
29 // input buffer
30 var _input = forge.util.createBuffer();
31
32 // used for word storage
33 var _w = new Array(16);
34
35 // message digest object
36 var md = {
37 algorithm: 'md5',
38 blockLength: 64,
39 digestLength: 16,
40 // 56-bit length of message so far (does not including padding)
41 messageLength: 0,
42 // true message length
43 fullMessageLength: null,
44 // size of message length in bytes
45 messageLengthSize: 8
46 };
47
48 /**
49 * Starts the digest.
50 *
51 * @return this digest object.
52 */
53 md.start = function() {
54 // up to 56-bit message length for convenience
55 md.messageLength = 0;
56
57 // full message length (set md.messageLength64 for backwards-compatibility)
58 md.fullMessageLength = md.messageLength64 = [];
59 var int32s = md.messageLengthSize / 4;
60 for(var i = 0; i < int32s; ++i) {
61 md.fullMessageLength.push(0);
62 }
63 _input = forge.util.createBuffer();
64 _state = {
65 h0: 0x67452301,
66 h1: 0xEFCDAB89,
67 h2: 0x98BADCFE,
68 h3: 0x10325476
69 };
70 return md;
71 };
72 // start digest automatically for first time
73 md.start();
74
75 /**
76 * Updates the digest with the given message input. The given input can
77 * treated as raw input (no encoding will be applied) or an encoding of
78 * 'utf8' maybe given to encode the input using UTF-8.
79 *
80 * @param msg the message input to update with.
81 * @param encoding the encoding to use (default: 'raw', other: 'utf8').
82 *
83 * @return this digest object.
84 */
85 md.update = function(msg, encoding) {
86 if(encoding === 'utf8') {
87 msg = forge.util.encodeUtf8(msg);
88 }
89
90 // update message length
91 var len = msg.length;
92 md.messageLength += len;
93 len = [(len / 0x100000000) >>> 0, len >>> 0];
94 for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
95 md.fullMessageLength[i] += len[1];
96 len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);
97 md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;
98 len[0] = (len[1] / 0x100000000) >>> 0;
99 }
100
101 // add bytes to input buffer
102 _input.putBytes(msg);
103
104 // process bytes
105 _update(_state, _w, _input);
106
107 // compact input buffer every 2K or if empty
108 if(_input.read > 2048 || _input.length() === 0) {
109 _input.compact();
110 }
111
112 return md;
113 };
114
115 /**
116 * Produces the digest.
117 *
118 * @return a byte buffer containing the digest value.
119 */
120 md.digest = function() {
121 /* Note: Here we copy the remaining bytes in the input buffer and
122 add the appropriate MD5 padding. Then we do the final update
123 on a copy of the state so that if the user wants to get
124 intermediate digests they can do so. */
125
126 /* Determine the number of bytes that must be added to the message
127 to ensure its length is congruent to 448 mod 512. In other words,
128 the data to be digested must be a multiple of 512 bits (or 128 bytes).
129 This data includes the message, some padding, and the length of the
130 message. Since the length of the message will be encoded as 8 bytes (64
131 bits), that means that the last segment of the data must have 56 bytes
132 (448 bits) of message and padding. Therefore, the length of the message
133 plus the padding must be congruent to 448 mod 512 because
134 512 - 128 = 448.
135
136 In order to fill up the message length it must be filled with
137 padding that begins with 1 bit followed by all 0 bits. Padding
138 must *always* be present, so if the message length is already
139 congruent to 448 mod 512, then 512 padding bits must be added. */
140
141 var finalBlock = forge.util.createBuffer();
142 finalBlock.putBytes(_input.bytes());
143
144 // compute remaining size to be digested (include message length size)
145 var remaining = (
146 md.fullMessageLength[md.fullMessageLength.length - 1] +
147 md.messageLengthSize);
148
149 // add padding for overflow blockSize - overflow
150 // _padding starts with 1 byte with first bit is set (byte value 128), then
151 // there may be up to (blockSize - 1) other pad bytes
152 var overflow = remaining & (md.blockLength - 1);
153 finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));
154
155 // serialize message length in bits in little-endian order; since length
156 // is stored in bytes we multiply by 8 and add carry
157 var bits, carry = 0;
158 for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
159 bits = md.fullMessageLength[i] * 8 + carry;
160 carry = (bits / 0x100000000) >>> 0;
161 finalBlock.putInt32Le(bits >>> 0);
162 }
163
164 var s2 = {
165 h0: _state.h0,
166 h1: _state.h1,
167 h2: _state.h2,
168 h3: _state.h3
169 };
170 _update(s2, _w, finalBlock);
171 var rval = forge.util.createBuffer();
172 rval.putInt32Le(s2.h0);
173 rval.putInt32Le(s2.h1);
174 rval.putInt32Le(s2.h2);
175 rval.putInt32Le(s2.h3);
176 return rval;
177 };
178
179 return md;
180};
181
182// padding, constant tables for calculating md5
183var _padding = null;
184var _g = null;
185var _r = null;
186var _k = null;
187var _initialized = false;
188
189/**
190 * Initializes the constant tables.
191 */
192function _init() {
193 // create padding
194 _padding = String.fromCharCode(128);
195 _padding += forge.util.fillString(String.fromCharCode(0x00), 64);
196
197 // g values
198 _g = [
199 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
200 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,
201 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,
202 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9];
203
204 // rounds table
205 _r = [
206 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
207 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
208 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
209 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];
210
211 // get the result of abs(sin(i + 1)) as a 32-bit integer
212 _k = new Array(64);
213 for(var i = 0; i < 64; ++i) {
214 _k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 0x100000000);
215 }
216
217 // now initialized
218 _initialized = true;
219}
220
221/**
222 * Updates an MD5 state with the given byte buffer.
223 *
224 * @param s the MD5 state to update.
225 * @param w the array to use to store words.
226 * @param bytes the byte buffer to update with.
227 */
228function _update(s, w, bytes) {
229 // consume 512 bit (64 byte) chunks
230 var t, a, b, c, d, f, r, i;
231 var len = bytes.length();
232 while(len >= 64) {
233 // initialize hash value for this chunk
234 a = s.h0;
235 b = s.h1;
236 c = s.h2;
237 d = s.h3;
238
239 // round 1
240 for(i = 0; i < 16; ++i) {
241 w[i] = bytes.getInt32Le();
242 f = d ^ (b & (c ^ d));
243 t = (a + f + _k[i] + w[i]);
244 r = _r[i];
245 a = d;
246 d = c;
247 c = b;
248 b += (t << r) | (t >>> (32 - r));
249 }
250 // round 2
251 for(; i < 32; ++i) {
252 f = c ^ (d & (b ^ c));
253 t = (a + f + _k[i] + w[_g[i]]);
254 r = _r[i];
255 a = d;
256 d = c;
257 c = b;
258 b += (t << r) | (t >>> (32 - r));
259 }
260 // round 3
261 for(; i < 48; ++i) {
262 f = b ^ c ^ d;
263 t = (a + f + _k[i] + w[_g[i]]);
264 r = _r[i];
265 a = d;
266 d = c;
267 c = b;
268 b += (t << r) | (t >>> (32 - r));
269 }
270 // round 4
271 for(; i < 64; ++i) {
272 f = c ^ (b | ~d);
273 t = (a + f + _k[i] + w[_g[i]]);
274 r = _r[i];
275 a = d;
276 d = c;
277 c = b;
278 b += (t << r) | (t >>> (32 - r));
279 }
280
281 // update hash state
282 s.h0 = (s.h0 + a) | 0;
283 s.h1 = (s.h1 + b) | 0;
284 s.h2 = (s.h2 + c) | 0;
285 s.h3 = (s.h3 + d) | 0;
286
287 len -= 64;
288 }
289}