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.
@@ -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":"AAAA,mEAAmE;AAEnE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAS9D,MAAM,OAAO,aAAa;IAA1B;QACU,WAAM,GAAqB,IAAI,CAAC;QAChC,aAAQ,GAA8B,IAAI,CAAC;QAC3C,oBAAe,GAA8B,IAAI,CAAC;QAE1D,+CAA+C;QACvC,iBAAY,GAAqB,IAAI,CAAC;QACtC,qBAAgB,GAAqB,IAAI,CAAC;QAC1C,uBAAkB,GAAqB,IAAI,CAAC;QAC5C,qBAAgB,GAAqB,IAAI,CAAC;QAC1C,yBAAoB,GAAW,CAAC,CAAC;QAEzC,+DAA+D;QACvD,0BAAqB,GAAyC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3E,4BAAuB,GAAyC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC7E,2BAAsB,GAAW,CAAC,CAAC;QAE3C,oEAAoE;QAC5D,cAAS,GAAwB,IAAI,CAAC;QACtC,mBAAc,GAAY,IAAI,CAAC;QAKvC,gCAAgC;QACxB,eAAU,GAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkYjC,CAAC;IAsRF,CAAC;IApRC,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;YACnB,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YACrC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;YACrD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;gBACrC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,IAAI,CAAC,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;YAE5C,2BAA2B;YAC3B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC;gBACvD,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;oBAC/E,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;oBAC/E,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;oBAC/E,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE;iBAC1F;aACF,CAAC,CAAC;YAEH,4BAA4B;YAC5B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;gBAC3C,IAAI,EAAE,EAAE,EAAE,SAAS;gBACnB,KAAK,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ;aACxD,CAAC,CAAC;YAEH,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;gBAC/C,IAAI,EAAE,CAAC;gBACP,KAAK,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ;aAClF,CAAC,CAAC;YAEH,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;gBACjD,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE,6BAA6B;gBAC7C,KAAK,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ;aACxD,CAAC,CAAC;YAEH,kCAAkC;YAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC3B,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;oBACvD,IAAI,EAAE,CAAC;oBACP,KAAK,EAAE,cAAc,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ;iBACzD,CAAC,CAAC;gBAEH,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;oBACzD,IAAI,EAAE,IAAI,GAAG,CAAC;oBACd,KAAK,EAAE,cAAc,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ;iBACzD,CAAC,CAAC;YACL,CAAC;YAED,kBAAkB;YAClB,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;gBAClD,IAAI,EAAE,IAAI,CAAC,UAAU;aACtB,CAAC,CAAC;YAEH,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;gBACtD,gBAAgB,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC;aACzC,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC;gBAChD,MAAM,EAAE,cAAc;gBACtB,OAAO,EAAE;oBACP,MAAM,EAAE,YAAY;oBACpB,UAAU,EAAE,MAAM;iBACnB;aACF,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,CAAC,CAAC,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;IACxD,CAAC;IAED,yEAAyE;IACzE,eAAe,CAAC,GAAW,EAAE,MAAc;QACzC,OAAO,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,CAAC;IAED,2DAA2D;IAC3D,mBAAmB,CAAC,GAAW;QAC7B,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,iBAAyB,EACzB,UAAkB,EAClB,WAAmB,EACnB,SAAiB,EACjB,aAAsB,EACtB,YAAqB;QAErB,IACE,CAAC,IAAI,CAAC,MAAM;YACZ,CAAC,IAAI,CAAC,QAAQ;YACd,CAAC,IAAI,CAAC,eAAe;YACrB,CAAC,IAAI,CAAC,YAAY;YAClB,CAAC,IAAI,CAAC,gBAAgB;YACtB,CAAC,IAAI,CAAC,kBAAkB;YACxB,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAC9B,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAC9B,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC;YAChC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAChC,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACzC,CAAC;QAED,0DAA0D;QAC1D,MAAM,aAAa,GAAG,IAAI,CAAC,sBAAsB,CAAC;QAClD,IAAI,CAAC,sBAAsB,GAAG,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC;QAE9D,MAAM,oBAAoB,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAE,CAAC;QACxE,MAAM,sBAAsB,GAAG,IAAI,CAAC,uBAAuB,CAAC,aAAa,CAAE,CAAC;QAE5E,+BAA+B;QAC/B,MAAM,SAAS,GAAG,aAAa,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,IAAI,eAA4B,CAAC;QACjC,IAAI,iBAAiB,GAAG,CAAC,CAAC;QAC1B,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,IAAI,SAAS,EAAE,CAAC;YACd,oDAAoD;YACpD,MAAM,eAAe,GAAG,IAAI,UAAU,CAAC,aAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAClE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChD,eAAe,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,aAAc,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrE,CAAC;YACD,iBAAiB,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;YAE/C,uDAAuD;YACvD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAC5D,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;YACzC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAC5B,eAAe,GAAG,IAAI,WAAW,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;YACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChD,eAAe,CAAC,CAAC,CAAC;oBAChB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;wBACrB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;wBACzB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;wBACxB,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YACtB,CAAC;YAED,6CAA6C;YAC7C,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAa,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAa,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1D,SAAS,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,eAAe,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ;QAClD,CAAC;QAED,iEAAiE;QACjE,MAAM,sBAAsB,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,oBAAoB,GAAG,sBAAsB,EAAE,CAAC;YACjF,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;YAClC,CAAC;YACD,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;gBAC/C,IAAI,EAAE,sBAAsB;gBAC5B,KAAK,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ;aACxD,CAAC,CAAC;YACH,IAAI,CAAC,oBAAoB,GAAG,sBAAsB,CAAC;YACnD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;QAED,eAAe;QACf,MAAM,UAAU,GAAG,IAAI,WAAW,CAAC;YACjC,iBAAiB;YACjB,WAAW;YACX,UAAU;YACV,SAAS;YACT,SAAS;YACT,eAAe,CAAC,MAAM;YACtB,iBAAiB;YACjB,SAAS;SACV,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;QAEhE,mBAAmB;QACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,EAAE,eAA2C,CAAC,CAAC;QAErG,+CAA+C;QAC/C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;QAEjF,qCAAqC;QACrC,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC3C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;gBAC3C,MAAM,EAAE,IAAI,CAAC,eAAe;gBAC5B,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE;oBACvD,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAAE;oBAC3D,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,EAAE;oBAC7D,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAAE;iBAC5D;aACF,CAAC,CAAC;YACH,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC9B,CAAC;QAED,yBAAyB;QACzB,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC;QAC1D,MAAM,WAAW,GAAG,cAAc,CAAC,gBAAgB,EAAE,CAAC;QACtD,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,WAAW,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,gEAAgE;QAChE,MAAM,qBAAqB,GAAG,EAAE,CAAC;QACjC,WAAW,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,GAAG,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACrF,WAAW,CAAC,GAAG,EAAE,CAAC;QAElB,0CAA0C;QAC1C,cAAc,CAAC,kBAAkB,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACxF,cAAc,CAAC,kBAAkB,CAC/B,IAAI,CAAC,kBAAkB,EACvB,CAAC,EACD,sBAAsB,EACtB,CAAC,EACD,IAAI,GAAG,CAAC,CACT,CAAC;QAEF,SAAS;QACT,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEpD,4CAA4C;QAC5C,MAAM,oBAAoB,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG,IAAI,WAAW,CAAC,oBAAoB,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7E,oBAAoB,CAAC,KAAK,EAAE,CAAC;QAE7B,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,sBAAsB,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACvD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,sBAAsB,CAAC,cAAc,EAAE,CAAC,CAAC;YACzE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBACpD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,sBAAsB,CAAC,KAAK,EAAE,CAAC;QACjC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO;QACL,8BAA8B;QAC9B,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,gBAAgB,EAAE,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC,kBAAkB,EAAE,OAAO,EAAE,CAAC;QACnC,IAAI,CAAC,gBAAgB,EAAE,OAAO,EAAE,CAAC;QAEjC,2CAA2C;QAC3C,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QAE3C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,qBAAqB,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,uBAAuB,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,sBAAsB,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAE3B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;IAC9B,CAAC;;AA3pBD,iDAAiD;AACzB,uBAAS,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,AAAvB,CAAwB;AA6pB3D;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,OAAO,SAAS,KAAK,WAAW,IAAI,KAAK,IAAI,SAAS,CAAC;AAChE,CAAC"}