1
1
/*---------------------------------------------------------
2
2
* Copyright (C) Microsoft Corporation. All rights reserved.
3
3
*--------------------------------------------------------*/
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' ;
5
10
import { promises as fs } from 'fs' ;
6
11
import { MessagePort , parentPort } from 'worker_threads' ;
7
12
@@ -13,17 +18,29 @@ export const enum MessageType {
13
18
}
14
19
15
20
export const enum HashMode {
21
+ // Legacy hash mode, pre-https://chromium-review.googlesource.com/c/v8/v8/+/3229957
16
22
Chromehash ,
23
+ // BOM-aware hash mode, used by Chrome/Browsers. (Hashes the contents normalized to UTF-8)
17
24
SHA256 ,
25
+ // Naive hash mode, used by Node.js. (Hashes the raw file bytes)
26
+ SHA256Naive ,
18
27
}
19
28
29
+ export const shaHashNaive = ( input : Buffer ) => createHash ( 'sha256' ) . update ( input ) . digest ( 'hex' ) ;
30
+
20
31
/**
21
32
* Message sent to the hash worker.
22
33
*/
23
34
export type HashRequest =
24
35
| { type : MessageType . HashFile ; id : number ; file : string ; mode : HashMode }
25
36
| { 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
+ }
27
44
| {
28
45
type : MessageType . VerifyBytes ;
29
46
id : number ;
@@ -74,9 +91,33 @@ const LF = Buffer.from('\n')[0];
74
91
75
92
const hasPrefix = ( buf : Buffer , prefix : Buffer ) => buf . slice ( 0 , prefix . length ) . equals ( prefix ) ;
76
93
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
+
77
119
const 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 ) ) {
80
121
return true ;
81
122
}
82
123
@@ -88,16 +129,16 @@ const verifyBytes = (bytes: Buffer, expected: string, checkNode: boolean) => {
88
129
end -- ;
89
130
}
90
131
91
- return hashFn ( bytes . slice ( end ) ) === expected ;
132
+ return verifyHash ( expected , bytes . subarray ( end ) ) ;
92
133
}
93
134
94
- if ( hashFn ( Buffer . concat ( [ nodePrefix , bytes , nodeSuffix ] ) ) === expected ) {
135
+ if ( verifyHash ( expected , nodePrefix , bytes , nodeSuffix ) ) {
95
136
return true ;
96
137
}
97
138
}
98
139
99
140
// 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 ) ) {
101
142
return true ;
102
143
}
103
144
@@ -114,14 +155,18 @@ async function handle(message: HashRequest): Promise<HashResponse<HashRequest>>
114
155
const data = await fs . readFile ( message . file ) ;
115
156
return {
116
157
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 ) ,
118
163
} ;
119
164
} catch ( e ) {
120
165
return { id : message . id } ;
121
166
}
122
167
case MessageType . HashBytes :
123
168
try {
124
- return { id : message . id , hash : hash ( toBuffer ( message . data ) ) } ;
169
+ return { id : message . id , hash : chromeHash ( toBuffer ( message . data ) ) } ;
125
170
} catch ( e ) {
126
171
return { id : message . id } ;
127
172
}
0 commit comments