11/**
22 * Secure Hash Algorithm with 160-bit digest (SHA-1) implementation.
3+ * This implementation follows the FIPS 180-2 specification and is based on the
4+ * node-forge implementation.
35 *
46 * @author Chris Breuer
57 */
@@ -41,8 +43,8 @@ export function create(): MessageDigest {
4143 // input buffer
4244 let _input = createBuffer ( )
4345
44- // Fix array initialization with proper typing
45- const _w : number [ ] = Array . from ( { length : 80 } ) . fill ( 0 )
46+ // used for word storage
47+ const _w : number [ ] = new Array ( 80 ) . fill ( 0 )
4648
4749 // message digest object
4850 const md : MessageDigest = {
@@ -84,31 +86,31 @@ export function create(): MessageDigest {
8486 } ,
8587
8688 /**
87- * Updates the digest with the given message input. The given input can
88- * treated as raw input (no encoding will be applied) or an encoding of
89- * 'utf8' maybe given to encode the input using UTF-8.
89+ * Updates the digest with the given message input.
9090 *
9191 * @param msg the message input to update with.
9292 * @param encoding the encoding to use (default: 'raw', other: 'utf8').
9393 *
9494 * @return this digest object.
9595 */
9696 update ( msg : string | ByteStringBuffer , encoding ?: string ) {
97+ if ( ! msg ) {
98+ return md
99+ }
100+
97101 if ( encoding === 'utf8' ) {
98102 msg = encodeUtf8 ( msg as string )
99103 }
100104
101105 // update message length
102106 const len = msg instanceof ByteStringBuffer ? msg . length ( ) : msg . length
103107 md . messageLength += len
104- const lenArr = [ Math . floor ( len / 0x100000000 ) , len >>> 0 ]
108+ const lenArr = [ ( len / 0x100000000 ) >>> 0 , len >>> 0 ]
105109 for ( let i = md . fullMessageLength . length - 1 ; i >= 0 ; -- i ) {
106110 md . fullMessageLength [ i ] += lenArr [ 1 ]
107- const carry = Math . floor ( md . fullMessageLength [ i ] / 0x100000000 )
111+ lenArr [ 1 ] = lenArr [ 0 ] + ( ( md . fullMessageLength [ i ] / 0x100000000 ) >>> 0 )
108112 md . fullMessageLength [ i ] = md . fullMessageLength [ i ] >>> 0
109- if ( carry > 0 && i > 0 ) {
110- md . fullMessageLength [ i - 1 ] += carry
111- }
113+ lenArr [ 0 ] = ( ( lenArr [ 1 ] / 0x100000000 ) >>> 0 )
112114 }
113115
114116 // add bytes to input buffer
@@ -117,9 +119,9 @@ export function create(): MessageDigest {
117119 // process bytes
118120 _update ( _state ! , _w , _input )
119121
120- // compact input buffer every 2K or if empty
121- if ( _input . read > 2048 || _input . length ( ) === 0 ) {
122- _input . compact ( )
122+ // compact input buffer every 2K bytes
123+ if ( _input . length ( ) > 2048 ) {
124+ _input = createBuffer ( _input . bytes ( ) )
123125 }
124126
125127 return md
@@ -131,70 +133,65 @@ export function create(): MessageDigest {
131133 * @return a byte buffer containing the digest value.
132134 */
133135 digest ( ) {
134- /* Note: Here we copy the remaining bytes in the input buffer and
135- add the appropriate SHA-1 padding. Then we do the final update
136- on a copy of the state so that if the user wants to get
137- intermediate digests they can do so. */
138-
139- /* Determine the number of bytes that must be added to the message
140- to ensure its length is congruent to 448 mod 512. In other words,
141- the data to be digested must be a multiple of 512 bits (or 128 bytes).
142- This data includes the message, some padding, and the length of the
143- message. Since the length of the message will be encoded as 8 bytes (64
144- bits), that means that the last segment of the data must have 56 bytes
145- (448 bits) of message and padding. Therefore, the length of the message
146- plus the padding must be congruent to 448 mod 512 because
147- 512 - 128 = 448.
148-
149- In order to fill up the message length it must be filled with
150- padding that begins with 1 bit followed by all 0 bits. Padding
151- must *always* be present, so if the message length is already
152- congruent to 448 mod 512, then 512 padding bits must be added. */
153-
154136 const finalBlock = createBuffer ( )
155137 finalBlock . putBytes ( _input . bytes ( ) )
156138
157139 // compute remaining size to be digested (include message length size)
158140 const remaining = (
159- md . fullMessageLength [ md . fullMessageLength . length - 1 ]
160- + md . messageLengthSize )
141+ md . fullMessageLength [ md . fullMessageLength . length - 1 ] +
142+ md . messageLengthSize
143+ )
161144
162145 // add padding for overflow blockSize - overflow
163- // _padding starts with 1 byte with first bit is set (byte value 128), then
164- // there may be up to (blockSize - 1) other pad bytes
165146 const overflow = remaining & ( md . blockLength - 1 )
166147 if ( _padding === null ) {
167148 throw new Error ( 'SHA-1 padding not initialized' )
168149 }
169- finalBlock . putBytes ( _padding . substr ( 0 , md . blockLength - overflow ) )
170150
171- // serialize message length in bits in big-endian order; since length
172- // is stored in bytes we multiply by 8 and add carry from next int
173- let next : number , carry : number
151+ // add padding
152+ const padLength = overflow < 56 ? 56 - overflow : 120 - overflow
153+ finalBlock . putBytes ( _padding . substr ( 0 , padLength ) )
154+
155+ // serialize message length in bits in big-endian order
174156 let bits = md . fullMessageLength [ 0 ] * 8
157+ const finalState = {
158+ h0 : _state ! . h0 ,
159+ h1 : _state ! . h1 ,
160+ h2 : _state ! . h2 ,
161+ h3 : _state ! . h3 ,
162+ h4 : _state ! . h4 ,
163+ }
164+
175165 for ( let i = 0 ; i < md . fullMessageLength . length - 1 ; ++ i ) {
176- next = md . fullMessageLength [ i + 1 ] * 8
177- carry = ( next / 0x100000000 ) >>> 0
166+ const next = md . fullMessageLength [ i + 1 ] * 8
167+ const carry = ( next / 0x100000000 ) >>> 0
178168 bits += carry
179169 finalBlock . putInt32 ( bits >>> 0 )
180170 bits = next >>> 0
181171 }
182172 finalBlock . putInt32 ( bits )
183173
184- const s2 = {
185- h0 : _state ! . h0 ,
186- h1 : _state ! . h1 ,
187- h2 : _state ! . h2 ,
188- h3 : _state ! . h3 ,
189- h4 : _state ! . h4 ,
190- }
191- _update ( s2 , _w , finalBlock )
174+ // update state one last time
175+ _update ( finalState , _w , finalBlock )
176+
177+ // build final hash value
192178 const rval = createBuffer ( )
193- rval . putInt32 ( s2 . h0 )
194- rval . putInt32 ( s2 . h1 )
195- rval . putInt32 ( s2 . h2 )
196- rval . putInt32 ( s2 . h3 )
197- rval . putInt32 ( s2 . h4 )
179+ rval . putInt32 ( finalState . h0 )
180+ rval . putInt32 ( finalState . h1 )
181+ rval . putInt32 ( finalState . h2 )
182+ rval . putInt32 ( finalState . h3 )
183+ rval . putInt32 ( finalState . h4 )
184+
185+ // reset state for next use
186+ _input = createBuffer ( )
187+ _state = {
188+ h0 : 0x67452301 ,
189+ h1 : 0xEFCDAB89 ,
190+ h2 : 0x98BADCFE ,
191+ h3 : 0x10325476 ,
192+ h4 : 0xC3D2E1F0 ,
193+ }
194+
198195 return rval
199196 } ,
200197 }
@@ -237,108 +234,56 @@ function _update(s: {
237234} , w : number [ ] , bytes : ByteStringBuffer ) {
238235 // consume 512 bit (64 byte) chunks
239236 let t : number , a : number , b : number , c : number , d : number , e : number , f : number
240- let i : number
241237 let len = bytes . length ( )
242238 while ( len >= 64 ) {
243- // the w array will be populated with sixteen 32-bit big-endian words
244- // and then extended into 80 32-bit words according to SHA-1 algorithm
245- // and for 32-79 using Max Locktyukhin's optimization
246-
247239 // initialize hash value for this chunk
248240 a = s . h0
249241 b = s . h1
250242 c = s . h2
251243 d = s . h3
252244 e = s . h4
253245
254- // round 1
255- for ( i = 0 ; i < 16 ; ++ i ) {
256- t = bytes . getInt32 ( )
257- w [ i ] = t
258- f = d ^ ( b & ( c ^ d ) )
259- t = ( ( a << 5 ) | ( a >>> 27 ) ) + f + e + 0x5A827999 + t
260- e = d
261- d = c
262- // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
263- c = ( ( b << 30 ) | ( b >>> 2 ) ) >>> 0
264- b = a
265- a = t
266- }
267- for ( ; i < 20 ; ++ i ) {
268- t = ( w [ i - 3 ] ^ w [ i - 8 ] ^ w [ i - 14 ] ^ w [ i - 16 ] )
269- t = ( t << 1 ) | ( t >>> 31 )
270- w [ i ] = t
271- f = d ^ ( b & ( c ^ d ) )
272- t = ( ( a << 5 ) | ( a >>> 27 ) ) + f + e + 0x5A827999 + t
273- e = d
274- d = c
275- // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
276- c = ( ( b << 30 ) | ( b >>> 2 ) ) >>> 0
277- b = a
278- a = t
279- }
280- // round 2
281- for ( ; i < 32 ; ++ i ) {
282- t = ( w [ i - 3 ] ^ w [ i - 8 ] ^ w [ i - 14 ] ^ w [ i - 16 ] )
283- t = ( t << 1 ) | ( t >>> 31 )
284- w [ i ] = t
285- f = b ^ c ^ d
286- t = ( ( a << 5 ) | ( a >>> 27 ) ) + f + e + 0x6ED9EBA1 + t
287- e = d
288- d = c
289- // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
290- c = ( ( b << 30 ) | ( b >>> 2 ) ) >>> 0
291- b = a
292- a = t
246+ // The w array will be populated with sixteen 32-bit big-endian words
247+ for ( let i = 0 ; i < 16 ; ++ i ) {
248+ w [ i ] = bytes . getInt32 ( )
293249 }
294- for ( ; i < 40 ; ++ i ) {
295- t = ( w [ i - 6 ] ^ w [ i - 16 ] ^ w [ i - 28 ] ^ w [ i - 32 ] )
296- t = ( t << 2 ) | ( t >>> 30 )
297- w [ i ] = t
298- f = b ^ c ^ d
299- t = ( ( a << 5 ) | ( a >>> 27 ) ) + f + e + 0x6ED9EBA1 + t
300- e = d
301- d = c
302- // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
303- c = ( ( b << 30 ) | ( b >>> 2 ) ) >>> 0
304- b = a
305- a = t
306- }
307- // round 3
308- for ( ; i < 60 ; ++ i ) {
309- t = ( w [ i - 6 ] ^ w [ i - 16 ] ^ w [ i - 28 ] ^ w [ i - 32 ] )
310- t = ( t << 2 ) | ( t >>> 30 )
311- w [ i ] = t
312- f = ( b & c ) | ( d & ( b ^ c ) )
313- t = ( ( a << 5 ) | ( a >>> 27 ) ) + f + e + 0x8F1BBCDC + t
314- e = d
315- d = c
316- // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
317- c = ( ( b << 30 ) | ( b >>> 2 ) ) >>> 0
318- b = a
319- a = t
250+
251+ // Extend into 80 32-bit words
252+ for ( let i = 16 ; i < 80 ; ++ i ) {
253+ t = w [ i - 3 ] ^ w [ i - 8 ] ^ w [ i - 14 ] ^ w [ i - 16 ]
254+ w [ i ] = ( ( t << 1 ) | ( t >>> 31 ) ) >>> 0
320255 }
321- // round 4
322- for ( ; i < 80 ; ++ i ) {
323- t = ( w [ i - 6 ] ^ w [ i - 16 ] ^ w [ i - 28 ] ^ w [ i - 32 ] )
324- t = ( t << 2 ) | ( t >>> 30 )
325- w [ i ] = t
326- f = b ^ c ^ d
327- t = ( ( a << 5 ) | ( a >>> 27 ) ) + f + e + 0xCA62C1D6 + t
256+
257+ // Round function
258+ for ( let i = 0 ; i < 80 ; ++ i ) {
259+ if ( i < 20 ) {
260+ f = ( b & c ) | ( ( ~ b ) & d )
261+ t = 0x5A827999
262+ } else if ( i < 40 ) {
263+ f = b ^ c ^ d
264+ t = 0x6ED9EBA1
265+ } else if ( i < 60 ) {
266+ f = ( b & c ) | ( b & d ) | ( c & d )
267+ t = 0x8F1BBCDC
268+ } else {
269+ f = b ^ c ^ d
270+ t = 0xCA62C1D6
271+ }
272+
273+ t = ( ( ( a << 5 ) | ( a >>> 27 ) ) + f + e + t + w [ i ] ) >>> 0
328274 e = d
329275 d = c
330- // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
331276 c = ( ( b << 30 ) | ( b >>> 2 ) ) >>> 0
332277 b = a
333278 a = t
334279 }
335280
336- // update hash state
337- s . h0 = ( s . h0 + a ) | 0
338- s . h1 = ( s . h1 + b ) | 0
339- s . h2 = ( s . h2 + c ) | 0
340- s . h3 = ( s . h3 + d ) | 0
341- s . h4 = ( s . h4 + e ) | 0
281+ // update 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+ s . h4 = ( s . h4 + e ) >>> 0
342287
343288 len -= 64
344289 }
@@ -347,3 +292,5 @@ function _update(s: {
347292export const sha1 : { create : typeof create } = {
348293 create,
349294}
295+
296+ export default sha1
0 commit comments