@hamradio/meshcore 1.1.2 → 1.2.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/dist/index.mjs CHANGED
@@ -1,8 +1,51 @@
1
- // src/crypto.ts
2
- import { ed25519, x25519 } from "@noble/curves/ed25519.js";
1
+ // src/packet.types.ts
2
+ var RouteType = /* @__PURE__ */ ((RouteType2) => {
3
+ RouteType2[RouteType2["TRANSPORT_FLOOD"] = 0] = "TRANSPORT_FLOOD";
4
+ RouteType2[RouteType2["FLOOD"] = 1] = "FLOOD";
5
+ RouteType2[RouteType2["DIRECT"] = 2] = "DIRECT";
6
+ RouteType2[RouteType2["TRANSPORT_DIRECT"] = 3] = "TRANSPORT_DIRECT";
7
+ return RouteType2;
8
+ })(RouteType || {});
9
+ var PayloadType = /* @__PURE__ */ ((PayloadType2) => {
10
+ PayloadType2[PayloadType2["REQUEST"] = 0] = "REQUEST";
11
+ PayloadType2[PayloadType2["RESPONSE"] = 1] = "RESPONSE";
12
+ PayloadType2[PayloadType2["TEXT"] = 2] = "TEXT";
13
+ PayloadType2[PayloadType2["ACK"] = 3] = "ACK";
14
+ PayloadType2[PayloadType2["ADVERT"] = 4] = "ADVERT";
15
+ PayloadType2[PayloadType2["GROUP_TEXT"] = 5] = "GROUP_TEXT";
16
+ PayloadType2[PayloadType2["GROUP_DATA"] = 6] = "GROUP_DATA";
17
+ PayloadType2[PayloadType2["ANON_REQ"] = 7] = "ANON_REQ";
18
+ PayloadType2[PayloadType2["PATH"] = 8] = "PATH";
19
+ PayloadType2[PayloadType2["TRACE"] = 9] = "TRACE";
20
+ PayloadType2[PayloadType2["RAW_CUSTOM"] = 15] = "RAW_CUSTOM";
21
+ return PayloadType2;
22
+ })(PayloadType || {});
23
+ var RequestType = /* @__PURE__ */ ((RequestType2) => {
24
+ RequestType2[RequestType2["GET_STATS"] = 1] = "GET_STATS";
25
+ RequestType2[RequestType2["KEEP_ALIVE"] = 2] = "KEEP_ALIVE";
26
+ RequestType2[RequestType2["GET_TELEMETRY"] = 3] = "GET_TELEMETRY";
27
+ RequestType2[RequestType2["GET_MIN_MAX_AVG"] = 4] = "GET_MIN_MAX_AVG";
28
+ RequestType2[RequestType2["GET_ACL"] = 5] = "GET_ACL";
29
+ RequestType2[RequestType2["GET_NEIGHBORS"] = 6] = "GET_NEIGHBORS";
30
+ RequestType2[RequestType2["GET_OWNER_INFO"] = 7] = "GET_OWNER_INFO";
31
+ return RequestType2;
32
+ })(RequestType || {});
33
+ var TextType = /* @__PURE__ */ ((TextType2) => {
34
+ TextType2[TextType2["PLAIN_TEXT"] = 0] = "PLAIN_TEXT";
35
+ TextType2[TextType2["CLI_COMMAND"] = 1] = "CLI_COMMAND";
36
+ TextType2[TextType2["SIGNED_PLAIN_TEXT"] = 2] = "SIGNED_PLAIN_TEXT";
37
+ return TextType2;
38
+ })(TextType || {});
39
+ var NodeType = /* @__PURE__ */ ((NodeType2) => {
40
+ NodeType2[NodeType2["CHAT_NODE"] = 1] = "CHAT_NODE";
41
+ NodeType2[NodeType2["REPEATER"] = 2] = "REPEATER";
42
+ NodeType2[NodeType2["ROOM_SERVER"] = 3] = "ROOM_SERVER";
43
+ NodeType2[NodeType2["SENSOR_NODE"] = 4] = "SENSOR_NODE";
44
+ return NodeType2;
45
+ })(NodeType || {});
46
+
47
+ // src/packet.ts
3
48
  import { sha256 } from "@noble/hashes/sha2.js";
4
- import { hmac } from "@noble/hashes/hmac.js";
5
- import { ecb } from "@noble/ciphers/aes.js";
6
49
 
7
50
  // src/parser.ts
8
51
  import { equalBytes } from "@noble/ciphers/utils.js";
@@ -122,7 +165,510 @@ var BufferWriter = class {
122
165
  }
123
166
  };
124
167
 
168
+ // src/packet.ts
169
+ var Packet = class _Packet {
170
+ constructor(header, transport, pathLength, path, payload) {
171
+ this.header = header;
172
+ this.transport = transport;
173
+ this.pathLength = pathLength;
174
+ this.path = path;
175
+ this.payload = payload;
176
+ this.routeType = header & 3;
177
+ this.payloadVersion = header >> 6 & 3;
178
+ this.payloadType = header >> 2 & 15;
179
+ this.pathHashSize = (pathLength >> 6) + 1;
180
+ this.pathHashCount = pathLength & 63;
181
+ this.pathHashBytes = this.pathHashCount * this.pathHashSize;
182
+ this.pathHashes = [];
183
+ for (let i = 0; i < this.pathHashBytes; i += this.pathHashSize) {
184
+ const hashBytes = this.path.slice(i, i + this.pathHashSize);
185
+ const hashHex = bytesToHex(hashBytes);
186
+ this.pathHashes.push(hashHex);
187
+ }
188
+ }
189
+ static fromBytes(bytes) {
190
+ if (typeof bytes === "string") {
191
+ bytes = base64ToBytes(bytes);
192
+ }
193
+ let offset = 0;
194
+ const header = bytes[offset++];
195
+ const routeType = header & 3;
196
+ let transport;
197
+ if (_Packet.hasTransportCodes(routeType)) {
198
+ const uitn16View = new DataView(bytes.buffer, bytes.byteOffset + offset, 4);
199
+ transport = [uitn16View.getUint16(0, false), uitn16View.getUint16(2, false)];
200
+ offset += 4;
201
+ }
202
+ const pathLength = bytes[offset++];
203
+ const path = bytes.slice(offset, offset + pathLength);
204
+ offset += pathLength;
205
+ const payload = bytes.slice(offset);
206
+ return new _Packet(header, transport, pathLength, path, payload);
207
+ }
208
+ static hasTransportCodes(routeType) {
209
+ return routeType === 0 /* TRANSPORT_FLOOD */ || routeType === 3 /* TRANSPORT_DIRECT */;
210
+ }
211
+ hash() {
212
+ const hash = sha256.create();
213
+ hash.update(new Uint8Array([this.payloadType]));
214
+ if (this.payloadType === 9 /* TRACE */) {
215
+ hash.update(new Uint8Array([this.pathLength]));
216
+ }
217
+ hash.update(this.payload);
218
+ const digest = hash.digest();
219
+ return bytesToHex(digest.slice(0, 8));
220
+ }
221
+ ensureStructure() {
222
+ if (typeof this.structure !== "undefined") {
223
+ return;
224
+ }
225
+ let pathHashType;
226
+ switch (this.pathHashSize) {
227
+ case 1:
228
+ pathHashType = 6 /* BYTES */;
229
+ break;
230
+ case 2:
231
+ pathHashType = 7 /* WORDS */;
232
+ break;
233
+ case 4:
234
+ pathHashType = 8 /* DWORDS */;
235
+ break;
236
+ default:
237
+ throw new Error(`Unsupported path hash size: ${this.pathHashSize}`);
238
+ }
239
+ this.structure = [
240
+ /* Header segment */
241
+ { name: "header", data: new Uint8Array([this.header, this.pathLength, ...this.path]), fields: [
242
+ /* Header flags */
243
+ {
244
+ name: "flags",
245
+ type: 0 /* BITS */,
246
+ size: 1,
247
+ bits: [
248
+ { name: "route type", size: 2 },
249
+ { name: "payload version", size: 2 },
250
+ { name: "payload type", size: 4 }
251
+ ]
252
+ },
253
+ /* Transport codes */
254
+ ..._Packet.hasTransportCodes(this.routeType) ? [
255
+ {
256
+ name: "transport code 1",
257
+ type: 3 /* UINT16_BE */,
258
+ size: 2
259
+ },
260
+ {
261
+ name: "transport code 2",
262
+ type: 3 /* UINT16_BE */,
263
+ size: 2
264
+ }
265
+ ] : [],
266
+ /* Path length and hashes */
267
+ {
268
+ name: "path length",
269
+ type: 1 /* UINT8 */,
270
+ size: 1,
271
+ bits: [
272
+ { name: "path hash size", size: 2 },
273
+ { name: "path hash count", size: 6 }
274
+ ]
275
+ },
276
+ {
277
+ name: "path hashes",
278
+ type: pathHashType,
279
+ size: this.path.length
280
+ }
281
+ ] }
282
+ ];
283
+ }
284
+ decode(withStructure) {
285
+ let result;
286
+ switch (this.payloadType) {
287
+ case 0 /* REQUEST */:
288
+ result = this.decodeRequest(withStructure);
289
+ break;
290
+ case 1 /* RESPONSE */:
291
+ result = this.decodeResponse(withStructure);
292
+ break;
293
+ case 2 /* TEXT */:
294
+ result = this.decodeText(withStructure);
295
+ break;
296
+ case 3 /* ACK */:
297
+ result = this.decodeAck(withStructure);
298
+ break;
299
+ case 4 /* ADVERT */:
300
+ result = this.decodeAdvert(withStructure);
301
+ break;
302
+ case 5 /* GROUP_TEXT */:
303
+ result = this.decodeGroupText(withStructure);
304
+ break;
305
+ case 6 /* GROUP_DATA */:
306
+ result = this.decodeGroupData(withStructure);
307
+ break;
308
+ case 7 /* ANON_REQ */:
309
+ result = this.decodeAnonReq(withStructure);
310
+ break;
311
+ case 8 /* PATH */:
312
+ result = this.decodePath(withStructure);
313
+ break;
314
+ case 9 /* TRACE */:
315
+ result = this.decodeTrace(withStructure);
316
+ break;
317
+ case 15 /* RAW_CUSTOM */:
318
+ result = this.decodeRawCustom(withStructure);
319
+ break;
320
+ default:
321
+ throw new Error(`Unsupported payload type: ${this.payloadType}`);
322
+ }
323
+ console.log("packet decode with structure:", typeof withStructure, withStructure, { result });
324
+ if (typeof withStructure === "boolean" && withStructure && "segment" in result && "payload" in result) {
325
+ this.ensureStructure();
326
+ const structure = [...this.structure, result.segment];
327
+ return { payload: result.payload, structure };
328
+ }
329
+ return result;
330
+ }
331
+ decodeEncryptedPayload(reader) {
332
+ const cipherMAC = reader.readBytes(2);
333
+ const cipherText = reader.readBytes(reader.remainingBytes());
334
+ return { cipherMAC, cipherText };
335
+ }
336
+ decodeRequest(withSegment) {
337
+ if (this.payload.length < 4) {
338
+ throw new Error("Invalid request payload: too short");
339
+ }
340
+ const reader = new BufferReader(this.payload);
341
+ const dst = reader.readByte();
342
+ const src = reader.readByte();
343
+ const encrypted = this.decodeEncryptedPayload(reader);
344
+ const payload = {
345
+ type: 0 /* REQUEST */,
346
+ dst,
347
+ src,
348
+ encrypted
349
+ };
350
+ if (typeof withSegment === "boolean" && withSegment) {
351
+ const segment = {
352
+ name: "request payload",
353
+ data: this.payload,
354
+ fields: [
355
+ { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: dst },
356
+ { name: "source hash", type: 1 /* UINT8 */, size: 1, value: src },
357
+ { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: encrypted.cipherMAC },
358
+ { name: "cipher text", type: 6 /* BYTES */, size: encrypted.cipherText.length, value: encrypted.cipherText }
359
+ ]
360
+ };
361
+ return { payload, segment };
362
+ }
363
+ return payload;
364
+ }
365
+ decodeResponse(withSegment) {
366
+ if (this.payload.length < 4) {
367
+ throw new Error("Invalid response payload: too short");
368
+ }
369
+ const reader = new BufferReader(this.payload);
370
+ const dst = reader.readByte();
371
+ const src = reader.readByte();
372
+ const encrypted = this.decodeEncryptedPayload(reader);
373
+ const payload = {
374
+ type: 1 /* RESPONSE */,
375
+ dst,
376
+ src,
377
+ encrypted
378
+ };
379
+ if (typeof withSegment === "boolean" && withSegment) {
380
+ const segment = {
381
+ name: "response payload",
382
+ data: this.payload,
383
+ fields: [
384
+ { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: dst },
385
+ { name: "source hash", type: 1 /* UINT8 */, size: 1, value: src },
386
+ { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: encrypted.cipherMAC },
387
+ { name: "cipher text", type: 6 /* BYTES */, size: encrypted.cipherText.length, value: encrypted.cipherText }
388
+ ]
389
+ };
390
+ return { payload, segment };
391
+ }
392
+ return payload;
393
+ }
394
+ decodeText(withSegment) {
395
+ if (this.payload.length < 4) {
396
+ throw new Error("Invalid text payload: too short");
397
+ }
398
+ const reader = new BufferReader(this.payload);
399
+ const dst = reader.readByte();
400
+ const src = reader.readByte();
401
+ const encrypted = this.decodeEncryptedPayload(reader);
402
+ const payload = {
403
+ type: 2 /* TEXT */,
404
+ dst,
405
+ src,
406
+ encrypted
407
+ };
408
+ if (typeof withSegment === "boolean" && withSegment) {
409
+ const segment = {
410
+ name: "text payload",
411
+ data: this.payload,
412
+ fields: [
413
+ { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: dst },
414
+ { name: "source hash", type: 1 /* UINT8 */, size: 1, value: src },
415
+ { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: encrypted.cipherMAC },
416
+ { name: "cipher text", type: 6 /* BYTES */, size: encrypted.cipherText.length, value: encrypted.cipherText }
417
+ ]
418
+ };
419
+ return { payload, segment };
420
+ }
421
+ return payload;
422
+ }
423
+ decodeAck(withSegment) {
424
+ if (this.payload.length < 4) {
425
+ throw new Error("Invalid ack payload: too short");
426
+ }
427
+ const reader = new BufferReader(this.payload);
428
+ const checksum = reader.readBytes(4);
429
+ const payload = {
430
+ type: 3 /* ACK */,
431
+ checksum
432
+ };
433
+ if (typeof withSegment === "boolean" && withSegment) {
434
+ const segment = {
435
+ name: "ack payload",
436
+ data: this.payload,
437
+ fields: [
438
+ { name: "checksum", type: 6 /* BYTES */, size: 4, value: checksum }
439
+ ]
440
+ };
441
+ return { payload, segment };
442
+ }
443
+ return payload;
444
+ }
445
+ decodeAdvert(withSegment) {
446
+ if (this.payload.length < 4) {
447
+ throw new Error("Invalid advert payload: too short");
448
+ }
449
+ const reader = new BufferReader(this.payload);
450
+ const payload = {
451
+ type: 4 /* ADVERT */,
452
+ publicKey: reader.readBytes(32),
453
+ timestamp: reader.readTimestamp(),
454
+ signature: reader.readBytes(64)
455
+ };
456
+ let segment;
457
+ if (typeof withSegment === "boolean" && withSegment) {
458
+ segment = {
459
+ name: "advert payload",
460
+ data: this.payload,
461
+ fields: [
462
+ { type: 6 /* BYTES */, name: "public key", size: 32 },
463
+ { type: 4 /* UINT32_LE */, name: "timestamp", size: 4, value: payload.timestamp },
464
+ { type: 6 /* BYTES */, name: "signature", size: 64 }
465
+ ]
466
+ };
467
+ }
468
+ const flags = reader.readByte();
469
+ const appdata = {
470
+ nodeType: flags & 15,
471
+ hasLocation: (flags & 16 /* HAS_LOCATION */) !== 0,
472
+ hasFeature1: (flags & 32 /* HAS_FEATURE1 */) !== 0,
473
+ hasFeature2: (flags & 64 /* HAS_FEATURE2 */) !== 0,
474
+ hasName: (flags & 128 /* HAS_NAME */) !== 0
475
+ };
476
+ if (typeof withSegment === "boolean" && withSegment) {
477
+ segment.fields.push({ type: 0 /* BITS */, name: "flags", size: 1, value: flags, bits: [
478
+ { size: 4, name: "node type" },
479
+ { size: 1, name: "location flag" },
480
+ { size: 1, name: "feature1 flag" },
481
+ { size: 1, name: "feature2 flag" },
482
+ { size: 1, name: "name flag" }
483
+ ] });
484
+ }
485
+ if (appdata.hasLocation) {
486
+ const lat = reader.readInt32LE() / 1e5;
487
+ const lon = reader.readInt32LE() / 1e5;
488
+ appdata.location = [lat, lon];
489
+ if (typeof withSegment === "boolean" && withSegment) {
490
+ segment.fields.push({ type: 4 /* UINT32_LE */, name: "latitude", size: 4, value: lat });
491
+ segment.fields.push({ type: 4 /* UINT32_LE */, name: "longitude", size: 4, value: lon });
492
+ }
493
+ }
494
+ if (appdata.hasFeature1) {
495
+ appdata.feature1 = reader.readUint16LE();
496
+ if (typeof withSegment === "boolean" && withSegment) {
497
+ segment.fields.push({ type: 2 /* UINT16_LE */, name: "feature1", size: 2, value: appdata.feature1 });
498
+ }
499
+ }
500
+ if (appdata.hasFeature2) {
501
+ appdata.feature2 = reader.readUint16LE();
502
+ if (typeof withSegment === "boolean" && withSegment) {
503
+ segment.fields.push({ type: 2 /* UINT16_LE */, name: "feature2", size: 2, value: appdata.feature2 });
504
+ }
505
+ }
506
+ if (appdata.hasName) {
507
+ const nameBytes = reader.readBytes();
508
+ let nullPos = nameBytes.indexOf(0);
509
+ if (nullPos === -1) {
510
+ nullPos = nameBytes.length;
511
+ }
512
+ appdata.name = new TextDecoder("utf-8").decode(nameBytes.subarray(0, nullPos));
513
+ if (typeof withSegment === "boolean" && withSegment) {
514
+ segment.fields.push({ type: 10 /* C_STRING */, name: "name", size: nameBytes.length, value: appdata.name });
515
+ }
516
+ }
517
+ if (typeof withSegment === "boolean" && withSegment && typeof segment !== "undefined") {
518
+ return { payload: { ...payload, appdata }, segment };
519
+ }
520
+ return { ...payload, appdata };
521
+ }
522
+ decodeGroupText(withSegment) {
523
+ if (this.payload.length < 3) {
524
+ throw new Error("Invalid group text payload: too short");
525
+ }
526
+ const reader = new BufferReader(this.payload);
527
+ const channelHash = reader.readByte();
528
+ const encrypted = this.decodeEncryptedPayload(reader);
529
+ const payload = {
530
+ type: 5 /* GROUP_TEXT */,
531
+ channelHash,
532
+ encrypted
533
+ };
534
+ if (typeof withSegment === "boolean" && withSegment) {
535
+ const segment = {
536
+ name: "group text payload",
537
+ data: this.payload,
538
+ fields: [
539
+ { name: "channel hash", type: 1 /* UINT8 */, size: 1, value: channelHash },
540
+ { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: encrypted.cipherMAC },
541
+ { name: "cipher text", type: 6 /* BYTES */, size: encrypted.cipherText.length, value: encrypted.cipherText }
542
+ ]
543
+ };
544
+ return { payload, segment };
545
+ }
546
+ return payload;
547
+ }
548
+ decodeGroupData(withSegment) {
549
+ if (this.payload.length < 3) {
550
+ throw new Error("Invalid group data payload: too short");
551
+ }
552
+ const reader = new BufferReader(this.payload);
553
+ const payload = {
554
+ type: 6 /* GROUP_DATA */,
555
+ channelHash: reader.readByte(),
556
+ encrypted: this.decodeEncryptedPayload(reader)
557
+ };
558
+ if (typeof withSegment === "boolean" && withSegment) {
559
+ const segment = {
560
+ name: "group data payload",
561
+ data: this.payload,
562
+ fields: [
563
+ { name: "channel hash", type: 1 /* UINT8 */, size: 1, value: payload.channelHash },
564
+ { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: payload.encrypted.cipherMAC },
565
+ { name: "cipher text", type: 6 /* BYTES */, size: payload.encrypted.cipherText.length, value: payload.encrypted.cipherText }
566
+ ]
567
+ };
568
+ return { payload, segment };
569
+ }
570
+ return payload;
571
+ }
572
+ decodeAnonReq(withSegment) {
573
+ if (this.payload.length < 1 + 32 + 2) {
574
+ throw new Error("Invalid anon req payload: too short");
575
+ }
576
+ const reader = new BufferReader(this.payload);
577
+ const payload = {
578
+ type: 7 /* ANON_REQ */,
579
+ dst: reader.readByte(),
580
+ publicKey: reader.readBytes(32),
581
+ encrypted: this.decodeEncryptedPayload(reader)
582
+ };
583
+ if (typeof withSegment === "boolean" && withSegment) {
584
+ const segment = {
585
+ name: "anon req payload",
586
+ data: this.payload,
587
+ fields: [
588
+ { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: payload.dst },
589
+ { name: "public key", type: 6 /* BYTES */, size: 32, value: payload.publicKey },
590
+ { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: payload.encrypted.cipherMAC },
591
+ { name: "cipher text", type: 6 /* BYTES */, size: payload.encrypted.cipherText.length, value: payload.encrypted.cipherText }
592
+ ]
593
+ };
594
+ return { payload, segment };
595
+ }
596
+ return payload;
597
+ }
598
+ decodePath(withSegment) {
599
+ if (this.payload.length < 2) {
600
+ throw new Error("Invalid path payload: too short");
601
+ }
602
+ const reader = new BufferReader(this.payload);
603
+ const payload = {
604
+ type: 8 /* PATH */,
605
+ dst: reader.readByte(),
606
+ src: reader.readByte()
607
+ };
608
+ if (typeof withSegment === "boolean" && withSegment) {
609
+ const segment = {
610
+ name: "path payload",
611
+ data: this.payload,
612
+ fields: [
613
+ { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: payload.dst },
614
+ { name: "source hash", type: 1 /* UINT8 */, size: 1, value: payload.src }
615
+ ]
616
+ };
617
+ return { payload, segment };
618
+ }
619
+ return payload;
620
+ }
621
+ decodeTrace(withSegment) {
622
+ if (this.payload.length < 9) {
623
+ throw new Error("Invalid trace payload: too short");
624
+ }
625
+ const reader = new BufferReader(this.payload);
626
+ const payload = {
627
+ type: 9 /* TRACE */,
628
+ tag: reader.readUint32LE() >>> 0,
629
+ authCode: reader.readUint32LE() >>> 0,
630
+ flags: reader.readByte() & 3,
631
+ nodes: reader.readBytes()
632
+ };
633
+ if (typeof withSegment === "boolean" && withSegment) {
634
+ const segment = {
635
+ name: "trace payload",
636
+ data: this.payload,
637
+ fields: [
638
+ { name: "tag", type: 8 /* DWORDS */, size: 4, value: payload.tag },
639
+ { name: "auth code", type: 8 /* DWORDS */, size: 4, value: payload.authCode },
640
+ { name: "flags", type: 1 /* UINT8 */, size: 1, value: payload.flags },
641
+ { name: "nodes", type: 6 /* BYTES */, size: payload.nodes.length, value: payload.nodes }
642
+ ]
643
+ };
644
+ return { payload, segment };
645
+ }
646
+ return payload;
647
+ }
648
+ decodeRawCustom(withSegment) {
649
+ const payload = {
650
+ type: 15 /* RAW_CUSTOM */,
651
+ data: this.payload
652
+ };
653
+ if (typeof withSegment === "boolean" && withSegment) {
654
+ const segment = {
655
+ name: "raw custom payload",
656
+ data: this.payload,
657
+ fields: [
658
+ { name: "data", type: 6 /* BYTES */, size: this.payload.length, value: this.payload }
659
+ ]
660
+ };
661
+ return { payload, segment };
662
+ }
663
+ return payload;
664
+ }
665
+ };
666
+
125
667
  // src/crypto.ts
668
+ import { ed25519, x25519 } from "@noble/curves/ed25519.js";
669
+ import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
670
+ import { hmac } from "@noble/hashes/hmac.js";
671
+ import { ecb } from "@noble/ciphers/aes.js";
126
672
  var PUBLIC_KEY_SIZE = 32;
127
673
  var SEED_SIZE = 32;
128
674
  var HMAC_SIZE = 2;
@@ -141,7 +687,7 @@ var PublicKey = class _PublicKey {
141
687
  }
142
688
  }
143
689
  toHash() {
144
- return sha256.create().update(this.key).digest()[0];
690
+ return sha2562.create().update(this.key).digest()[0];
145
691
  }
146
692
  toBytes() {
147
693
  return this.key;
@@ -274,7 +820,7 @@ var SharedSecret = class _SharedSecret {
274
820
  return { hmac: hmac2, ciphertext };
275
821
  }
276
822
  calculateHmac(data) {
277
- return hmac(sha256, this.secret, data).slice(0, HMAC_SIZE);
823
+ return hmac(sha2562, this.secret, data).slice(0, HMAC_SIZE);
278
824
  }
279
825
  static fromName(name) {
280
826
  if (name === "Public") {
@@ -282,7 +828,7 @@ var SharedSecret = class _SharedSecret {
282
828
  } else if (!/^#/.test(name)) {
283
829
  throw new Error("Only the 'Public' group or groups starting with '#' are supported");
284
830
  }
285
- const hash = sha256.create().update(new TextEncoder().encode(name)).digest();
831
+ const hash = sha2562.create().update(new TextEncoder().encode(name)).digest();
286
832
  return new _SharedSecret(hash.slice(0, SHARED_SECRET_SIZE));
287
833
  }
288
834
  };
@@ -577,258 +1123,21 @@ var Contacts = class {
577
1123
  throw new Error("Decryption failed with all known groups");
578
1124
  }
579
1125
  };
580
-
581
- // src/packet.ts
582
- import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
583
- var Packet = class _Packet {
584
- constructor(header, transport, pathLength, path, payload) {
585
- this.header = header;
586
- this.transport = transport;
587
- this.pathLength = pathLength;
588
- this.path = path;
589
- this.payload = payload;
590
- this.routeType = header & 3;
591
- this.payloadVersion = header >> 6 & 3;
592
- this.payloadType = header >> 2 & 15;
593
- this.pathHashCount = (pathLength >> 6) + 1;
594
- this.pathHashSize = pathLength & 63;
595
- this.pathHashBytes = this.pathHashCount * this.pathHashSize;
596
- this.pathHashes = [];
597
- for (let i = 0; i < this.pathHashCount; i++) {
598
- const hashBytes = this.path.slice(i * this.pathHashSize, (i + 1) * this.pathHashSize);
599
- const hashHex = bytesToHex(hashBytes);
600
- this.pathHashes.push(hashHex);
601
- }
602
- }
603
- static fromBytes(bytes) {
604
- if (typeof bytes === "string") {
605
- bytes = base64ToBytes(bytes);
606
- }
607
- let offset = 0;
608
- const header = bytes[offset++];
609
- const routeType = header & 3;
610
- let transport;
611
- if (_Packet.hasTransportCodes(routeType)) {
612
- const uitn16View = new DataView(bytes.buffer, bytes.byteOffset + offset, 4);
613
- transport = [uitn16View.getUint16(0, false), uitn16View.getUint16(2, false)];
614
- offset += 4;
615
- }
616
- const pathLength = bytes[offset++];
617
- const path = bytes.slice(offset, offset + pathLength);
618
- offset += pathLength;
619
- const payload = bytes.slice(offset);
620
- return new _Packet(header, transport, pathLength, path, payload);
621
- }
622
- static hasTransportCodes(routeType) {
623
- return routeType === 0 /* TRANSPORT_FLOOD */ || routeType === 3 /* TRANSPORT_DIRECT */;
624
- }
625
- hash() {
626
- const hash = sha2562.create();
627
- hash.update(new Uint8Array([this.payloadType]));
628
- if (this.payloadType === 9 /* TRACE */) {
629
- hash.update(new Uint8Array([this.pathLength]));
630
- }
631
- hash.update(this.payload);
632
- const digest = hash.digest();
633
- return bytesToHex(digest.slice(0, 8));
634
- }
635
- decode() {
636
- switch (this.payloadType) {
637
- case 0 /* REQUEST */:
638
- return this.decodeRequest();
639
- case 1 /* RESPONSE */:
640
- return this.decodeResponse();
641
- case 2 /* TEXT */:
642
- return this.decodeText();
643
- case 3 /* ACK */:
644
- return this.decodeAck();
645
- case 4 /* ADVERT */:
646
- return this.decodeAdvert();
647
- case 5 /* GROUP_TEXT */:
648
- return this.decodeGroupText();
649
- case 6 /* GROUP_DATA */:
650
- return this.decodeGroupData();
651
- case 7 /* ANON_REQ */:
652
- return this.decodeAnonReq();
653
- case 8 /* PATH */:
654
- return this.decodePath();
655
- case 9 /* TRACE */:
656
- return this.decodeTrace();
657
- case 15 /* RAW_CUSTOM */:
658
- return this.decodeRawCustom();
659
- default:
660
- throw new Error(`Unsupported payload type: ${this.payloadType}`);
661
- }
662
- }
663
- decodeEncryptedPayload(reader) {
664
- const cipherMAC = reader.readBytes(2);
665
- const cipherText = reader.readBytes(reader.remainingBytes());
666
- return { cipherMAC, cipherText };
667
- }
668
- decodeRequest() {
669
- if (this.payload.length < 4) {
670
- throw new Error("Invalid request payload: too short");
671
- }
672
- const reader = new BufferReader(this.payload);
673
- return {
674
- type: 0 /* REQUEST */,
675
- dst: reader.readByte(),
676
- src: reader.readByte(),
677
- encrypted: this.decodeEncryptedPayload(reader)
678
- };
679
- }
680
- decodeResponse() {
681
- if (this.payload.length < 4) {
682
- throw new Error("Invalid response payload: too short");
683
- }
684
- const reader = new BufferReader(this.payload);
685
- return {
686
- type: 1 /* RESPONSE */,
687
- dst: reader.readByte(),
688
- src: reader.readByte(),
689
- encrypted: this.decodeEncryptedPayload(reader)
690
- };
691
- }
692
- decodeText() {
693
- if (this.payload.length < 4) {
694
- throw new Error("Invalid text payload: too short");
695
- }
696
- const reader = new BufferReader(this.payload);
697
- return {
698
- type: 2 /* TEXT */,
699
- dst: reader.readByte(),
700
- src: reader.readByte(),
701
- encrypted: this.decodeEncryptedPayload(reader)
702
- };
703
- }
704
- decodeAck() {
705
- if (this.payload.length < 4) {
706
- throw new Error("Invalid ack payload: too short");
707
- }
708
- const reader = new BufferReader(this.payload);
709
- return {
710
- type: 3 /* ACK */,
711
- checksum: reader.readBytes(4)
712
- };
713
- }
714
- decodeAdvert() {
715
- if (this.payload.length < 4) {
716
- throw new Error("Invalid advert payload: too short");
717
- }
718
- const reader = new BufferReader(this.payload);
719
- const payload = {
720
- type: 4 /* ADVERT */,
721
- publicKey: reader.readBytes(32),
722
- timestamp: reader.readTimestamp(),
723
- signature: reader.readBytes(64)
724
- };
725
- const flags = reader.readByte();
726
- const appdata = {
727
- nodeType: flags & 15,
728
- hasLocation: (flags & 16) !== 0,
729
- hasFeature1: (flags & 32) !== 0,
730
- hasFeature2: (flags & 64) !== 0,
731
- hasName: (flags & 128) !== 0
732
- };
733
- if (appdata.hasLocation) {
734
- const lat = reader.readInt32LE() / 1e5;
735
- const lon = reader.readInt32LE() / 1e5;
736
- appdata.location = [lat, lon];
737
- }
738
- if (appdata.hasFeature1) {
739
- appdata.feature1 = reader.readUint16LE();
740
- }
741
- if (appdata.hasFeature2) {
742
- appdata.feature2 = reader.readUint16LE();
743
- }
744
- if (appdata.hasName) {
745
- const nameBytes = reader.readBytes();
746
- let nullPos = nameBytes.indexOf(0);
747
- if (nullPos === -1) {
748
- nullPos = nameBytes.length;
749
- }
750
- appdata.name = new TextDecoder("utf-8").decode(nameBytes.subarray(0, nullPos));
751
- }
752
- return {
753
- ...payload,
754
- appdata
755
- };
756
- }
757
- decodeGroupText() {
758
- if (this.payload.length < 3) {
759
- throw new Error("Invalid group text payload: too short");
760
- }
761
- const reader = new BufferReader(this.payload);
762
- return {
763
- type: 5 /* GROUP_TEXT */,
764
- channelHash: reader.readByte(),
765
- encrypted: this.decodeEncryptedPayload(reader)
766
- };
767
- }
768
- decodeGroupData() {
769
- if (this.payload.length < 3) {
770
- throw new Error("Invalid group data payload: too short");
771
- }
772
- const reader = new BufferReader(this.payload);
773
- return {
774
- type: 6 /* GROUP_DATA */,
775
- channelHash: reader.readByte(),
776
- encrypted: this.decodeEncryptedPayload(reader)
777
- };
778
- }
779
- decodeAnonReq() {
780
- if (this.payload.length < 1 + 32 + 2) {
781
- throw new Error("Invalid anon req payload: too short");
782
- }
783
- const reader = new BufferReader(this.payload);
784
- return {
785
- type: 7 /* ANON_REQ */,
786
- dst: reader.readByte(),
787
- publicKey: reader.readBytes(32),
788
- encrypted: this.decodeEncryptedPayload(reader)
789
- };
790
- }
791
- decodePath() {
792
- if (this.payload.length < 2) {
793
- throw new Error("Invalid path payload: too short");
794
- }
795
- const reader = new BufferReader(this.payload);
796
- return {
797
- type: 8 /* PATH */,
798
- dst: reader.readByte(),
799
- src: reader.readByte()
800
- };
801
- }
802
- decodeTrace() {
803
- if (this.payload.length < 9) {
804
- throw new Error("Invalid trace payload: too short");
805
- }
806
- const reader = new BufferReader(this.payload);
807
- return {
808
- type: 9 /* TRACE */,
809
- tag: reader.readUint32LE() >>> 0,
810
- authCode: reader.readUint32LE() >>> 0,
811
- flags: reader.readByte() & 3,
812
- nodes: reader.readBytes()
813
- };
814
- }
815
- decodeRawCustom() {
816
- return {
817
- type: 15 /* RAW_CUSTOM */,
818
- data: this.payload
819
- };
820
- }
821
- };
822
1126
  export {
823
1127
  Contact,
824
1128
  Contacts,
825
1129
  Group,
826
1130
  Identity,
827
1131
  LocalIdentity,
1132
+ NodeType,
828
1133
  Packet,
1134
+ PayloadType,
829
1135
  PrivateKey,
830
1136
  PublicKey,
1137
+ RequestType,
1138
+ RouteType,
831
1139
  SharedSecret,
832
1140
  StaticSecret,
1141
+ TextType,
833
1142
  parseNodeHash
834
1143
  };