meshcore-hashtag-cracker 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/dist/core.d.ts +69 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +244 -0
- package/dist/core.js.map +1 -0
- package/dist/cpu-bruteforce.d.ts +13 -0
- package/dist/cpu-bruteforce.d.ts.map +1 -0
- package/dist/cpu-bruteforce.js +45 -0
- package/dist/cpu-bruteforce.js.map +1 -0
- package/dist/cracker.d.ts +64 -0
- package/dist/cracker.d.ts.map +1 -0
- package/dist/cracker.js +358 -0
- package/dist/cracker.js.map +1 -0
- package/dist/gpu-bruteforce.d.ts +34 -0
- package/dist/gpu-bruteforce.d.ts.map +1 -0
- package/dist/gpu-bruteforce.js +645 -0
- package/dist/gpu-bruteforce.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
// WebGPU-accelerated brute force key cracking for MeshCore packets
|
|
2
|
+
import { indexToRoomName, countNamesForLength } from './core';
|
|
3
|
+
export class GpuBruteForce {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.device = null;
|
|
6
|
+
this.pipeline = null;
|
|
7
|
+
this.bindGroupLayout = null;
|
|
8
|
+
// Persistent buffers for reuse between batches
|
|
9
|
+
this.paramsBuffer = null;
|
|
10
|
+
this.matchCountBuffer = null;
|
|
11
|
+
this.matchIndicesBuffer = null;
|
|
12
|
+
this.ciphertextBuffer = null;
|
|
13
|
+
this.ciphertextBufferSize = 0;
|
|
14
|
+
// Double-buffered staging buffers for overlapping GPU/CPU work
|
|
15
|
+
this.matchCountReadBuffers = [null, null];
|
|
16
|
+
this.matchIndicesReadBuffers = [null, null];
|
|
17
|
+
this.currentReadBufferIndex = 0;
|
|
18
|
+
// Cached bind group (recreated only when ciphertext buffer changes)
|
|
19
|
+
this.bindGroup = null;
|
|
20
|
+
this.bindGroupDirty = true;
|
|
21
|
+
// Shader for SHA256 computation
|
|
22
|
+
this.shaderCode = `
|
|
23
|
+
// SHA256 round constants
|
|
24
|
+
const K: array<u32, 64> = array<u32, 64>(
|
|
25
|
+
0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u, 0x3956c25bu, 0x59f111f1u, 0x923f82a4u, 0xab1c5ed5u,
|
|
26
|
+
0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u, 0x72be5d74u, 0x80deb1feu, 0x9bdc06a7u, 0xc19bf174u,
|
|
27
|
+
0xe49b69c1u, 0xefbe4786u, 0x0fc19dc6u, 0x240ca1ccu, 0x2de92c6fu, 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau,
|
|
28
|
+
0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u, 0xc6e00bf3u, 0xd5a79147u, 0x06ca6351u, 0x14292967u,
|
|
29
|
+
0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu, 0x53380d13u, 0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u,
|
|
30
|
+
0xa2bfe8a1u, 0xa81a664bu, 0xc24b8b70u, 0xc76c51a3u, 0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u,
|
|
31
|
+
0x19a4c116u, 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u, 0x391c0cb3u, 0x4ed8aa4au, 0x5b9cca4fu, 0x682e6ff3u,
|
|
32
|
+
0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u, 0x90befffau, 0xa4506cebu, 0xbef9a3f7u, 0xc67178f2u
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Character lookup table (a-z = 0-25, 0-9 = 26-35, dash = 36)
|
|
36
|
+
const CHARS: array<u32, 37> = array<u32, 37>(
|
|
37
|
+
0x61u, 0x62u, 0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u, 0x69u, 0x6au, // a-j
|
|
38
|
+
0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x70u, 0x71u, 0x72u, 0x73u, 0x74u, // k-t
|
|
39
|
+
0x75u, 0x76u, 0x77u, 0x78u, 0x79u, 0x7au, // u-z
|
|
40
|
+
0x30u, 0x31u, 0x32u, 0x33u, 0x34u, 0x35u, 0x36u, 0x37u, 0x38u, 0x39u, // 0-9
|
|
41
|
+
0x2du // dash
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
struct Params {
|
|
45
|
+
target_channel_hash: u32,
|
|
46
|
+
batch_offset: u32,
|
|
47
|
+
name_length: u32,
|
|
48
|
+
batch_size: u32,
|
|
49
|
+
target_mac: u32, // First 2 bytes of target MAC (in high 16 bits)
|
|
50
|
+
ciphertext_words: u32, // Number of 32-bit words in ciphertext
|
|
51
|
+
ciphertext_len_bits: u32, // Length of ciphertext in bits
|
|
52
|
+
verify_mac: u32, // 1 to verify MAC, 0 to skip
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@group(0) @binding(0) var<uniform> params: Params;
|
|
56
|
+
@group(0) @binding(1) var<storage, read_write> match_count: atomic<u32>;
|
|
57
|
+
@group(0) @binding(2) var<storage, read_write> match_indices: array<u32>;
|
|
58
|
+
@group(0) @binding(3) var<storage, read> ciphertext: array<u32>; // Ciphertext data
|
|
59
|
+
|
|
60
|
+
fn rotr(x: u32, n: u32) -> u32 {
|
|
61
|
+
return (x >> n) | (x << (32u - n));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn ch(x: u32, y: u32, z: u32) -> u32 {
|
|
65
|
+
return (x & y) ^ (~x & z);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fn maj(x: u32, y: u32, z: u32) -> u32 {
|
|
69
|
+
return (x & y) ^ (x & z) ^ (y & z);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn sigma0(x: u32) -> u32 {
|
|
73
|
+
return rotr(x, 2u) ^ rotr(x, 13u) ^ rotr(x, 22u);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fn sigma1(x: u32) -> u32 {
|
|
77
|
+
return rotr(x, 6u) ^ rotr(x, 11u) ^ rotr(x, 25u);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn gamma0(x: u32) -> u32 {
|
|
81
|
+
return rotr(x, 7u) ^ rotr(x, 18u) ^ (x >> 3u);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn gamma1(x: u32) -> u32 {
|
|
85
|
+
return rotr(x, 17u) ^ rotr(x, 19u) ^ (x >> 10u);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Convert index to room name bytes, returns the hash as a u32 for the first byte check
|
|
89
|
+
fn index_to_room_name(idx: u32, length: u32, msg: ptr<function, array<u32, 16>>) -> bool {
|
|
90
|
+
// Message starts with '#' (0x23)
|
|
91
|
+
var byte_pos = 0u;
|
|
92
|
+
var word_idx = 0u;
|
|
93
|
+
var current_word = 0x23000000u; // '#' in big-endian position 0
|
|
94
|
+
byte_pos = 1u;
|
|
95
|
+
|
|
96
|
+
var remaining = idx;
|
|
97
|
+
var prev_was_dash = false;
|
|
98
|
+
|
|
99
|
+
// Generate room name from index
|
|
100
|
+
for (var i = 0u; i < length; i++) {
|
|
101
|
+
let char_count = select(37u, 36u, i == 0u || i == length - 1u); // no dash at start/end
|
|
102
|
+
var char_idx = remaining % char_count;
|
|
103
|
+
remaining = remaining / char_count;
|
|
104
|
+
|
|
105
|
+
// Check for consecutive dashes (invalid)
|
|
106
|
+
let is_dash = char_idx == 36u && i > 0u && i < length - 1u;
|
|
107
|
+
if (is_dash && prev_was_dash) {
|
|
108
|
+
return false; // Invalid: consecutive dashes
|
|
109
|
+
}
|
|
110
|
+
prev_was_dash = is_dash;
|
|
111
|
+
|
|
112
|
+
// Map char index to actual character
|
|
113
|
+
let c = CHARS[char_idx];
|
|
114
|
+
|
|
115
|
+
// Pack byte into current word (big-endian)
|
|
116
|
+
let shift = (3u - byte_pos % 4u) * 8u;
|
|
117
|
+
if (byte_pos % 4u == 0u && byte_pos > 0u) {
|
|
118
|
+
(*msg)[word_idx] = current_word;
|
|
119
|
+
word_idx = word_idx + 1u;
|
|
120
|
+
current_word = 0u;
|
|
121
|
+
}
|
|
122
|
+
current_word = current_word | (c << shift);
|
|
123
|
+
byte_pos = byte_pos + 1u;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Add padding: 0x80 followed by zeros, then length in bits
|
|
127
|
+
let msg_len_bits = (length + 1u) * 8u; // +1 for '#'
|
|
128
|
+
|
|
129
|
+
// Add 0x80 padding byte
|
|
130
|
+
let shift = (3u - byte_pos % 4u) * 8u;
|
|
131
|
+
if (byte_pos % 4u == 0u) {
|
|
132
|
+
(*msg)[word_idx] = current_word;
|
|
133
|
+
word_idx = word_idx + 1u;
|
|
134
|
+
current_word = 0x80000000u;
|
|
135
|
+
} else {
|
|
136
|
+
current_word = current_word | (0x80u << shift);
|
|
137
|
+
}
|
|
138
|
+
byte_pos = byte_pos + 1u;
|
|
139
|
+
|
|
140
|
+
// Store current word
|
|
141
|
+
if (byte_pos % 4u == 0u || word_idx < 14u) {
|
|
142
|
+
(*msg)[word_idx] = current_word;
|
|
143
|
+
word_idx = word_idx + 1u;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Zero-fill until word 14
|
|
147
|
+
for (var i = word_idx; i < 14u; i++) {
|
|
148
|
+
(*msg)[i] = 0u;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Length in bits (64-bit, but we only use lower 32 bits for short messages)
|
|
152
|
+
(*msg)[14u] = 0u;
|
|
153
|
+
(*msg)[15u] = msg_len_bits;
|
|
154
|
+
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
fn sha256_block(msg: ptr<function, array<u32, 16>>) -> array<u32, 8> {
|
|
159
|
+
// Initialize hash values
|
|
160
|
+
var h: array<u32, 8> = array<u32, 8>(
|
|
161
|
+
0x6a09e667u, 0xbb67ae85u, 0x3c6ef372u, 0xa54ff53au,
|
|
162
|
+
0x510e527fu, 0x9b05688cu, 0x1f83d9abu, 0x5be0cd19u
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Message schedule
|
|
166
|
+
var w: array<u32, 64>;
|
|
167
|
+
for (var i = 0u; i < 16u; i++) {
|
|
168
|
+
w[i] = (*msg)[i];
|
|
169
|
+
}
|
|
170
|
+
for (var i = 16u; i < 64u; i++) {
|
|
171
|
+
w[i] = gamma1(w[i-2u]) + w[i-7u] + gamma0(w[i-15u]) + w[i-16u];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Compression
|
|
175
|
+
var a = h[0]; var b = h[1]; var c = h[2]; var d = h[3];
|
|
176
|
+
var e = h[4]; var f = h[5]; var g = h[6]; var hh = h[7];
|
|
177
|
+
|
|
178
|
+
for (var i = 0u; i < 64u; i++) {
|
|
179
|
+
let t1 = hh + sigma1(e) + ch(e, f, g) + K[i] + w[i];
|
|
180
|
+
let t2 = sigma0(a) + maj(a, b, c);
|
|
181
|
+
hh = g; g = f; f = e; e = d + t1;
|
|
182
|
+
d = c; c = b; b = a; a = t1 + t2;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
h[0] = h[0] + a; h[1] = h[1] + b; h[2] = h[2] + c; h[3] = h[3] + d;
|
|
186
|
+
h[4] = h[4] + e; h[5] = h[5] + f; h[6] = h[6] + g; h[7] = h[7] + hh;
|
|
187
|
+
|
|
188
|
+
return h;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Compute SHA256 of the key (16 bytes) to get channel hash
|
|
192
|
+
fn sha256_key(key: array<u32, 4>) -> u32 {
|
|
193
|
+
var msg: array<u32, 16>;
|
|
194
|
+
|
|
195
|
+
// Key bytes (16 bytes = 4 words)
|
|
196
|
+
msg[0] = key[0];
|
|
197
|
+
msg[1] = key[1];
|
|
198
|
+
msg[2] = key[2];
|
|
199
|
+
msg[3] = key[3];
|
|
200
|
+
|
|
201
|
+
// Padding: 0x80 followed by zeros
|
|
202
|
+
msg[4] = 0x80000000u;
|
|
203
|
+
for (var i = 5u; i < 14u; i++) {
|
|
204
|
+
msg[i] = 0u;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Length: 128 bits
|
|
208
|
+
msg[14] = 0u;
|
|
209
|
+
msg[15] = 128u;
|
|
210
|
+
|
|
211
|
+
let hash = sha256_block(&msg);
|
|
212
|
+
|
|
213
|
+
// Return first byte of hash (big-endian)
|
|
214
|
+
return hash[0] >> 24u;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// HMAC-SHA256 for MAC verification
|
|
218
|
+
// Key is 16 bytes (4 words), padded to 32 bytes with zeros for MeshCore
|
|
219
|
+
// Returns first 2 bytes of HMAC (as u32 in high 16 bits)
|
|
220
|
+
fn hmac_sha256_mac(key: array<u32, 4>, ciphertext_len: u32) -> u32 {
|
|
221
|
+
// HMAC: H((K' ^ opad) || H((K' ^ ipad) || message))
|
|
222
|
+
// K' is 64 bytes (32 bytes key + 32 bytes zero padding for MeshCore, then padded to 64)
|
|
223
|
+
// ipad = 0x36 repeated, opad = 0x5c repeated
|
|
224
|
+
|
|
225
|
+
// Build padded key (64 bytes = 16 words)
|
|
226
|
+
// MeshCore uses 32-byte secret: 16-byte key + 16 zero bytes
|
|
227
|
+
var k_pad: array<u32, 16>;
|
|
228
|
+
k_pad[0] = key[0];
|
|
229
|
+
k_pad[1] = key[1];
|
|
230
|
+
k_pad[2] = key[2];
|
|
231
|
+
k_pad[3] = key[3];
|
|
232
|
+
for (var i = 4u; i < 16u; i++) {
|
|
233
|
+
k_pad[i] = 0u;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Inner hash: SHA256((K' ^ ipad) || message)
|
|
237
|
+
// First block: K' ^ ipad (64 bytes)
|
|
238
|
+
var inner_block: array<u32, 16>;
|
|
239
|
+
for (var i = 0u; i < 16u; i++) {
|
|
240
|
+
inner_block[i] = k_pad[i] ^ 0x36363636u;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Initialize hash state with first block
|
|
244
|
+
var h: array<u32, 8> = sha256_block(&inner_block);
|
|
245
|
+
|
|
246
|
+
// Process ciphertext blocks (continuing from h state)
|
|
247
|
+
let ciphertext_words = params.ciphertext_words;
|
|
248
|
+
var word_idx = 0u;
|
|
249
|
+
|
|
250
|
+
// Process full 64-byte blocks of ciphertext
|
|
251
|
+
while (word_idx + 16u <= ciphertext_words) {
|
|
252
|
+
var block: array<u32, 16>;
|
|
253
|
+
for (var i = 0u; i < 16u; i++) {
|
|
254
|
+
block[i] = ciphertext[word_idx + i];
|
|
255
|
+
}
|
|
256
|
+
h = sha256_block_continue(&block, h);
|
|
257
|
+
word_idx = word_idx + 16u;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Final block with remaining ciphertext + padding
|
|
261
|
+
var final_block: array<u32, 16>;
|
|
262
|
+
var remaining = ciphertext_words - word_idx;
|
|
263
|
+
for (var i = 0u; i < 16u; i++) {
|
|
264
|
+
if (i < remaining) {
|
|
265
|
+
final_block[i] = ciphertext[word_idx + i];
|
|
266
|
+
} else if (i == remaining) {
|
|
267
|
+
// Add 0x80 padding
|
|
268
|
+
final_block[i] = 0x80000000u;
|
|
269
|
+
} else {
|
|
270
|
+
final_block[i] = 0u;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Add length (64 bytes of ipad + ciphertext length)
|
|
275
|
+
let total_bits = 512u + params.ciphertext_len_bits;
|
|
276
|
+
if (remaining < 14u) {
|
|
277
|
+
final_block[14] = 0u;
|
|
278
|
+
final_block[15] = total_bits;
|
|
279
|
+
h = sha256_block_continue(&final_block, h);
|
|
280
|
+
} else {
|
|
281
|
+
// Need extra block for length
|
|
282
|
+
h = sha256_block_continue(&final_block, h);
|
|
283
|
+
var len_block: array<u32, 16>;
|
|
284
|
+
for (var i = 0u; i < 14u; i++) {
|
|
285
|
+
len_block[i] = 0u;
|
|
286
|
+
}
|
|
287
|
+
len_block[14] = 0u;
|
|
288
|
+
len_block[15] = total_bits;
|
|
289
|
+
h = sha256_block_continue(&len_block, h);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let inner_hash = h;
|
|
293
|
+
|
|
294
|
+
// Outer hash: SHA256((K' ^ opad) || inner_hash)
|
|
295
|
+
var outer_block: array<u32, 16>;
|
|
296
|
+
for (var i = 0u; i < 16u; i++) {
|
|
297
|
+
outer_block[i] = k_pad[i] ^ 0x5c5c5c5cu;
|
|
298
|
+
}
|
|
299
|
+
h = sha256_block(&outer_block);
|
|
300
|
+
|
|
301
|
+
// Second block: inner_hash (32 bytes) + padding
|
|
302
|
+
var hash_block: array<u32, 16>;
|
|
303
|
+
for (var i = 0u; i < 8u; i++) {
|
|
304
|
+
hash_block[i] = inner_hash[i];
|
|
305
|
+
}
|
|
306
|
+
hash_block[8] = 0x80000000u;
|
|
307
|
+
for (var i = 9u; i < 14u; i++) {
|
|
308
|
+
hash_block[i] = 0u;
|
|
309
|
+
}
|
|
310
|
+
hash_block[14] = 0u;
|
|
311
|
+
hash_block[15] = 512u + 256u; // 64 bytes opad + 32 bytes inner hash
|
|
312
|
+
|
|
313
|
+
h = sha256_block_continue(&hash_block, h);
|
|
314
|
+
|
|
315
|
+
// Return first 2 bytes (high 16 bits of first word)
|
|
316
|
+
return h[0] & 0xFFFF0000u;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// SHA256 block computation continuing from existing state
|
|
320
|
+
fn sha256_block_continue(msg: ptr<function, array<u32, 16>>, h_in: array<u32, 8>) -> array<u32, 8> {
|
|
321
|
+
var h = h_in;
|
|
322
|
+
|
|
323
|
+
// Message schedule
|
|
324
|
+
var w: array<u32, 64>;
|
|
325
|
+
for (var i = 0u; i < 16u; i++) {
|
|
326
|
+
w[i] = (*msg)[i];
|
|
327
|
+
}
|
|
328
|
+
for (var i = 16u; i < 64u; i++) {
|
|
329
|
+
w[i] = gamma1(w[i-2u]) + w[i-7u] + gamma0(w[i-15u]) + w[i-16u];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Compression
|
|
333
|
+
var a = h[0]; var b = h[1]; var c = h[2]; var d = h[3];
|
|
334
|
+
var e = h[4]; var f = h[5]; var g = h[6]; var hh = h[7];
|
|
335
|
+
|
|
336
|
+
for (var i = 0u; i < 64u; i++) {
|
|
337
|
+
let t1 = hh + sigma1(e) + ch(e, f, g) + K[i] + w[i];
|
|
338
|
+
let t2 = sigma0(a) + maj(a, b, c);
|
|
339
|
+
hh = g; g = f; f = e; e = d + t1;
|
|
340
|
+
d = c; c = b; b = a; a = t1 + t2;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
h[0] = h[0] + a; h[1] = h[1] + b; h[2] = h[2] + c; h[3] = h[3] + d;
|
|
344
|
+
h[4] = h[4] + e; h[5] = h[5] + f; h[6] = h[6] + g; h[7] = h[7] + hh;
|
|
345
|
+
|
|
346
|
+
return h;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Process a single candidate and record match if found
|
|
350
|
+
fn process_candidate(name_idx: u32) {
|
|
351
|
+
// Generate message for this room name
|
|
352
|
+
var msg: array<u32, 16>;
|
|
353
|
+
let valid = index_to_room_name(name_idx, params.name_length, &msg);
|
|
354
|
+
|
|
355
|
+
if (!valid) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Compute SHA256("#roomname") - this gives us the key
|
|
360
|
+
let key_hash = sha256_block(&msg);
|
|
361
|
+
|
|
362
|
+
// Take first 16 bytes (4 words) as the key
|
|
363
|
+
var key: array<u32, 4>;
|
|
364
|
+
key[0] = key_hash[0];
|
|
365
|
+
key[1] = key_hash[1];
|
|
366
|
+
key[2] = key_hash[2];
|
|
367
|
+
key[3] = key_hash[3];
|
|
368
|
+
|
|
369
|
+
// Compute SHA256(key) to get channel hash
|
|
370
|
+
let channel_hash = sha256_key(key);
|
|
371
|
+
|
|
372
|
+
// Check if channel hash matches target
|
|
373
|
+
if (channel_hash != params.target_channel_hash) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Channel hash matches - verify MAC if enabled
|
|
378
|
+
if (params.verify_mac == 1u) {
|
|
379
|
+
let computed_mac = hmac_sha256_mac(key, params.ciphertext_len_bits);
|
|
380
|
+
if (computed_mac != params.target_mac) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Found a match - record the index
|
|
386
|
+
let match_idx = atomicAdd(&match_count, 1u);
|
|
387
|
+
if (match_idx < 1024u) { // Limit stored matches
|
|
388
|
+
match_indices[match_idx] = name_idx;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Each thread processes 16 candidates to amortize thread overhead
|
|
393
|
+
const CANDIDATES_PER_THREAD: u32 = 16u;
|
|
394
|
+
|
|
395
|
+
@compute @workgroup_size(256)
|
|
396
|
+
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
|
397
|
+
let base_idx = global_id.x * CANDIDATES_PER_THREAD;
|
|
398
|
+
|
|
399
|
+
for (var i = 0u; i < CANDIDATES_PER_THREAD; i++) {
|
|
400
|
+
let idx = base_idx + i;
|
|
401
|
+
if (idx >= params.batch_size) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
let name_idx = params.batch_offset + idx;
|
|
405
|
+
process_candidate(name_idx);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
`;
|
|
409
|
+
}
|
|
410
|
+
async init() {
|
|
411
|
+
if (!navigator.gpu) {
|
|
412
|
+
console.warn('WebGPU not supported');
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
417
|
+
if (!adapter) {
|
|
418
|
+
console.warn('No GPU adapter found');
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
this.device = await adapter.requestDevice();
|
|
422
|
+
// Create bind group layout
|
|
423
|
+
this.bindGroupLayout = this.device.createBindGroupLayout({
|
|
424
|
+
entries: [
|
|
425
|
+
{ binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } },
|
|
426
|
+
{ binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } },
|
|
427
|
+
{ binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } },
|
|
428
|
+
{ binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } },
|
|
429
|
+
],
|
|
430
|
+
});
|
|
431
|
+
// Create persistent buffers
|
|
432
|
+
this.paramsBuffer = this.device.createBuffer({
|
|
433
|
+
size: 32, // 8 u32s
|
|
434
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
435
|
+
});
|
|
436
|
+
this.matchCountBuffer = this.device.createBuffer({
|
|
437
|
+
size: 4,
|
|
438
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
439
|
+
});
|
|
440
|
+
this.matchIndicesBuffer = this.device.createBuffer({
|
|
441
|
+
size: 1024 * 4, // Max 1024 matches per batch
|
|
442
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
|
|
443
|
+
});
|
|
444
|
+
// Double-buffered staging buffers
|
|
445
|
+
for (let i = 0; i < 2; i++) {
|
|
446
|
+
this.matchCountReadBuffers[i] = this.device.createBuffer({
|
|
447
|
+
size: 4,
|
|
448
|
+
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
|
|
449
|
+
});
|
|
450
|
+
this.matchIndicesReadBuffers[i] = this.device.createBuffer({
|
|
451
|
+
size: 1024 * 4,
|
|
452
|
+
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
// Create pipeline
|
|
456
|
+
const shaderModule = this.device.createShaderModule({
|
|
457
|
+
code: this.shaderCode,
|
|
458
|
+
});
|
|
459
|
+
const pipelineLayout = this.device.createPipelineLayout({
|
|
460
|
+
bindGroupLayouts: [this.bindGroupLayout],
|
|
461
|
+
});
|
|
462
|
+
this.pipeline = this.device.createComputePipeline({
|
|
463
|
+
layout: pipelineLayout,
|
|
464
|
+
compute: {
|
|
465
|
+
module: shaderModule,
|
|
466
|
+
entryPoint: 'main',
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
catch (e) {
|
|
472
|
+
console.error('WebGPU initialization failed:', e);
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
isAvailable() {
|
|
477
|
+
return this.device !== null && this.pipeline !== null;
|
|
478
|
+
}
|
|
479
|
+
// Convert room name index to actual room name string (delegates to core)
|
|
480
|
+
indexToRoomName(idx, length) {
|
|
481
|
+
return indexToRoomName(length, idx);
|
|
482
|
+
}
|
|
483
|
+
// Count valid names for a given length (delegates to core)
|
|
484
|
+
countNamesForLength(len) {
|
|
485
|
+
return countNamesForLength(len);
|
|
486
|
+
}
|
|
487
|
+
async runBatch(targetChannelHash, nameLength, batchOffset, batchSize, ciphertextHex, targetMacHex) {
|
|
488
|
+
if (!this.device ||
|
|
489
|
+
!this.pipeline ||
|
|
490
|
+
!this.bindGroupLayout ||
|
|
491
|
+
!this.paramsBuffer ||
|
|
492
|
+
!this.matchCountBuffer ||
|
|
493
|
+
!this.matchIndicesBuffer ||
|
|
494
|
+
!this.matchCountReadBuffers[0] ||
|
|
495
|
+
!this.matchCountReadBuffers[1] ||
|
|
496
|
+
!this.matchIndicesReadBuffers[0] ||
|
|
497
|
+
!this.matchIndicesReadBuffers[1]) {
|
|
498
|
+
throw new Error('GPU not initialized');
|
|
499
|
+
}
|
|
500
|
+
// Swap to alternate staging buffer set (double-buffering)
|
|
501
|
+
const readBufferIdx = this.currentReadBufferIndex;
|
|
502
|
+
this.currentReadBufferIndex = 1 - this.currentReadBufferIndex;
|
|
503
|
+
const matchCountReadBuffer = this.matchCountReadBuffers[readBufferIdx];
|
|
504
|
+
const matchIndicesReadBuffer = this.matchIndicesReadBuffers[readBufferIdx];
|
|
505
|
+
// Parse ciphertext if provided
|
|
506
|
+
const verifyMac = ciphertextHex && targetMacHex ? 1 : 0;
|
|
507
|
+
let ciphertextWords;
|
|
508
|
+
let ciphertextLenBits = 0;
|
|
509
|
+
let targetMac = 0;
|
|
510
|
+
if (verifyMac) {
|
|
511
|
+
// Convert hex to bytes then to big-endian u32 words
|
|
512
|
+
const ciphertextBytes = new Uint8Array(ciphertextHex.length / 2);
|
|
513
|
+
for (let i = 0; i < ciphertextBytes.length; i++) {
|
|
514
|
+
ciphertextBytes[i] = parseInt(ciphertextHex.substr(i * 2, 2), 16);
|
|
515
|
+
}
|
|
516
|
+
ciphertextLenBits = ciphertextBytes.length * 8;
|
|
517
|
+
// Pad to 4-byte boundary and convert to big-endian u32
|
|
518
|
+
const paddedLen = Math.ceil(ciphertextBytes.length / 4) * 4;
|
|
519
|
+
const padded = new Uint8Array(paddedLen);
|
|
520
|
+
padded.set(ciphertextBytes);
|
|
521
|
+
ciphertextWords = new Uint32Array(paddedLen / 4);
|
|
522
|
+
for (let i = 0; i < ciphertextWords.length; i++) {
|
|
523
|
+
ciphertextWords[i] =
|
|
524
|
+
(padded[i * 4] << 24) |
|
|
525
|
+
(padded[i * 4 + 1] << 16) |
|
|
526
|
+
(padded[i * 4 + 2] << 8) |
|
|
527
|
+
padded[i * 4 + 3];
|
|
528
|
+
}
|
|
529
|
+
// Parse target MAC (2 bytes in high 16 bits)
|
|
530
|
+
const macByte0 = parseInt(targetMacHex.substr(0, 2), 16);
|
|
531
|
+
const macByte1 = parseInt(targetMacHex.substr(2, 2), 16);
|
|
532
|
+
targetMac = (macByte0 << 24) | (macByte1 << 16);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
ciphertextWords = new Uint32Array([0]); // Dummy
|
|
536
|
+
}
|
|
537
|
+
// Resize ciphertext buffer if needed (marks bind group as dirty)
|
|
538
|
+
const requiredCiphertextSize = Math.max(ciphertextWords.length * 4, 4);
|
|
539
|
+
if (!this.ciphertextBuffer || this.ciphertextBufferSize < requiredCiphertextSize) {
|
|
540
|
+
if (this.ciphertextBuffer) {
|
|
541
|
+
this.ciphertextBuffer.destroy();
|
|
542
|
+
}
|
|
543
|
+
this.ciphertextBuffer = this.device.createBuffer({
|
|
544
|
+
size: requiredCiphertextSize,
|
|
545
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
546
|
+
});
|
|
547
|
+
this.ciphertextBufferSize = requiredCiphertextSize;
|
|
548
|
+
this.bindGroupDirty = true;
|
|
549
|
+
}
|
|
550
|
+
// Write params
|
|
551
|
+
const paramsData = new Uint32Array([
|
|
552
|
+
targetChannelHash,
|
|
553
|
+
batchOffset,
|
|
554
|
+
nameLength,
|
|
555
|
+
batchSize,
|
|
556
|
+
targetMac,
|
|
557
|
+
ciphertextWords.length,
|
|
558
|
+
ciphertextLenBits,
|
|
559
|
+
verifyMac,
|
|
560
|
+
]);
|
|
561
|
+
this.device.queue.writeBuffer(this.paramsBuffer, 0, paramsData);
|
|
562
|
+
// Write ciphertext
|
|
563
|
+
this.device.queue.writeBuffer(this.ciphertextBuffer, 0, ciphertextWords);
|
|
564
|
+
// Reset match count (reuse static zero buffer)
|
|
565
|
+
this.device.queue.writeBuffer(this.matchCountBuffer, 0, GpuBruteForce.ZERO_DATA);
|
|
566
|
+
// Recreate bind group only if needed
|
|
567
|
+
if (this.bindGroupDirty || !this.bindGroup) {
|
|
568
|
+
this.bindGroup = this.device.createBindGroup({
|
|
569
|
+
layout: this.bindGroupLayout,
|
|
570
|
+
entries: [
|
|
571
|
+
{ binding: 0, resource: { buffer: this.paramsBuffer } },
|
|
572
|
+
{ binding: 1, resource: { buffer: this.matchCountBuffer } },
|
|
573
|
+
{ binding: 2, resource: { buffer: this.matchIndicesBuffer } },
|
|
574
|
+
{ binding: 3, resource: { buffer: this.ciphertextBuffer } },
|
|
575
|
+
],
|
|
576
|
+
});
|
|
577
|
+
this.bindGroupDirty = false;
|
|
578
|
+
}
|
|
579
|
+
// Create command encoder
|
|
580
|
+
const commandEncoder = this.device.createCommandEncoder();
|
|
581
|
+
const passEncoder = commandEncoder.beginComputePass();
|
|
582
|
+
passEncoder.setPipeline(this.pipeline);
|
|
583
|
+
passEncoder.setBindGroup(0, this.bindGroup);
|
|
584
|
+
// Each workgroup has 256 threads, each processing 16 candidates
|
|
585
|
+
const CANDIDATES_PER_THREAD = 16;
|
|
586
|
+
passEncoder.dispatchWorkgroups(Math.ceil(batchSize / (256 * CANDIDATES_PER_THREAD)));
|
|
587
|
+
passEncoder.end();
|
|
588
|
+
// Copy results to current staging buffers
|
|
589
|
+
commandEncoder.copyBufferToBuffer(this.matchCountBuffer, 0, matchCountReadBuffer, 0, 4);
|
|
590
|
+
commandEncoder.copyBufferToBuffer(this.matchIndicesBuffer, 0, matchIndicesReadBuffer, 0, 1024 * 4);
|
|
591
|
+
// Submit
|
|
592
|
+
this.device.queue.submit([commandEncoder.finish()]);
|
|
593
|
+
// Read results from current staging buffers
|
|
594
|
+
await matchCountReadBuffer.mapAsync(GPUMapMode.READ);
|
|
595
|
+
const matchCount = new Uint32Array(matchCountReadBuffer.getMappedRange())[0];
|
|
596
|
+
matchCountReadBuffer.unmap();
|
|
597
|
+
const matches = [];
|
|
598
|
+
if (matchCount > 0) {
|
|
599
|
+
await matchIndicesReadBuffer.mapAsync(GPUMapMode.READ);
|
|
600
|
+
const indices = new Uint32Array(matchIndicesReadBuffer.getMappedRange());
|
|
601
|
+
for (let i = 0; i < Math.min(matchCount, 1024); i++) {
|
|
602
|
+
matches.push(indices[i]);
|
|
603
|
+
}
|
|
604
|
+
matchIndicesReadBuffer.unmap();
|
|
605
|
+
}
|
|
606
|
+
return matches;
|
|
607
|
+
}
|
|
608
|
+
destroy() {
|
|
609
|
+
// Clean up persistent buffers
|
|
610
|
+
this.paramsBuffer?.destroy();
|
|
611
|
+
this.matchCountBuffer?.destroy();
|
|
612
|
+
this.matchIndicesBuffer?.destroy();
|
|
613
|
+
this.ciphertextBuffer?.destroy();
|
|
614
|
+
// Clean up double-buffered staging buffers
|
|
615
|
+
this.matchCountReadBuffers[0]?.destroy();
|
|
616
|
+
this.matchCountReadBuffers[1]?.destroy();
|
|
617
|
+
this.matchIndicesReadBuffers[0]?.destroy();
|
|
618
|
+
this.matchIndicesReadBuffers[1]?.destroy();
|
|
619
|
+
this.paramsBuffer = null;
|
|
620
|
+
this.matchCountBuffer = null;
|
|
621
|
+
this.matchIndicesBuffer = null;
|
|
622
|
+
this.ciphertextBuffer = null;
|
|
623
|
+
this.ciphertextBufferSize = 0;
|
|
624
|
+
this.matchCountReadBuffers = [null, null];
|
|
625
|
+
this.matchIndicesReadBuffers = [null, null];
|
|
626
|
+
this.currentReadBufferIndex = 0;
|
|
627
|
+
this.bindGroup = null;
|
|
628
|
+
this.bindGroupDirty = true;
|
|
629
|
+
if (this.device) {
|
|
630
|
+
this.device.destroy();
|
|
631
|
+
this.device = null;
|
|
632
|
+
}
|
|
633
|
+
this.pipeline = null;
|
|
634
|
+
this.bindGroupLayout = null;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
// Reusable zero buffer for resetting match count
|
|
638
|
+
GpuBruteForce.ZERO_DATA = new Uint32Array([0]);
|
|
639
|
+
/**
|
|
640
|
+
* Check if WebGPU is supported in the current browser.
|
|
641
|
+
*/
|
|
642
|
+
export function isWebGpuSupported() {
|
|
643
|
+
return typeof navigator !== 'undefined' && 'gpu' in navigator;
|
|
644
|
+
}
|
|
645
|
+
//# sourceMappingURL=gpu-bruteforce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gpu-bruteforce.js","sourceRoot":"","sources":["../src/gpu-bruteforce.ts"],"names":[],"mappings":"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"}
|