UNPKG

4.02 kBJavaScriptView Raw
1var Buffer = require('buffer/').Buffer;
2var hashUtils = require('./browserHashUtils');
3
4var BLOCK_SIZE = 64;
5
6var DIGEST_LENGTH = 20;
7
8var KEY = new Uint32Array([
9 0x5a827999,
10 0x6ed9eba1,
11 0x8f1bbcdc | 0,
12 0xca62c1d6 | 0
13]);
14
15var INIT = [
16 0x6a09e667,
17 0xbb67ae85,
18 0x3c6ef372,
19 0xa54ff53a,
20 0x510e527f,
21 0x9b05688c,
22 0x1f83d9ab,
23 0x5be0cd19,
24];
25
26var MAX_HASHABLE_LENGTH = Math.pow(2, 53) - 1;
27
28/**
29 * @api private
30 */
31function Sha1() {
32 this.h0 = 0x67452301;
33 this.h1 = 0xEFCDAB89;
34 this.h2 = 0x98BADCFE;
35 this.h3 = 0x10325476;
36 this.h4 = 0xC3D2E1F0;
37 // The first 64 bytes (16 words) is the data chunk
38 this.block = new Uint32Array(80);
39 this.offset = 0;
40 this.shift = 24;
41 this.totalLength = 0;
42}
43
44/**
45 * @api private
46 */
47module.exports = exports = Sha1;
48
49Sha1.BLOCK_SIZE = BLOCK_SIZE;
50
51Sha1.prototype.update = function (data) {
52 if (this.finished) {
53 throw new Error('Attempted to update an already finished hash.');
54 }
55
56 if (hashUtils.isEmptyData(data)) {
57 return this;
58 }
59
60 data = hashUtils.convertToBuffer(data);
61
62 var length = data.length;
63 this.totalLength += length * 8;
64 for (var i = 0; i < length; i++) {
65 this.write(data[i]);
66 }
67
68 return this;
69};
70
71Sha1.prototype.write = function write(byte) {
72 this.block[this.offset] |= (byte & 0xff) << this.shift;
73 if (this.shift) {
74 this.shift -= 8;
75 } else {
76 this.offset++;
77 this.shift = 24;
78 }
79
80 if (this.offset === 16) this.processBlock();
81};
82
83Sha1.prototype.digest = function (encoding) {
84 // Pad
85 this.write(0x80);
86 if (this.offset > 14 || (this.offset === 14 && this.shift < 24)) {
87 this.processBlock();
88 }
89 this.offset = 14;
90 this.shift = 24;
91
92 // 64-bit length big-endian
93 this.write(0x00); // numbers this big aren't accurate in javascript anyway
94 this.write(0x00); // ..So just hard-code to zero.
95 this.write(this.totalLength > 0xffffffffff ? this.totalLength / 0x10000000000 : 0x00);
96 this.write(this.totalLength > 0xffffffff ? this.totalLength / 0x100000000 : 0x00);
97 for (var s = 24; s >= 0; s -= 8) {
98 this.write(this.totalLength >> s);
99 }
100 // The value in state is little-endian rather than big-endian, so flip
101 // each word into a new Uint8Array
102 var out = new Buffer(DIGEST_LENGTH);
103 var outView = new DataView(out.buffer);
104 outView.setUint32(0, this.h0, false);
105 outView.setUint32(4, this.h1, false);
106 outView.setUint32(8, this.h2, false);
107 outView.setUint32(12, this.h3, false);
108 outView.setUint32(16, this.h4, false);
109
110 return encoding ? out.toString(encoding) : out;
111};
112
113Sha1.prototype.processBlock = function processBlock() {
114 // Extend the sixteen 32-bit words into eighty 32-bit words:
115 for (var i = 16; i < 80; i++) {
116 var w = this.block[i - 3] ^ this.block[i - 8] ^ this.block[i - 14] ^ this.block[i - 16];
117 this.block[i] = (w << 1) | (w >>> 31);
118 }
119
120 // Initialize hash value for this chunk:
121 var a = this.h0;
122 var b = this.h1;
123 var c = this.h2;
124 var d = this.h3;
125 var e = this.h4;
126 var f, k;
127
128 // Main loop:
129 for (i = 0; i < 80; i++) {
130 if (i < 20) {
131 f = d ^ (b & (c ^ d));
132 k = 0x5A827999;
133 }
134 else if (i < 40) {
135 f = b ^ c ^ d;
136 k = 0x6ED9EBA1;
137 }
138 else if (i < 60) {
139 f = (b & c) | (d & (b | c));
140 k = 0x8F1BBCDC;
141 }
142 else {
143 f = b ^ c ^ d;
144 k = 0xCA62C1D6;
145 }
146 var temp = (a << 5 | a >>> 27) + f + e + k + (this.block[i]|0);
147 e = d;
148 d = c;
149 c = (b << 30 | b >>> 2);
150 b = a;
151 a = temp;
152 }
153
154 // Add this chunk's hash to result so far:
155 this.h0 = (this.h0 + a) | 0;
156 this.h1 = (this.h1 + b) | 0;
157 this.h2 = (this.h2 + c) | 0;
158 this.h3 = (this.h3 + d) | 0;
159 this.h4 = (this.h4 + e) | 0;
160
161 // The block is now reusable.
162 this.offset = 0;
163 for (i = 0; i < 16; i++) {
164 this.block[i] = 0;
165 }
166};