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