Blame view

node_modules/node-forge/lib/md5.js 7.8 KB
aaac7fed   liuqimichale   add
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  /**
   * Message Digest Algorithm 5 with 128-bit digest (MD5) implementation.
   *
   * @author Dave Longley
   *
   * Copyright (c) 2010-2014 Digital Bazaar, Inc.
   */
  var forge = require('./forge');
  require('./md');
  require('./util');
  
  var md5 = module.exports = forge.md5 = forge.md5 || {};
  forge.md.md5 = forge.md.algorithms.md5 = md5;
  
  /**
   * Creates an MD5 message digest object.
   *
   * @return a message digest object.
   */
  md5.create = function() {
    // do initialization as necessary
    if(!_initialized) {
      _init();
    }
  
    // MD5 state contains four 32-bit integers
    var _state = null;
  
    // input buffer
    var _input = forge.util.createBuffer();
  
    // used for word storage
    var _w = new Array(16);
  
    // message digest object
    var md = {
      algorithm: 'md5',
      blockLength: 64,
      digestLength: 16,
      // 56-bit length of message so far (does not including padding)
      messageLength: 0,
      // true message length
      fullMessageLength: null,
      // size of message length in bytes
      messageLengthSize: 8
    };
  
    /**
     * Starts the digest.
     *
     * @return this digest object.
     */
    md.start = function() {
      // up to 56-bit message length for convenience
      md.messageLength = 0;
  
      // full message length (set md.messageLength64 for backwards-compatibility)
      md.fullMessageLength = md.messageLength64 = [];
      var int32s = md.messageLengthSize / 4;
      for(var i = 0; i < int32s; ++i) {
        md.fullMessageLength.push(0);
      }
      _input = forge.util.createBuffer();
      _state = {
        h0: 0x67452301,
        h1: 0xEFCDAB89,
        h2: 0x98BADCFE,
        h3: 0x10325476
      };
      return md;
    };
    // start digest automatically for first time
    md.start();
  
    /**
     * Updates the digest with the given message input. The given input can
     * treated as raw input (no encoding will be applied) or an encoding of
     * 'utf8' maybe given to encode the input using UTF-8.
     *
     * @param msg the message input to update with.
     * @param encoding the encoding to use (default: 'raw', other: 'utf8').
     *
     * @return this digest object.
     */
    md.update = function(msg, encoding) {
      if(encoding === 'utf8') {
        msg = forge.util.encodeUtf8(msg);
      }
  
      // update message length
      var len = msg.length;
      md.messageLength += len;
      len = [(len / 0x100000000) >>> 0, len >>> 0];
      for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
        md.fullMessageLength[i] += len[1];
        len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);
        md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;
        len[0] = (len[1] / 0x100000000) >>> 0;
      }
  
      // add bytes to input buffer
      _input.putBytes(msg);
  
      // process bytes
      _update(_state, _w, _input);
  
      // compact input buffer every 2K or if empty
      if(_input.read > 2048 || _input.length() === 0) {
        _input.compact();
      }
  
      return md;
    };
  
    /**
     * Produces the digest.
     *
     * @return a byte buffer containing the digest value.
     */
    md.digest = function() {
      /* Note: Here we copy the remaining bytes in the input buffer and
      add the appropriate MD5 padding. Then we do the final update
      on a copy of the state so that if the user wants to get
      intermediate digests they can do so. */
  
      /* Determine the number of bytes that must be added to the message
      to ensure its length is congruent to 448 mod 512. In other words,
      the data to be digested must be a multiple of 512 bits (or 128 bytes).
      This data includes the message, some padding, and the length of the
      message. Since the length of the message will be encoded as 8 bytes (64
      bits), that means that the last segment of the data must have 56 bytes
      (448 bits) of message and padding. Therefore, the length of the message
      plus the padding must be congruent to 448 mod 512 because
      512 - 128 = 448.
  
      In order to fill up the message length it must be filled with
      padding that begins with 1 bit followed by all 0 bits. Padding
      must *always* be present, so if the message length is already
      congruent to 448 mod 512, then 512 padding bits must be added. */
  
      var finalBlock = forge.util.createBuffer();
      finalBlock.putBytes(_input.bytes());
  
      // compute remaining size to be digested (include message length size)
      var remaining = (
        md.fullMessageLength[md.fullMessageLength.length - 1] +
        md.messageLengthSize);
  
      // add padding for overflow blockSize - overflow
      // _padding starts with 1 byte with first bit is set (byte value 128), then
      // there may be up to (blockSize - 1) other pad bytes
      var overflow = remaining & (md.blockLength - 1);
      finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));
  
      // serialize message length in bits in little-endian order; since length
      // is stored in bytes we multiply by 8 and add carry
      var bits, carry = 0;
      for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
        bits = md.fullMessageLength[i] * 8 + carry;
        carry = (bits / 0x100000000) >>> 0;
        finalBlock.putInt32Le(bits >>> 0);
      }
  
      var s2 = {
        h0: _state.h0,
        h1: _state.h1,
        h2: _state.h2,
        h3: _state.h3
      };
      _update(s2, _w, finalBlock);
      var rval = forge.util.createBuffer();
      rval.putInt32Le(s2.h0);
      rval.putInt32Le(s2.h1);
      rval.putInt32Le(s2.h2);
      rval.putInt32Le(s2.h3);
      return rval;
    };
  
    return md;
  };
  
  // padding, constant tables for calculating md5
  var _padding = null;
  var _g = null;
  var _r = null;
  var _k = null;
  var _initialized = false;
  
  /**
   * Initializes the constant tables.
   */
  function _init() {
    // create padding
    _padding = String.fromCharCode(128);
    _padding += forge.util.fillString(String.fromCharCode(0x00), 64);
  
    // g values
    _g = [
      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
      1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,
      5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,
      0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9];
  
    // rounds table
    _r = [
      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];
  
    // get the result of abs(sin(i + 1)) as a 32-bit integer
    _k = new Array(64);
    for(var i = 0; i < 64; ++i) {
      _k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 0x100000000);
    }
  
    // now initialized
    _initialized = true;
  }
  
  /**
   * Updates an MD5 state with the given byte buffer.
   *
   * @param s the MD5 state to update.
   * @param w the array to use to store words.
   * @param bytes the byte buffer to update with.
   */
  function _update(s, w, bytes) {
    // consume 512 bit (64 byte) chunks
    var t, a, b, c, d, f, r, i;
    var len = bytes.length();
    while(len >= 64) {
      // initialize hash value for this chunk
      a = s.h0;
      b = s.h1;
      c = s.h2;
      d = s.h3;
  
      // round 1
      for(i = 0; i < 16; ++i) {
        w[i] = bytes.getInt32Le();
        f = d ^ (b & (c ^ d));
        t = (a + f + _k[i] + w[i]);
        r = _r[i];
        a = d;
        d = c;
        c = b;
        b += (t << r) | (t >>> (32 - r));
      }
      // round 2
      for(; i < 32; ++i) {
        f = c ^ (d & (b ^ c));
        t = (a + f + _k[i] + w[_g[i]]);
        r = _r[i];
        a = d;
        d = c;
        c = b;
        b += (t << r) | (t >>> (32 - r));
      }
      // round 3
      for(; i < 48; ++i) {
        f = b ^ c ^ d;
        t = (a + f + _k[i] + w[_g[i]]);
        r = _r[i];
        a = d;
        d = c;
        c = b;
        b += (t << r) | (t >>> (32 - r));
      }
      // round 4
      for(; i < 64; ++i) {
        f = c ^ (b | ~d);
        t = (a + f + _k[i] + w[_g[i]]);
        r = _r[i];
        a = d;
        d = c;
        c = b;
        b += (t << r) | (t >>> (32 - r));
      }
  
      // update hash state
      s.h0 = (s.h0 + a) | 0;
      s.h1 = (s.h1 + b) | 0;
      s.h2 = (s.h2 + c) | 0;
      s.h3 = (s.h3 + d) | 0;
  
      len -= 64;
    }
  }