@@ -369,27 +369,152 @@ function makeCacheKey(
369369}
370370
371371/**
372- * Generates a 64-bit hash representation of a string by combining two independent 32-bit
373- * DJB2 hashes (one with multiplier 31, one with multiplier 33).
372+ * Generates a SHA-256 hash representation of a string.
374373 *
375- * Using a 64-bit keyspace virtually eliminates the probability of any accidental transfer
376- * cache key collisions.
374+ * Note: A custom synchronous SHA-256 implementation is used here because
375+ * Web Crypto API (`crypto.subtle.digest`) is strictly asynchronous (Promise-based),
376+ * whereas the transfer cache state lookup and interceptor flow must operate synchronously due to the HttpResource API.
377377 */
378- function generateHash ( value : string ) : string {
379- const padding = '00000000' ;
380- let hash1 = 0 ;
381- let hash2 = 5381 ;
382-
383- for ( let i = 0 ; i < value . length ; i ++ ) {
384- const charCode = value . charCodeAt ( i ) ;
385- hash1 = ( Math . imul ( 31 , hash1 ) + charCode ) << 0 ;
386- hash2 = ( Math . imul ( 33 , hash2 ) + charCode ) << 0 ;
387- }
378+ export function generateHash ( value : string ) : string {
379+ const inputBytes = new TextEncoder ( ) . encode ( value ) ;
380+
381+ // Initial hash values (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
382+ let hashState0 = 0x6a09e667 ;
383+ let hashState1 = 0xbb67ae85 ;
384+ let hashState2 = 0x3c6ef372 ;
385+ let hashState3 = 0xa54ff53a ;
386+ let hashState4 = 0x510e527f ;
387+ let hashState5 = 0x9b05688c ;
388+ let hashState6 = 0x1f83d9ab ;
389+ let hashState7 = 0x5be0cd19 ;
390+
391+ // SHA-256 Constants (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
392+ const SHA256_ROUND_CONSTANTS = [
393+ 0x428a2f98 , 0x71374491 , 0xb5c0fbcf , 0xe9b5dba5 , 0x3956c25b , 0x59f111f1 , 0x923f82a4 , 0xab1c5ed5 ,
394+ 0xd807aa98 , 0x12835b01 , 0x243185be , 0x550c7dc3 , 0x72be5d74 , 0x80deb1fe , 0x9bdc06a7 , 0xc19bf174 ,
395+ 0xe49b69c1 , 0xefbe4786 , 0x0fc19dc6 , 0x240ca1cc , 0x2de92c6f , 0x4a7484aa , 0x5cb0a9dc , 0x76f988da ,
396+ 0x983e5152 , 0xa831c66d , 0xb00327c8 , 0xbf597fc7 , 0xc6e00bf3 , 0xd5a79147 , 0x06ca6351 , 0x14292967 ,
397+ 0x27b70a85 , 0x2e1b2138 , 0x4d2c6dfc , 0x53380d13 , 0x650a7354 , 0x766a0abb , 0x81c2c92e , 0x92722c85 ,
398+ 0xa2bfe8a1 , 0xa81a664b , 0xc24b8b70 , 0xc76c51a3 , 0xd192e819 , 0xd6990624 , 0xf40e3585 , 0x106aa070 ,
399+ 0x19a4c116 , 0x1e376c08 , 0x2748774c , 0x34b0bcb5 , 0x391c0cb3 , 0x4ed8aa4a , 0x5b9cca4f , 0x682e6ff3 ,
400+ 0x748f82ee , 0x78a5636f , 0x84c87814 , 0x8cc70208 , 0x90befffa , 0xa4506ceb , 0xbef9a3f7 , 0xc67178f2 ,
401+ ] ;
402+
403+ // Pre-processing (Padding):
404+ const messageLengthInBits = inputBytes . length * 8 ;
405+
406+ // The total length of the padded message must be a multiple of 64 bytes (512 bits)
407+ const paddedLengthInBytes = ( ( ( inputBytes . length + 8 ) >> 6 ) + 1 ) << 6 ;
408+ const paddedBytes = new Uint8Array ( paddedLengthInBytes ) ;
409+ paddedBytes . set ( inputBytes ) ;
410+ paddedBytes [ inputBytes . length ] = 0x80 ; // Append a single '1' bit (0x80 byte)
411+
412+ const paddedBytesView = new DataView ( paddedBytes . buffer ) ;
413+ const lowBits = messageLengthInBits >>> 0 ;
414+ const highBits = ( messageLengthInBits / 0x100000000 ) >>> 0 ;
415+ paddedBytesView . setUint32 ( paddedLengthInBytes - 8 , highBits , false ) ;
416+ paddedBytesView . setUint32 ( paddedLengthInBytes - 4 , lowBits , false ) ;
417+
418+ // Process the message in successive 64-byte chunks:
419+ const messageSchedule = new Uint32Array ( 64 ) ;
420+ for ( let chunkOffset = 0 ; chunkOffset < paddedLengthInBytes ; chunkOffset += 64 ) {
421+ // Initialize first 16 words of the message schedule:
422+ for ( let i = 0 ; i < 16 ; i ++ ) {
423+ messageSchedule [ i ] = paddedBytesView . getUint32 ( chunkOffset + i * 4 , false ) ;
424+ }
425+
426+ // Extend to 64 words:
427+ for ( let i = 16 ; i < 64 ; i ++ ) {
428+ const prevWord15 = messageSchedule [ i - 15 ] ;
429+ const sigma0 =
430+ ( ( ( prevWord15 >>> 7 ) | ( prevWord15 << 25 ) ) ^
431+ ( ( prevWord15 >>> 18 ) | ( prevWord15 << 14 ) ) ^
432+ ( prevWord15 >>> 3 ) ) >>>
433+ 0 ;
434+
435+ const prevWord2 = messageSchedule [ i - 2 ] ;
436+ const sigma1 =
437+ ( ( ( prevWord2 >>> 17 ) | ( prevWord2 << 15 ) ) ^
438+ ( ( prevWord2 >>> 19 ) | ( prevWord2 << 13 ) ) ^
439+ ( prevWord2 >>> 10 ) ) >>>
440+ 0 ;
441+
442+ messageSchedule [ i ] =
443+ ( messageSchedule [ i - 16 ] + sigma0 + messageSchedule [ i - 7 ] + sigma1 ) >>> 0 ;
444+ }
388445
389- const hex1 = ( padding + ( hash1 >>> 0 ) . toString ( 16 ) ) . slice ( - 8 ) ;
390- const hex2 = ( padding + ( hash2 >>> 0 ) . toString ( 16 ) ) . slice ( - 8 ) ;
446+ // Initialize working variables to current hash values:
447+ let workingStateA = hashState0 ;
448+ let workingStateB = hashState1 ;
449+ let workingStateC = hashState2 ;
450+ let workingStateD = hashState3 ;
451+ let workingStateE = hashState4 ;
452+ let workingStateF = hashState5 ;
453+ let workingStateG = hashState6 ;
454+ let workingStateH = hashState7 ;
455+
456+ // Compression function main loop:
457+ for ( let i = 0 ; i < 64 ; i ++ ) {
458+ const capitalSigma1 =
459+ ( ( ( workingStateE >>> 6 ) | ( workingStateE << 26 ) ) ^
460+ ( ( workingStateE >>> 11 ) | ( workingStateE << 21 ) ) ^
461+ ( ( workingStateE >>> 25 ) | ( workingStateE << 7 ) ) ) >>>
462+ 0 ;
463+ const chFunction = ( ( workingStateE & workingStateF ) ^ ( ~ workingStateE & workingStateG ) ) >>> 0 ;
464+ const temp1 =
465+ ( workingStateH +
466+ capitalSigma1 +
467+ chFunction +
468+ SHA256_ROUND_CONSTANTS [ i ] +
469+ messageSchedule [ i ] ) >>>
470+ 0 ;
471+
472+ const capitalSigma0 =
473+ ( ( ( workingStateA >>> 2 ) | ( workingStateA << 30 ) ) ^
474+ ( ( workingStateA >>> 13 ) | ( workingStateA << 19 ) ) ^
475+ ( ( workingStateA >>> 22 ) | ( workingStateA << 10 ) ) ) >>>
476+ 0 ;
477+ const majFunction =
478+ ( ( workingStateA & workingStateB ) ^
479+ ( workingStateA & workingStateC ) ^
480+ ( workingStateB & workingStateC ) ) >>>
481+ 0 ;
482+ const temp2 = ( capitalSigma0 + majFunction ) >>> 0 ;
483+
484+ workingStateH = workingStateG ;
485+ workingStateG = workingStateF ;
486+ workingStateF = workingStateE ;
487+ workingStateE = ( workingStateD + temp1 ) >>> 0 ;
488+ workingStateD = workingStateC ;
489+ workingStateC = workingStateB ;
490+ workingStateB = workingStateA ;
491+ workingStateA = ( temp1 + temp2 ) >>> 0 ;
492+ }
391493
392- return hex1 + hex2 ;
494+ // Update intermediate hash state:
495+ hashState0 = ( hashState0 + workingStateA ) >>> 0 ;
496+ hashState1 = ( hashState1 + workingStateB ) >>> 0 ;
497+ hashState2 = ( hashState2 + workingStateC ) >>> 0 ;
498+ hashState3 = ( hashState3 + workingStateD ) >>> 0 ;
499+ hashState4 = ( hashState4 + workingStateE ) >>> 0 ;
500+ hashState5 = ( hashState5 + workingStateF ) >>> 0 ;
501+ hashState6 = ( hashState6 + workingStateG ) >>> 0 ;
502+ hashState7 = ( hashState7 + workingStateH ) >>> 0 ;
503+ }
504+
505+ // Produce the final 64-character hexadecimal hash:
506+ return [
507+ hashState0 ,
508+ hashState1 ,
509+ hashState2 ,
510+ hashState3 ,
511+ hashState4 ,
512+ hashState5 ,
513+ hashState6 ,
514+ hashState7 ,
515+ ]
516+ . map ( ( x ) => x . toString ( 16 ) . padStart ( 8 , '0' ) )
517+ . join ( '' ) ;
393518}
394519
395520function toBase64 ( buffer : unknown ) : string {
0 commit comments