11/*--------------------------------------------------------- 
22 * Copyright (C) Microsoft Corporation. All rights reserved. 
33 *--------------------------------------------------------*/ 
4- import  {  hash ,  shaHash  }  from  '@c4312/chromehash' ; 
4+ import  { 
5+   hash  as  chromeHash , 
6+   normalizeShaBuffer , 
7+   shaHash  as  chromeShaHash , 
8+ }  from  '@c4312/chromehash' ; 
9+ import  {  createHash  }  from  'crypto' ; 
510import  {  promises  as  fs  }  from  'fs' ; 
611import  {  MessagePort ,  parentPort  }  from  'worker_threads' ; 
712
@@ -13,17 +18,29 @@ export const enum MessageType {
1318} 
1419
1520export  const  enum  HashMode  { 
21+   // Legacy hash mode, pre-https://chromium-review.googlesource.com/c/v8/v8/+/3229957 
1622  Chromehash , 
23+   // BOM-aware hash mode, used by Chrome/Browsers. (Hashes the contents normalized to UTF-8) 
1724  SHA256 , 
25+   // Naive hash mode, used by Node.js. (Hashes the raw file bytes) 
26+   SHA256Naive , 
1827} 
1928
29+ export  const  shaHashNaive  =  ( input : Buffer )  =>  createHash ( 'sha256' ) . update ( input ) . digest ( 'hex' ) ; 
30+ 
2031/** 
2132 * Message sent to the hash worker. 
2233 */ 
2334export  type  HashRequest  = 
2435  |  {  type : MessageType . HashFile ;  id : number ;  file : string ;  mode : HashMode  } 
2536  |  {  type : MessageType . HashBytes ;  id : number ;  data : string  |  Buffer ;  mode : HashMode  } 
26-   |  {  type : MessageType . VerifyFile ;  id : number ;  file : string ;  expected : string ;  checkNode : boolean  } 
37+   |  { 
38+     type : MessageType . VerifyFile ; 
39+     id : number ; 
40+     file : string ; 
41+     expected : string ; 
42+     checkNode : boolean ; 
43+   } 
2744  |  { 
2845    type : MessageType . VerifyBytes ; 
2946    id : number ; 
@@ -74,9 +91,33 @@ const LF = Buffer.from('\n')[0];
7491
7592const  hasPrefix  =  ( buf : Buffer ,  prefix : Buffer )  =>  buf . slice ( 0 ,  prefix . length ) . equals ( prefix ) ; 
7693
94+ const  verifyHash  =  ( expected : string ,  ...data : Buffer [ ] )  =>  { 
95+   if  ( expected . length  !==  64 )  { 
96+     return  chromeHash ( data . length  ===  1  ? data [ 0 ]  : Buffer . concat ( data ) )  ===  expected ; 
97+   } 
98+ 
99+   // Check if the non-normalized hash matches first. We check both because we 
100+   // want to check with both BOM-normalization (used in Chrome) and 
101+   // without it (used in Node.js). 
102+   const  nonNormalizedHash  =  createHash ( 'sha256' ) ; 
103+   for  ( const  d  of  data )  { 
104+     nonNormalizedHash . update ( d ) ; 
105+   } 
106+   if  ( nonNormalizedHash . digest ( 'hex' )  ===  expected )  { 
107+     return  true ; 
108+   } 
109+ 
110+   const  normalizedInput  =  data . length  ===  1  ? data [ 0 ]  : Buffer . concat ( data ) ; 
111+   const  normalizedOutput  =  normalizeShaBuffer ( normalizedInput ) ; 
112+   if  ( normalizedInput  ===  normalizedOutput )  { 
113+     return  false ; 
114+   } 
115+ 
116+   return  createHash ( 'sha256' ) . update ( normalizedOutput ) . digest ( 'hex' )  ===  expected ; 
117+ } ; 
118+ 
77119const  verifyBytes  =  ( bytes : Buffer ,  expected : string ,  checkNode : boolean )  =>  { 
78-   const  hashFn  =  expected . length  ===  64  ? shaHash  : hash ; 
79-   if  ( hashFn ( bytes )  ===  expected )  { 
120+   if  ( verifyHash ( expected ,  bytes ) )  { 
80121    return  true ; 
81122  } 
82123
@@ -88,16 +129,16 @@ const verifyBytes = (bytes: Buffer, expected: string, checkNode: boolean) => {
88129        end -- ; 
89130      } 
90131
91-       return  hashFn ( bytes . slice ( end ) )   ===   expected ; 
132+       return  verifyHash ( expected ,   bytes . subarray ( end ) ) ; 
92133    } 
93134
94-     if  ( hashFn ( Buffer . concat ( [ nodePrefix ,  bytes ,  nodeSuffix ] ) )   ===   expected )  { 
135+     if  ( verifyHash ( expected ,   nodePrefix ,  bytes ,  nodeSuffix ) )  { 
95136      return  true ; 
96137    } 
97138  } 
98139
99140  // todo -- doing a lot of concats, make chromehash able to hash an iterable of buffers? 
100-   if  ( hashFn ( Buffer . concat ( [ electronPrefix ,  bytes ,  electronSuffix ] ) )   ===   expected )  { 
141+   if  ( verifyHash ( expected ,   electronPrefix ,  bytes ,  electronSuffix ) )  { 
101142    return  true ; 
102143  } 
103144
@@ -114,14 +155,18 @@ async function handle(message: HashRequest): Promise<HashResponse<HashRequest>>
114155        const  data  =  await  fs . readFile ( message . file ) ; 
115156        return  { 
116157          id : message . id , 
117-           hash : message . mode  ===  HashMode . Chromehash  ? hash ( data )  : shaHash ( data ) , 
158+           hash : message . mode  ===  HashMode . Chromehash 
159+             ? chromeHash ( data ) 
160+             : message . mode  ===  HashMode . SHA256Naive 
161+             ? shaHashNaive ( data ) 
162+             : chromeShaHash ( data ) , 
118163        } ; 
119164      }  catch  ( e )  { 
120165        return  {  id : message . id  } ; 
121166      } 
122167    case  MessageType . HashBytes :
123168      try  { 
124-         return  {  id : message . id ,  hash : hash ( toBuffer ( message . data ) )  } ; 
169+         return  {  id : message . id ,  hash : chromeHash ( toBuffer ( message . data ) )  } ; 
125170      }  catch  ( e )  { 
126171        return  {  id : message . id  } ; 
127172      } 
0 commit comments