1
+ import { Hash } from './hash' ;
2
+
3
+ /**
4
+ * @fileoverview SHA-1 cryptographic hash.
5
+ * Variable names follow the notation in FIPS PUB 180-3:
6
+ * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.
7
+ *
8
+ * Usage:
9
+ * var sha1 = new sha1();
10
+ * sha1.update(bytes);
11
+ * var hash = sha1.digest();
12
+ *
13
+ * Performance:
14
+ * Chrome 23: ~400 Mbit/s
15
+ * Firefox 16: ~250 Mbit/s
16
+ *
17
+ */
18
+
19
+ /**
20
+ * SHA-1 cryptographic hash constructor.
21
+ *
22
+ * The properties declared here are discussed in the above algorithm document.
23
+ * @constructor
24
+ * @extends {Hash }
25
+ * @final
26
+ * @struct
27
+ */
28
+ export class Sha1 extends Hash {
29
+ /**
30
+ * Holds the previous values of accumulated variables a-e in the compress_
31
+ * function.
32
+ * @type {!Array<number> }
33
+ * @private
34
+ */
35
+ private chain_ : Array < number > = [ ] ;
36
+
37
+ /**
38
+ * A buffer holding the partially computed hash result.
39
+ * @type {!Array<number> }
40
+ * @private
41
+ */
42
+ private buf_ : Array < number > = [ ] ;
43
+
44
+ /**
45
+ * An array of 80 bytes, each a part of the message to be hashed. Referred to
46
+ * as the message schedule in the docs.
47
+ * @type {!Array<number> }
48
+ * @private
49
+ */
50
+ private W_ : Array < number > = [ ] ;
51
+
52
+ /**
53
+ * Contains data needed to pad messages less than 64 bytes.
54
+ * @type {!Array<number> }
55
+ * @private
56
+ */
57
+ private pad_ : Array < number > = [ ] ;
58
+
59
+ /**
60
+ * @private {number}
61
+ */
62
+ private inbuf_ : number = 0 ;
63
+
64
+ /**
65
+ * @private {number}
66
+ */
67
+ private total_ : number = 0 ;
68
+
69
+ constructor ( ) {
70
+ super ( ) ;
71
+
72
+ this . blockSize = 512 / 8 ;
73
+
74
+ this . pad_ [ 0 ] = 128 ;
75
+ for ( var i = 1 ; i < this . blockSize ; ++ i ) {
76
+ this . pad_ [ i ] = 0 ;
77
+ }
78
+
79
+ this . reset ( ) ;
80
+ }
81
+
82
+ reset ( ) {
83
+ this . chain_ [ 0 ] = 0x67452301 ;
84
+ this . chain_ [ 1 ] = 0xefcdab89 ;
85
+ this . chain_ [ 2 ] = 0x98badcfe ;
86
+ this . chain_ [ 3 ] = 0x10325476 ;
87
+ this . chain_ [ 4 ] = 0xc3d2e1f0 ;
88
+
89
+ this . inbuf_ = 0 ;
90
+ this . total_ = 0 ;
91
+ }
92
+
93
+
94
+ /**
95
+ * Internal compress helper function.
96
+ * @param {!Array<number>|!Uint8Array|string } buf Block to compress.
97
+ * @param {number= } opt_offset Offset of the block in the buffer.
98
+ * @private
99
+ */
100
+ compress_ ( buf , opt_offset ?) {
101
+ if ( ! opt_offset ) {
102
+ opt_offset = 0 ;
103
+ }
104
+
105
+ var W = this . W_ ;
106
+
107
+ // get 16 big endian words
108
+ if ( typeof buf === 'string' ) {
109
+ for ( var i = 0 ; i < 16 ; i ++ ) {
110
+ // TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS
111
+ // have a bug that turns the post-increment ++ operator into pre-increment
112
+ // during JIT compilation. We have code that depends heavily on SHA-1 for
113
+ // correctness and which is affected by this bug, so I've removed all uses
114
+ // of post-increment ++ in which the result value is used. We can revert
115
+ // this change once the Safari bug
116
+ // (https://bugs.webkit.org/show_bug.cgi?id=109036) has been fixed and
117
+ // most clients have been updated.
118
+ W [ i ] = ( buf . charCodeAt ( opt_offset ) << 24 ) |
119
+ ( buf . charCodeAt ( opt_offset + 1 ) << 16 ) |
120
+ ( buf . charCodeAt ( opt_offset + 2 ) << 8 ) |
121
+ ( buf . charCodeAt ( opt_offset + 3 ) ) ;
122
+ opt_offset += 4 ;
123
+ }
124
+ } else {
125
+ for ( var i = 0 ; i < 16 ; i ++ ) {
126
+ W [ i ] = ( buf [ opt_offset ] << 24 ) |
127
+ ( buf [ opt_offset + 1 ] << 16 ) |
128
+ ( buf [ opt_offset + 2 ] << 8 ) |
129
+ ( buf [ opt_offset + 3 ] ) ;
130
+ opt_offset += 4 ;
131
+ }
132
+ }
133
+
134
+ // expand to 80 words
135
+ for ( var i = 16 ; i < 80 ; i ++ ) {
136
+ var t = W [ i - 3 ] ^ W [ i - 8 ] ^ W [ i - 14 ] ^ W [ i - 16 ] ;
137
+ W [ i ] = ( ( t << 1 ) | ( t >>> 31 ) ) & 0xffffffff ;
138
+ }
139
+
140
+ var a = this . chain_ [ 0 ] ;
141
+ var b = this . chain_ [ 1 ] ;
142
+ var c = this . chain_ [ 2 ] ;
143
+ var d = this . chain_ [ 3 ] ;
144
+ var e = this . chain_ [ 4 ] ;
145
+ var f , k ;
146
+
147
+ // TODO(user): Try to unroll this loop to speed up the computation.
148
+ for ( var i = 0 ; i < 80 ; i ++ ) {
149
+ if ( i < 40 ) {
150
+ if ( i < 20 ) {
151
+ f = d ^ ( b & ( c ^ d ) ) ;
152
+ k = 0x5a827999 ;
153
+ } else {
154
+ f = b ^ c ^ d ;
155
+ k = 0x6ed9eba1 ;
156
+ }
157
+ } else {
158
+ if ( i < 60 ) {
159
+ f = ( b & c ) | ( d & ( b | c ) ) ;
160
+ k = 0x8f1bbcdc ;
161
+ } else {
162
+ f = b ^ c ^ d ;
163
+ k = 0xca62c1d6 ;
164
+ }
165
+ }
166
+
167
+ var t = ( ( ( a << 5 ) | ( a >>> 27 ) ) + f + e + k + W [ i ] ) & 0xffffffff ;
168
+ e = d ;
169
+ d = c ;
170
+ c = ( ( b << 30 ) | ( b >>> 2 ) ) & 0xffffffff ;
171
+ b = a ;
172
+ a = t ;
173
+ }
174
+
175
+ this . chain_ [ 0 ] = ( this . chain_ [ 0 ] + a ) & 0xffffffff ;
176
+ this . chain_ [ 1 ] = ( this . chain_ [ 1 ] + b ) & 0xffffffff ;
177
+ this . chain_ [ 2 ] = ( this . chain_ [ 2 ] + c ) & 0xffffffff ;
178
+ this . chain_ [ 3 ] = ( this . chain_ [ 3 ] + d ) & 0xffffffff ;
179
+ this . chain_ [ 4 ] = ( this . chain_ [ 4 ] + e ) & 0xffffffff ;
180
+ }
181
+
182
+ update ( bytes , opt_length ?) {
183
+ // TODO(johnlenz): tighten the function signature and remove this check
184
+ if ( bytes == null ) {
185
+ return ;
186
+ }
187
+
188
+ if ( opt_length === 'undefined' ) {
189
+ opt_length = bytes . length ;
190
+ }
191
+
192
+ var lengthMinusBlock = opt_length - this . blockSize ;
193
+ var n = 0 ;
194
+ // Using local instead of member variables gives ~5% speedup on Firefox 16.
195
+ var buf = this . buf_ ;
196
+ var inbuf = this . inbuf_ ;
197
+
198
+ // The outer while loop should execute at most twice.
199
+ while ( n < opt_length ) {
200
+ // When we have no data in the block to top up, we can directly process the
201
+ // input buffer (assuming it contains sufficient data). This gives ~25%
202
+ // speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that
203
+ // the data is provided in large chunks (or in multiples of 64 bytes).
204
+ if ( inbuf == 0 ) {
205
+ while ( n <= lengthMinusBlock ) {
206
+ this . compress_ ( bytes , n ) ;
207
+ n += this . blockSize ;
208
+ }
209
+ }
210
+
211
+ if ( typeof bytes === 'string' ) {
212
+ while ( n < opt_length ) {
213
+ buf [ inbuf ] = bytes . charCodeAt ( n ) ;
214
+ ++ inbuf ;
215
+ ++ n ;
216
+ if ( inbuf == this . blockSize ) {
217
+ this . compress_ ( buf ) ;
218
+ inbuf = 0 ;
219
+ // Jump to the outer loop so we use the full-block optimization.
220
+ break ;
221
+ }
222
+ }
223
+ } else {
224
+ while ( n < opt_length ) {
225
+ buf [ inbuf ] = bytes [ n ] ;
226
+ ++ inbuf ;
227
+ ++ n ;
228
+ if ( inbuf == this . blockSize ) {
229
+ this . compress_ ( buf ) ;
230
+ inbuf = 0 ;
231
+ // Jump to the outer loop so we use the full-block optimization.
232
+ break ;
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ this . inbuf_ = inbuf ;
239
+ this . total_ += opt_length ;
240
+ }
241
+
242
+
243
+ /** @override */
244
+ digest ( ) {
245
+ var digest = [ ] ;
246
+ var totalBits = this . total_ * 8 ;
247
+
248
+ // Add pad 0x80 0x00*.
249
+ if ( this . inbuf_ < 56 ) {
250
+ this . update ( this . pad_ , 56 - this . inbuf_ ) ;
251
+ } else {
252
+ this . update ( this . pad_ , this . blockSize - ( this . inbuf_ - 56 ) ) ;
253
+ }
254
+
255
+ // Add # bits.
256
+ for ( var i = this . blockSize - 1 ; i >= 56 ; i -- ) {
257
+ this . buf_ [ i ] = totalBits & 255 ;
258
+ totalBits /= 256 ; // Don't use bit-shifting here!
259
+ }
260
+
261
+ this . compress_ ( this . buf_ ) ;
262
+
263
+ var n = 0 ;
264
+ for ( var i = 0 ; i < 5 ; i ++ ) {
265
+ for ( var j = 24 ; j >= 0 ; j -= 8 ) {
266
+ digest [ n ] = ( this . chain_ [ i ] >> j ) & 255 ;
267
+ ++ n ;
268
+ }
269
+ }
270
+ return digest ;
271
+ }
272
+ }
0 commit comments