@hamradio/meshcore 1.2.2 → 1.3.1
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 → index.cjs} +339 -323
- package/dist/{index.d.mts → index.d.cts} +118 -132
- package/dist/index.d.ts +118 -132
- package/dist/index.js +296 -364
- package/package.json +8 -4
- package/dist/crypto.d.ts +0 -42
- package/dist/crypto.js +0 -199
- package/dist/crypto.types.d.ts +0 -26
- package/dist/crypto.types.js +0 -1
- package/dist/identity.d.ts +0 -65
- package/dist/identity.js +0 -302
- package/dist/identity.types.d.ts +0 -17
- package/dist/identity.types.js +0 -1
- package/dist/packet.d.ts +0 -32
- package/dist/packet.js +0 -242
- package/dist/packet.types.d.ts +0 -161
- package/dist/packet.types.js +0 -44
- package/dist/parser.d.ts +0 -31
- package/dist/parser.js +0 -124
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Contact: () => Contact,
|
|
24
|
+
Contacts: () => Contacts,
|
|
25
|
+
Group: () => Group,
|
|
26
|
+
Identity: () => Identity,
|
|
27
|
+
LocalIdentity: () => LocalIdentity,
|
|
28
|
+
NodeType: () => NodeType,
|
|
29
|
+
Packet: () => Packet,
|
|
30
|
+
PayloadType: () => PayloadType,
|
|
31
|
+
PrivateKey: () => PrivateKey,
|
|
32
|
+
PublicKey: () => PublicKey,
|
|
33
|
+
RequestType: () => RequestType,
|
|
34
|
+
RouteType: () => RouteType,
|
|
35
|
+
SharedSecret: () => SharedSecret,
|
|
36
|
+
StaticSecret: () => StaticSecret,
|
|
37
|
+
TextType: () => TextType,
|
|
38
|
+
parseNodeHash: () => parseNodeHash
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(index_exports);
|
|
41
|
+
|
|
1
42
|
// src/packet.types.ts
|
|
2
43
|
var RouteType = /* @__PURE__ */ ((RouteType2) => {
|
|
3
44
|
RouteType2[RouteType2["TRANSPORT_FLOOD"] = 0] = "TRANSPORT_FLOOD";
|
|
@@ -45,127 +86,8 @@ var NodeType = /* @__PURE__ */ ((NodeType2) => {
|
|
|
45
86
|
})(NodeType || {});
|
|
46
87
|
|
|
47
88
|
// src/packet.ts
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// src/parser.ts
|
|
51
|
-
import { equalBytes } from "@noble/ciphers/utils.js";
|
|
52
|
-
import { bytesToHex, hexToBytes as nobleHexToBytes } from "@noble/hashes/utils.js";
|
|
53
|
-
var base64ToBytes = (base64, size) => {
|
|
54
|
-
let normalized = base64.replace(/-/g, "+").replace(/_/g, "/");
|
|
55
|
-
while (normalized.length % 4 !== 0) {
|
|
56
|
-
normalized += "=";
|
|
57
|
-
}
|
|
58
|
-
const binaryString = atob(normalized);
|
|
59
|
-
const bytes = new Uint8Array(binaryString.length);
|
|
60
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
61
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
62
|
-
}
|
|
63
|
-
if (size !== void 0 && bytes.length !== size) {
|
|
64
|
-
throw new Error(`Invalid base64 length: expected ${size} bytes, got ${bytes.length}`);
|
|
65
|
-
}
|
|
66
|
-
return bytes;
|
|
67
|
-
};
|
|
68
|
-
var hexToBytes = (hex, size) => {
|
|
69
|
-
const bytes = nobleHexToBytes(hex);
|
|
70
|
-
if (size !== void 0 && bytes.length !== size) {
|
|
71
|
-
throw new Error(`Invalid hex length: expected ${size} bytes, got ${bytes.length}`);
|
|
72
|
-
}
|
|
73
|
-
return bytes;
|
|
74
|
-
};
|
|
75
|
-
var BufferReader = class {
|
|
76
|
-
constructor(buffer) {
|
|
77
|
-
this.buffer = buffer;
|
|
78
|
-
this.offset = 0;
|
|
79
|
-
}
|
|
80
|
-
readByte() {
|
|
81
|
-
if (!this.hasMore()) throw new Error("read past end");
|
|
82
|
-
return this.buffer[this.offset++];
|
|
83
|
-
}
|
|
84
|
-
readBytes(length) {
|
|
85
|
-
if (length === void 0) {
|
|
86
|
-
length = this.buffer.length - this.offset;
|
|
87
|
-
}
|
|
88
|
-
if (this.remainingBytes() < length) throw new Error("read past end");
|
|
89
|
-
const bytes = this.buffer.slice(this.offset, this.offset + length);
|
|
90
|
-
this.offset += length;
|
|
91
|
-
return bytes;
|
|
92
|
-
}
|
|
93
|
-
hasMore() {
|
|
94
|
-
return this.offset < this.buffer.length;
|
|
95
|
-
}
|
|
96
|
-
remainingBytes() {
|
|
97
|
-
return this.buffer.length - this.offset;
|
|
98
|
-
}
|
|
99
|
-
peekByte() {
|
|
100
|
-
if (!this.hasMore()) throw new Error("read past end");
|
|
101
|
-
return this.buffer[this.offset];
|
|
102
|
-
}
|
|
103
|
-
readUint16LE() {
|
|
104
|
-
if (this.remainingBytes() < 2) throw new Error("read past end");
|
|
105
|
-
const value = this.buffer[this.offset] | this.buffer[this.offset + 1] << 8;
|
|
106
|
-
this.offset += 2;
|
|
107
|
-
return value;
|
|
108
|
-
}
|
|
109
|
-
readUint32LE() {
|
|
110
|
-
if (this.remainingBytes() < 4) throw new Error("read past end");
|
|
111
|
-
const value = (this.buffer[this.offset] | this.buffer[this.offset + 1] << 8 | this.buffer[this.offset + 2] << 16 | this.buffer[this.offset + 3] << 24) >>> 0;
|
|
112
|
-
this.offset += 4;
|
|
113
|
-
return value;
|
|
114
|
-
}
|
|
115
|
-
readInt16LE() {
|
|
116
|
-
if (this.remainingBytes() < 2) throw new Error("read past end");
|
|
117
|
-
const value = this.buffer[this.offset] | this.buffer[this.offset + 1] << 8;
|
|
118
|
-
this.offset += 2;
|
|
119
|
-
return value < 32768 ? value : value - 65536;
|
|
120
|
-
}
|
|
121
|
-
readInt32LE() {
|
|
122
|
-
if (this.remainingBytes() < 4) throw new Error("read past end");
|
|
123
|
-
const u = (this.buffer[this.offset] | this.buffer[this.offset + 1] << 8 | this.buffer[this.offset + 2] << 16 | this.buffer[this.offset + 3] << 24) >>> 0;
|
|
124
|
-
this.offset += 4;
|
|
125
|
-
return u < 2147483648 ? u : u - 4294967296;
|
|
126
|
-
}
|
|
127
|
-
readTimestamp() {
|
|
128
|
-
const timestamp = this.readUint32LE();
|
|
129
|
-
return new Date(timestamp * 1e3);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
var BufferWriter = class {
|
|
133
|
-
constructor() {
|
|
134
|
-
this.buffer = [];
|
|
135
|
-
}
|
|
136
|
-
writeByte(value) {
|
|
137
|
-
this.buffer.push(value & 255);
|
|
138
|
-
}
|
|
139
|
-
writeBytes(bytes) {
|
|
140
|
-
this.buffer.push(...bytes);
|
|
141
|
-
}
|
|
142
|
-
writeUint16LE(value) {
|
|
143
|
-
this.buffer.push(value & 255, value >> 8 & 255);
|
|
144
|
-
}
|
|
145
|
-
writeUint32LE(value) {
|
|
146
|
-
this.buffer.push(
|
|
147
|
-
value & 255,
|
|
148
|
-
value >> 8 & 255,
|
|
149
|
-
value >> 16 & 255,
|
|
150
|
-
value >> 24 & 255
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
writeInt16LE(value) {
|
|
154
|
-
this.writeUint16LE(value < 0 ? value + 65536 : value);
|
|
155
|
-
}
|
|
156
|
-
writeInt32LE(value) {
|
|
157
|
-
this.writeUint32LE(value < 0 ? value + 4294967296 : value);
|
|
158
|
-
}
|
|
159
|
-
writeTimestamp(date) {
|
|
160
|
-
const timestamp = Math.floor(date.getTime() / 1e3);
|
|
161
|
-
this.writeUint32LE(timestamp);
|
|
162
|
-
}
|
|
163
|
-
toBytes() {
|
|
164
|
-
return new Uint8Array(this.buffer);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
// src/packet.ts
|
|
89
|
+
var import_sha2 = require("@noble/hashes/sha2.js");
|
|
90
|
+
var import_packet2 = require("@hamradio/packet");
|
|
169
91
|
var Packet = class _Packet {
|
|
170
92
|
constructor(header, transport, pathLength, path, payload) {
|
|
171
93
|
this.header = header;
|
|
@@ -182,13 +104,13 @@ var Packet = class _Packet {
|
|
|
182
104
|
this.pathHashes = [];
|
|
183
105
|
for (let i = 0; i < this.pathHashBytes; i += this.pathHashSize) {
|
|
184
106
|
const hashBytes = this.path.slice(i, i + this.pathHashSize);
|
|
185
|
-
const hashHex = bytesToHex(hashBytes);
|
|
107
|
+
const hashHex = (0, import_packet2.bytesToHex)(hashBytes);
|
|
186
108
|
this.pathHashes.push(hashHex);
|
|
187
109
|
}
|
|
188
110
|
}
|
|
189
111
|
static fromBytes(bytes) {
|
|
190
112
|
if (typeof bytes === "string") {
|
|
191
|
-
bytes = base64ToBytes(bytes);
|
|
113
|
+
bytes = (0, import_packet2.base64ToBytes)(bytes);
|
|
192
114
|
}
|
|
193
115
|
let offset = 0;
|
|
194
116
|
const header = bytes[offset++];
|
|
@@ -209,14 +131,14 @@ var Packet = class _Packet {
|
|
|
209
131
|
return routeType === 0 /* TRANSPORT_FLOOD */ || routeType === 3 /* TRANSPORT_DIRECT */;
|
|
210
132
|
}
|
|
211
133
|
hash() {
|
|
212
|
-
const hash = sha256.create();
|
|
134
|
+
const hash = import_sha2.sha256.create();
|
|
213
135
|
hash.update(new Uint8Array([this.payloadType]));
|
|
214
136
|
if (this.payloadType === 9 /* TRACE */) {
|
|
215
137
|
hash.update(new Uint8Array([this.pathLength]));
|
|
216
138
|
}
|
|
217
139
|
hash.update(this.payload);
|
|
218
140
|
const digest = hash.digest();
|
|
219
|
-
return bytesToHex(digest.slice(0, 8));
|
|
141
|
+
return (0, import_packet2.bytesToHex)(digest.slice(0, 8));
|
|
220
142
|
}
|
|
221
143
|
ensureStructure() {
|
|
222
144
|
if (typeof this.structure !== "undefined") {
|
|
@@ -225,13 +147,13 @@ var Packet = class _Packet {
|
|
|
225
147
|
let pathHashType;
|
|
226
148
|
switch (this.pathHashSize) {
|
|
227
149
|
case 1:
|
|
228
|
-
pathHashType =
|
|
150
|
+
pathHashType = import_packet2.FieldType.BYTES;
|
|
229
151
|
break;
|
|
230
152
|
case 2:
|
|
231
|
-
pathHashType =
|
|
153
|
+
pathHashType = import_packet2.FieldType.WORDS;
|
|
232
154
|
break;
|
|
233
155
|
case 4:
|
|
234
|
-
pathHashType =
|
|
156
|
+
pathHashType = import_packet2.FieldType.DWORDS;
|
|
235
157
|
break;
|
|
236
158
|
default:
|
|
237
159
|
throw new Error(`Unsupported path hash size: ${this.pathHashSize}`);
|
|
@@ -240,13 +162,13 @@ var Packet = class _Packet {
|
|
|
240
162
|
/* Header segment */
|
|
241
163
|
{
|
|
242
164
|
name: "header",
|
|
243
|
-
data: new Uint8Array([this.header]),
|
|
165
|
+
data: new Uint8Array([this.header]).buffer,
|
|
244
166
|
fields: [
|
|
245
167
|
/* Header flags */
|
|
246
168
|
{
|
|
247
169
|
name: "flags",
|
|
248
|
-
type:
|
|
249
|
-
|
|
170
|
+
type: import_packet2.FieldType.BITS,
|
|
171
|
+
length: 1,
|
|
250
172
|
bits: [
|
|
251
173
|
{ name: "payload version", size: 2 },
|
|
252
174
|
{ name: "payload type", size: 4 },
|
|
@@ -256,38 +178,40 @@ var Packet = class _Packet {
|
|
|
256
178
|
]
|
|
257
179
|
},
|
|
258
180
|
/* Transport codes */
|
|
259
|
-
..._Packet.hasTransportCodes(this.routeType) ? [
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
181
|
+
..._Packet.hasTransportCodes(this.routeType) ? [
|
|
182
|
+
{
|
|
183
|
+
name: "transport codes",
|
|
184
|
+
data: new Uint8Array([
|
|
185
|
+
this.transport[0] >> 8 & 255,
|
|
186
|
+
this.transport[0] & 255,
|
|
187
|
+
this.transport[1] >> 8 & 255,
|
|
188
|
+
this.transport[1] & 255
|
|
189
|
+
]).buffer,
|
|
190
|
+
fields: [
|
|
191
|
+
{
|
|
192
|
+
name: "transport code 1",
|
|
193
|
+
type: import_packet2.FieldType.UINT16_BE,
|
|
194
|
+
length: 2,
|
|
195
|
+
value: this.transport[0]
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "transport code 2",
|
|
199
|
+
type: import_packet2.FieldType.UINT16_BE,
|
|
200
|
+
length: 2,
|
|
201
|
+
value: this.transport[1]
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
] : [],
|
|
282
206
|
/* Path length and hashes */
|
|
283
207
|
{
|
|
284
208
|
name: "path",
|
|
285
|
-
data: new Uint8Array([this.pathLength, ...this.path]),
|
|
209
|
+
data: new Uint8Array([this.pathLength, ...this.path]).buffer,
|
|
286
210
|
fields: [
|
|
287
211
|
{
|
|
288
212
|
name: "path length",
|
|
289
|
-
type:
|
|
290
|
-
|
|
213
|
+
type: import_packet2.FieldType.UINT8,
|
|
214
|
+
length: 1,
|
|
291
215
|
bits: [
|
|
292
216
|
{ name: "path hash size", size: 2 },
|
|
293
217
|
{ name: "path hash count", size: 6 }
|
|
@@ -296,7 +220,7 @@ var Packet = class _Packet {
|
|
|
296
220
|
{
|
|
297
221
|
name: "path hashes",
|
|
298
222
|
type: pathHashType,
|
|
299
|
-
|
|
223
|
+
length: this.path.length
|
|
300
224
|
}
|
|
301
225
|
]
|
|
302
226
|
}
|
|
@@ -341,7 +265,6 @@ var Packet = class _Packet {
|
|
|
341
265
|
default:
|
|
342
266
|
throw new Error(`Unsupported payload type: ${this.payloadType}`);
|
|
343
267
|
}
|
|
344
|
-
console.log("packet decode with structure:", typeof withStructure, withStructure, { result });
|
|
345
268
|
if (typeof withStructure === "boolean" && withStructure && "segment" in result && "payload" in result) {
|
|
346
269
|
this.ensureStructure();
|
|
347
270
|
const structure = [...this.structure, result.segment];
|
|
@@ -350,17 +273,17 @@ var Packet = class _Packet {
|
|
|
350
273
|
return result;
|
|
351
274
|
}
|
|
352
275
|
decodeEncryptedPayload(reader) {
|
|
353
|
-
const cipherMAC = reader.
|
|
354
|
-
const cipherText = reader.
|
|
276
|
+
const cipherMAC = reader.bytes(2);
|
|
277
|
+
const cipherText = reader.bytes();
|
|
355
278
|
return { cipherMAC, cipherText };
|
|
356
279
|
}
|
|
357
280
|
decodeRequest(withSegment) {
|
|
358
281
|
if (this.payload.length < 4) {
|
|
359
282
|
throw new Error("Invalid request payload: too short");
|
|
360
283
|
}
|
|
361
|
-
const reader =
|
|
362
|
-
const dst = reader.
|
|
363
|
-
const src = reader.
|
|
284
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
285
|
+
const dst = reader.uint8();
|
|
286
|
+
const src = reader.uint8();
|
|
364
287
|
const encrypted = this.decodeEncryptedPayload(reader);
|
|
365
288
|
const payload = {
|
|
366
289
|
type: 0 /* REQUEST */,
|
|
@@ -371,12 +294,17 @@ var Packet = class _Packet {
|
|
|
371
294
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
372
295
|
const segment = {
|
|
373
296
|
name: "request payload",
|
|
374
|
-
data: this.payload,
|
|
297
|
+
data: new Uint8Array(this.payload).buffer,
|
|
375
298
|
fields: [
|
|
376
|
-
{ name: "destination hash", type:
|
|
377
|
-
{ name: "source hash", type:
|
|
378
|
-
{ name: "cipher MAC", type:
|
|
379
|
-
{
|
|
299
|
+
{ name: "destination hash", type: import_packet2.FieldType.UINT8, length: 1, value: dst },
|
|
300
|
+
{ name: "source hash", type: import_packet2.FieldType.UINT8, length: 1, value: src },
|
|
301
|
+
{ name: "cipher MAC", type: import_packet2.FieldType.BYTES, length: 2, value: encrypted.cipherMAC },
|
|
302
|
+
{
|
|
303
|
+
name: "cipher text",
|
|
304
|
+
type: import_packet2.FieldType.BYTES,
|
|
305
|
+
length: encrypted.cipherText.length,
|
|
306
|
+
value: encrypted.cipherText
|
|
307
|
+
}
|
|
380
308
|
]
|
|
381
309
|
};
|
|
382
310
|
return { payload, segment };
|
|
@@ -387,9 +315,9 @@ var Packet = class _Packet {
|
|
|
387
315
|
if (this.payload.length < 4) {
|
|
388
316
|
throw new Error("Invalid response payload: too short");
|
|
389
317
|
}
|
|
390
|
-
const reader =
|
|
391
|
-
const dst = reader.
|
|
392
|
-
const src = reader.
|
|
318
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
319
|
+
const dst = reader.uint8();
|
|
320
|
+
const src = reader.uint8();
|
|
393
321
|
const encrypted = this.decodeEncryptedPayload(reader);
|
|
394
322
|
const payload = {
|
|
395
323
|
type: 1 /* RESPONSE */,
|
|
@@ -400,12 +328,17 @@ var Packet = class _Packet {
|
|
|
400
328
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
401
329
|
const segment = {
|
|
402
330
|
name: "response payload",
|
|
403
|
-
data: this.payload,
|
|
331
|
+
data: new Uint8Array(this.payload).buffer,
|
|
404
332
|
fields: [
|
|
405
|
-
{ name: "destination hash", type:
|
|
406
|
-
{ name: "source hash", type:
|
|
407
|
-
{ name: "cipher MAC", type:
|
|
408
|
-
{
|
|
333
|
+
{ name: "destination hash", type: import_packet2.FieldType.UINT8, length: 1, value: dst },
|
|
334
|
+
{ name: "source hash", type: import_packet2.FieldType.UINT8, length: 1, value: src },
|
|
335
|
+
{ name: "cipher MAC", type: import_packet2.FieldType.BYTES, length: 2, value: encrypted.cipherMAC },
|
|
336
|
+
{
|
|
337
|
+
name: "cipher text",
|
|
338
|
+
type: import_packet2.FieldType.BYTES,
|
|
339
|
+
length: encrypted.cipherText.length,
|
|
340
|
+
value: encrypted.cipherText
|
|
341
|
+
}
|
|
409
342
|
]
|
|
410
343
|
};
|
|
411
344
|
return { payload, segment };
|
|
@@ -416,9 +349,9 @@ var Packet = class _Packet {
|
|
|
416
349
|
if (this.payload.length < 4) {
|
|
417
350
|
throw new Error("Invalid text payload: too short");
|
|
418
351
|
}
|
|
419
|
-
const reader =
|
|
420
|
-
const dst = reader.
|
|
421
|
-
const src = reader.
|
|
352
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
353
|
+
const dst = reader.uint8();
|
|
354
|
+
const src = reader.uint8();
|
|
422
355
|
const encrypted = this.decodeEncryptedPayload(reader);
|
|
423
356
|
const payload = {
|
|
424
357
|
type: 2 /* TEXT */,
|
|
@@ -429,12 +362,17 @@ var Packet = class _Packet {
|
|
|
429
362
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
430
363
|
const segment = {
|
|
431
364
|
name: "text payload",
|
|
432
|
-
data: this.payload,
|
|
365
|
+
data: new Uint8Array(this.payload).buffer,
|
|
433
366
|
fields: [
|
|
434
|
-
{ name: "destination hash", type:
|
|
435
|
-
{ name: "source hash", type:
|
|
436
|
-
{ name: "cipher MAC", type:
|
|
437
|
-
{
|
|
367
|
+
{ name: "destination hash", type: import_packet2.FieldType.UINT8, length: 1, value: dst },
|
|
368
|
+
{ name: "source hash", type: import_packet2.FieldType.UINT8, length: 1, value: src },
|
|
369
|
+
{ name: "cipher MAC", type: import_packet2.FieldType.BYTES, length: 2, value: encrypted.cipherMAC },
|
|
370
|
+
{
|
|
371
|
+
name: "cipher text",
|
|
372
|
+
type: import_packet2.FieldType.BYTES,
|
|
373
|
+
length: encrypted.cipherText.length,
|
|
374
|
+
value: encrypted.cipherText
|
|
375
|
+
}
|
|
438
376
|
]
|
|
439
377
|
};
|
|
440
378
|
return { payload, segment };
|
|
@@ -445,8 +383,8 @@ var Packet = class _Packet {
|
|
|
445
383
|
if (this.payload.length < 4) {
|
|
446
384
|
throw new Error("Invalid ack payload: too short");
|
|
447
385
|
}
|
|
448
|
-
const reader =
|
|
449
|
-
const checksum = reader.
|
|
386
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
387
|
+
const checksum = reader.bytes(4);
|
|
450
388
|
const payload = {
|
|
451
389
|
type: 3 /* ACK */,
|
|
452
390
|
checksum
|
|
@@ -454,10 +392,8 @@ var Packet = class _Packet {
|
|
|
454
392
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
455
393
|
const segment = {
|
|
456
394
|
name: "ack payload",
|
|
457
|
-
data: this.payload,
|
|
458
|
-
fields: [
|
|
459
|
-
{ name: "checksum", type: 6 /* BYTES */, size: 4, value: checksum }
|
|
460
|
-
]
|
|
395
|
+
data: new Uint8Array(this.payload).buffer,
|
|
396
|
+
fields: [{ name: "checksum", type: import_packet2.FieldType.BYTES, length: 4, value: checksum }]
|
|
461
397
|
};
|
|
462
398
|
return { payload, segment };
|
|
463
399
|
}
|
|
@@ -467,26 +403,26 @@ var Packet = class _Packet {
|
|
|
467
403
|
if (this.payload.length < 4) {
|
|
468
404
|
throw new Error("Invalid advert payload: too short");
|
|
469
405
|
}
|
|
470
|
-
const reader =
|
|
406
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
471
407
|
const payload = {
|
|
472
408
|
type: 4 /* ADVERT */,
|
|
473
|
-
publicKey: reader.
|
|
474
|
-
timestamp: reader.
|
|
475
|
-
signature: reader.
|
|
409
|
+
publicKey: reader.bytes(32),
|
|
410
|
+
timestamp: reader.date32(),
|
|
411
|
+
signature: reader.bytes(64)
|
|
476
412
|
};
|
|
477
413
|
let segment;
|
|
478
414
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
479
415
|
segment = {
|
|
480
416
|
name: "advert payload",
|
|
481
|
-
data: this.payload,
|
|
417
|
+
data: new Uint8Array(this.payload).buffer,
|
|
482
418
|
fields: [
|
|
483
|
-
{ type:
|
|
484
|
-
{ type:
|
|
485
|
-
{ type:
|
|
419
|
+
{ type: import_packet2.FieldType.BYTES, name: "public key", length: 32 },
|
|
420
|
+
{ type: import_packet2.FieldType.UINT32_LE, name: "timestamp", length: 4, value: payload.timestamp },
|
|
421
|
+
{ type: import_packet2.FieldType.BYTES, name: "signature", length: 64 }
|
|
486
422
|
]
|
|
487
423
|
};
|
|
488
424
|
}
|
|
489
|
-
const flags = reader.
|
|
425
|
+
const flags = reader.uint8();
|
|
490
426
|
const appdata = {
|
|
491
427
|
nodeType: flags & 15,
|
|
492
428
|
hasLocation: (flags & 16 /* HAS_LOCATION */) !== 0,
|
|
@@ -495,44 +431,50 @@ var Packet = class _Packet {
|
|
|
495
431
|
hasName: (flags & 128 /* HAS_NAME */) !== 0
|
|
496
432
|
};
|
|
497
433
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
498
|
-
segment.fields.push({
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
434
|
+
segment.fields.push({
|
|
435
|
+
type: import_packet2.FieldType.BITS,
|
|
436
|
+
name: "flags",
|
|
437
|
+
length: 1,
|
|
438
|
+
value: flags,
|
|
439
|
+
bits: [
|
|
440
|
+
{ size: 1, name: "name flag" },
|
|
441
|
+
{ size: 1, name: "feature2 flag" },
|
|
442
|
+
{ size: 1, name: "feature1 flag" },
|
|
443
|
+
{ size: 1, name: "location flag" },
|
|
444
|
+
{ size: 4, name: "node type" }
|
|
445
|
+
]
|
|
446
|
+
});
|
|
505
447
|
}
|
|
506
448
|
if (appdata.hasLocation) {
|
|
507
|
-
const lat = reader.
|
|
508
|
-
const lon = reader.
|
|
449
|
+
const lat = reader.int32() / 1e6;
|
|
450
|
+
const lon = reader.int32() / 1e6;
|
|
509
451
|
appdata.location = [lat, lon];
|
|
510
452
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
511
|
-
segment.fields.push({ type:
|
|
512
|
-
segment.fields.push({ type:
|
|
453
|
+
segment.fields.push({ type: import_packet2.FieldType.UINT32_LE, name: "latitude", length: 4, value: lat });
|
|
454
|
+
segment.fields.push({ type: import_packet2.FieldType.UINT32_LE, name: "longitude", length: 4, value: lon });
|
|
513
455
|
}
|
|
514
456
|
}
|
|
515
457
|
if (appdata.hasFeature1) {
|
|
516
|
-
appdata.feature1 = reader.
|
|
458
|
+
appdata.feature1 = reader.uint16();
|
|
517
459
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
518
|
-
segment.fields.push({ type:
|
|
460
|
+
segment.fields.push({ type: import_packet2.FieldType.UINT16_LE, name: "feature1", length: 2, value: appdata.feature1 });
|
|
519
461
|
}
|
|
520
462
|
}
|
|
521
463
|
if (appdata.hasFeature2) {
|
|
522
|
-
appdata.feature2 = reader.
|
|
464
|
+
appdata.feature2 = reader.uint16();
|
|
523
465
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
524
|
-
segment.fields.push({ type:
|
|
466
|
+
segment.fields.push({ type: import_packet2.FieldType.UINT16_LE, name: "feature2", length: 2, value: appdata.feature2 });
|
|
525
467
|
}
|
|
526
468
|
}
|
|
527
469
|
if (appdata.hasName) {
|
|
528
|
-
|
|
529
|
-
let nullPos = nameBytes.indexOf(0);
|
|
530
|
-
if (nullPos === -1) {
|
|
531
|
-
nullPos = nameBytes.length;
|
|
532
|
-
}
|
|
533
|
-
appdata.name = new TextDecoder("utf-8").decode(nameBytes.subarray(0, nullPos));
|
|
470
|
+
appdata.name = reader.cString();
|
|
534
471
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
535
|
-
segment.fields.push({
|
|
472
|
+
segment.fields.push({
|
|
473
|
+
type: import_packet2.FieldType.C_STRING,
|
|
474
|
+
name: "name",
|
|
475
|
+
length: appdata.name.length,
|
|
476
|
+
value: appdata.name
|
|
477
|
+
});
|
|
536
478
|
}
|
|
537
479
|
}
|
|
538
480
|
if (typeof withSegment === "boolean" && withSegment && typeof segment !== "undefined") {
|
|
@@ -544,8 +486,8 @@ var Packet = class _Packet {
|
|
|
544
486
|
if (this.payload.length < 3) {
|
|
545
487
|
throw new Error("Invalid group text payload: too short");
|
|
546
488
|
}
|
|
547
|
-
const reader =
|
|
548
|
-
const channelHash = reader.
|
|
489
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
490
|
+
const channelHash = reader.uint8();
|
|
549
491
|
const encrypted = this.decodeEncryptedPayload(reader);
|
|
550
492
|
const payload = {
|
|
551
493
|
type: 5 /* GROUP_TEXT */,
|
|
@@ -555,11 +497,16 @@ var Packet = class _Packet {
|
|
|
555
497
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
556
498
|
const segment = {
|
|
557
499
|
name: "group text payload",
|
|
558
|
-
data: this.payload,
|
|
500
|
+
data: new Uint8Array(this.payload).buffer,
|
|
559
501
|
fields: [
|
|
560
|
-
{ name: "channel hash", type:
|
|
561
|
-
{ name: "cipher MAC", type:
|
|
562
|
-
{
|
|
502
|
+
{ name: "channel hash", type: import_packet2.FieldType.UINT8, length: 1, value: channelHash },
|
|
503
|
+
{ name: "cipher MAC", type: import_packet2.FieldType.BYTES, length: 2, value: encrypted.cipherMAC },
|
|
504
|
+
{
|
|
505
|
+
name: "cipher text",
|
|
506
|
+
type: import_packet2.FieldType.BYTES,
|
|
507
|
+
length: encrypted.cipherText.length,
|
|
508
|
+
value: encrypted.cipherText
|
|
509
|
+
}
|
|
563
510
|
]
|
|
564
511
|
};
|
|
565
512
|
return { payload, segment };
|
|
@@ -570,20 +517,25 @@ var Packet = class _Packet {
|
|
|
570
517
|
if (this.payload.length < 3) {
|
|
571
518
|
throw new Error("Invalid group data payload: too short");
|
|
572
519
|
}
|
|
573
|
-
const reader =
|
|
520
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
574
521
|
const payload = {
|
|
575
522
|
type: 6 /* GROUP_DATA */,
|
|
576
|
-
channelHash: reader.
|
|
523
|
+
channelHash: reader.uint8(),
|
|
577
524
|
encrypted: this.decodeEncryptedPayload(reader)
|
|
578
525
|
};
|
|
579
526
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
580
527
|
const segment = {
|
|
581
528
|
name: "group data payload",
|
|
582
|
-
data: this.payload,
|
|
529
|
+
data: new Uint8Array(this.payload).buffer,
|
|
583
530
|
fields: [
|
|
584
|
-
{ name: "channel hash", type:
|
|
585
|
-
{ name: "cipher MAC", type:
|
|
586
|
-
{
|
|
531
|
+
{ name: "channel hash", type: import_packet2.FieldType.UINT8, length: 1, value: payload.channelHash },
|
|
532
|
+
{ name: "cipher MAC", type: import_packet2.FieldType.BYTES, length: 2, value: payload.encrypted.cipherMAC },
|
|
533
|
+
{
|
|
534
|
+
name: "cipher text",
|
|
535
|
+
type: import_packet2.FieldType.BYTES,
|
|
536
|
+
length: payload.encrypted.cipherText.length,
|
|
537
|
+
value: payload.encrypted.cipherText
|
|
538
|
+
}
|
|
587
539
|
]
|
|
588
540
|
};
|
|
589
541
|
return { payload, segment };
|
|
@@ -594,22 +546,27 @@ var Packet = class _Packet {
|
|
|
594
546
|
if (this.payload.length < 1 + 32 + 2) {
|
|
595
547
|
throw new Error("Invalid anon req payload: too short");
|
|
596
548
|
}
|
|
597
|
-
const reader =
|
|
549
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
598
550
|
const payload = {
|
|
599
551
|
type: 7 /* ANON_REQ */,
|
|
600
|
-
dst: reader.
|
|
601
|
-
publicKey: reader.
|
|
552
|
+
dst: reader.uint8(),
|
|
553
|
+
publicKey: reader.bytes(32),
|
|
602
554
|
encrypted: this.decodeEncryptedPayload(reader)
|
|
603
555
|
};
|
|
604
556
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
605
557
|
const segment = {
|
|
606
558
|
name: "anon req payload",
|
|
607
|
-
data: this.payload,
|
|
559
|
+
data: new Uint8Array(this.payload).buffer,
|
|
608
560
|
fields: [
|
|
609
|
-
{ name: "destination hash", type:
|
|
610
|
-
{ name: "public key", type:
|
|
611
|
-
{ name: "cipher MAC", type:
|
|
612
|
-
{
|
|
561
|
+
{ name: "destination hash", type: import_packet2.FieldType.UINT8, length: 1, value: payload.dst },
|
|
562
|
+
{ name: "public key", type: import_packet2.FieldType.BYTES, length: 32, value: payload.publicKey },
|
|
563
|
+
{ name: "cipher MAC", type: import_packet2.FieldType.BYTES, length: 2, value: payload.encrypted.cipherMAC },
|
|
564
|
+
{
|
|
565
|
+
name: "cipher text",
|
|
566
|
+
type: import_packet2.FieldType.BYTES,
|
|
567
|
+
length: payload.encrypted.cipherText.length,
|
|
568
|
+
value: payload.encrypted.cipherText
|
|
569
|
+
}
|
|
613
570
|
]
|
|
614
571
|
};
|
|
615
572
|
return { payload, segment };
|
|
@@ -620,19 +577,19 @@ var Packet = class _Packet {
|
|
|
620
577
|
if (this.payload.length < 2) {
|
|
621
578
|
throw new Error("Invalid path payload: too short");
|
|
622
579
|
}
|
|
623
|
-
const reader =
|
|
580
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
624
581
|
const payload = {
|
|
625
582
|
type: 8 /* PATH */,
|
|
626
|
-
dst: reader.
|
|
627
|
-
src: reader.
|
|
583
|
+
dst: reader.uint8(),
|
|
584
|
+
src: reader.uint8()
|
|
628
585
|
};
|
|
629
586
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
630
587
|
const segment = {
|
|
631
588
|
name: "path payload",
|
|
632
|
-
data: this.payload,
|
|
589
|
+
data: new Uint8Array(this.payload).buffer,
|
|
633
590
|
fields: [
|
|
634
|
-
{ name: "destination hash", type:
|
|
635
|
-
{ name: "source hash", type:
|
|
591
|
+
{ name: "destination hash", type: import_packet2.FieldType.UINT8, length: 1, value: payload.dst },
|
|
592
|
+
{ name: "source hash", type: import_packet2.FieldType.UINT8, length: 1, value: payload.src }
|
|
636
593
|
]
|
|
637
594
|
};
|
|
638
595
|
return { payload, segment };
|
|
@@ -643,23 +600,23 @@ var Packet = class _Packet {
|
|
|
643
600
|
if (this.payload.length < 9) {
|
|
644
601
|
throw new Error("Invalid trace payload: too short");
|
|
645
602
|
}
|
|
646
|
-
const reader =
|
|
603
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
647
604
|
const payload = {
|
|
648
605
|
type: 9 /* TRACE */,
|
|
649
|
-
tag: reader.
|
|
650
|
-
authCode: reader.
|
|
651
|
-
flags: reader.
|
|
652
|
-
nodes: reader.
|
|
606
|
+
tag: reader.uint32(),
|
|
607
|
+
authCode: reader.uint32(),
|
|
608
|
+
flags: reader.uint8() & 3,
|
|
609
|
+
nodes: reader.bytes()
|
|
653
610
|
};
|
|
654
611
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
655
612
|
const segment = {
|
|
656
613
|
name: "trace payload",
|
|
657
|
-
data: this.payload,
|
|
614
|
+
data: new Uint8Array(this.payload).buffer,
|
|
658
615
|
fields: [
|
|
659
|
-
{ name: "tag", type:
|
|
660
|
-
{ name: "auth code", type:
|
|
661
|
-
{ name: "flags", type:
|
|
662
|
-
{ name: "nodes", type:
|
|
616
|
+
{ name: "tag", type: import_packet2.FieldType.DWORDS, length: 4, value: payload.tag },
|
|
617
|
+
{ name: "auth code", type: import_packet2.FieldType.DWORDS, length: 4, value: payload.authCode },
|
|
618
|
+
{ name: "flags", type: import_packet2.FieldType.UINT8, length: 1, value: payload.flags },
|
|
619
|
+
{ name: "nodes", type: import_packet2.FieldType.BYTES, length: payload.nodes.length, value: payload.nodes }
|
|
663
620
|
]
|
|
664
621
|
};
|
|
665
622
|
return { payload, segment };
|
|
@@ -674,10 +631,8 @@ var Packet = class _Packet {
|
|
|
674
631
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
675
632
|
const segment = {
|
|
676
633
|
name: "raw custom payload",
|
|
677
|
-
data: this.payload,
|
|
678
|
-
fields: [
|
|
679
|
-
{ name: "data", type: 6 /* BYTES */, size: this.payload.length, value: this.payload }
|
|
680
|
-
]
|
|
634
|
+
data: new Uint8Array(this.payload).buffer,
|
|
635
|
+
fields: [{ name: "data", type: import_packet2.FieldType.BYTES, length: this.payload.length, value: this.payload }]
|
|
681
636
|
};
|
|
682
637
|
return { payload, segment };
|
|
683
638
|
}
|
|
@@ -686,35 +641,39 @@ var Packet = class _Packet {
|
|
|
686
641
|
};
|
|
687
642
|
|
|
688
643
|
// src/crypto.ts
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
644
|
+
var import_ed25519 = require("@noble/curves/ed25519.js");
|
|
645
|
+
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
646
|
+
var import_hmac = require("@noble/hashes/hmac.js");
|
|
647
|
+
var import_aes = require("@noble/ciphers/aes.js");
|
|
648
|
+
var import_packet3 = require("@hamradio/packet");
|
|
693
649
|
var PUBLIC_KEY_SIZE = 32;
|
|
694
650
|
var SEED_SIZE = 32;
|
|
695
651
|
var HMAC_SIZE = 2;
|
|
696
|
-
var SHARED_SECRET_SIZE =
|
|
652
|
+
var SHARED_SECRET_SIZE = 16;
|
|
697
653
|
var SIGNATURE_SIZE = 64;
|
|
698
654
|
var STATIC_SECRET_SIZE = 32;
|
|
699
|
-
var publicSecret = hexToBytes("8b3387e9c5cdea6ac9e5edbaa115cd72"
|
|
655
|
+
var publicSecret = (0, import_packet3.hexToBytes)("8b3387e9c5cdea6ac9e5edbaa115cd72");
|
|
700
656
|
var PublicKey = class _PublicKey {
|
|
701
657
|
constructor(key) {
|
|
702
658
|
if (typeof key === "string") {
|
|
703
|
-
this.key =
|
|
659
|
+
this.key = (0, import_packet3.hexToBytes)(key);
|
|
704
660
|
} else if (key instanceof Uint8Array) {
|
|
705
661
|
this.key = key;
|
|
706
662
|
} else {
|
|
707
663
|
throw new Error("Invalid type for PublicKey constructor");
|
|
708
664
|
}
|
|
665
|
+
if (this.key.length !== PUBLIC_KEY_SIZE) {
|
|
666
|
+
throw new Error(`Invalid public key length: expected ${PUBLIC_KEY_SIZE} bytes, got ${this.key.length}`);
|
|
667
|
+
}
|
|
709
668
|
}
|
|
710
669
|
toHash() {
|
|
711
|
-
return
|
|
670
|
+
return import_sha22.sha256.create().update(this.key).digest()[0];
|
|
712
671
|
}
|
|
713
672
|
toBytes() {
|
|
714
673
|
return this.key;
|
|
715
674
|
}
|
|
716
675
|
toString() {
|
|
717
|
-
return bytesToHex(this.key);
|
|
676
|
+
return (0, import_packet3.bytesToHex)(this.key);
|
|
718
677
|
}
|
|
719
678
|
equals(other) {
|
|
720
679
|
let otherKey;
|
|
@@ -723,28 +682,39 @@ var PublicKey = class _PublicKey {
|
|
|
723
682
|
} else if (other instanceof Uint8Array) {
|
|
724
683
|
otherKey = other;
|
|
725
684
|
} else if (typeof other === "string") {
|
|
726
|
-
otherKey =
|
|
685
|
+
otherKey = (0, import_packet3.hexToBytes)(other);
|
|
727
686
|
} else {
|
|
728
687
|
throw new Error("Invalid type for PublicKey comparison");
|
|
729
688
|
}
|
|
730
|
-
|
|
689
|
+
if (otherKey.length !== PUBLIC_KEY_SIZE) {
|
|
690
|
+
throw new Error(
|
|
691
|
+
`Invalid public key length for comparison: expected ${PUBLIC_KEY_SIZE} bytes, got ${otherKey.length}`
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
return (0, import_packet3.equalBytes)(this.key, otherKey);
|
|
731
695
|
}
|
|
732
696
|
verify(message, signature) {
|
|
733
697
|
if (signature.length !== SIGNATURE_SIZE) {
|
|
734
698
|
throw new Error(`Invalid signature length: expected ${SIGNATURE_SIZE} bytes, got ${signature.length}`);
|
|
735
699
|
}
|
|
736
|
-
return ed25519.verify(signature, message, this.key);
|
|
700
|
+
return import_ed25519.ed25519.verify(signature, message, this.key);
|
|
701
|
+
}
|
|
702
|
+
static fromBytes(key) {
|
|
703
|
+
return new _PublicKey(key);
|
|
704
|
+
}
|
|
705
|
+
static fromString(key) {
|
|
706
|
+
return new _PublicKey(key);
|
|
737
707
|
}
|
|
738
708
|
};
|
|
739
709
|
var PrivateKey = class _PrivateKey {
|
|
740
710
|
constructor(seed) {
|
|
741
711
|
if (typeof seed === "string") {
|
|
742
|
-
seed =
|
|
712
|
+
seed = (0, import_packet3.hexToBytes)(seed);
|
|
743
713
|
}
|
|
744
714
|
if (seed.length !== SEED_SIZE) {
|
|
745
715
|
throw new Error(`Invalid seed length: expected ${SEED_SIZE} bytes, got ${seed.length}`);
|
|
746
716
|
}
|
|
747
|
-
const { secretKey, publicKey } = ed25519.keygen(seed);
|
|
717
|
+
const { secretKey, publicKey } = import_ed25519.ed25519.keygen(seed);
|
|
748
718
|
this.secretKey = secretKey;
|
|
749
719
|
this.publicKey = new PublicKey(publicKey);
|
|
750
720
|
}
|
|
@@ -755,10 +725,10 @@ var PrivateKey = class _PrivateKey {
|
|
|
755
725
|
return this.secretKey;
|
|
756
726
|
}
|
|
757
727
|
toString() {
|
|
758
|
-
return bytesToHex(this.secretKey);
|
|
728
|
+
return (0, import_packet3.bytesToHex)(this.secretKey);
|
|
759
729
|
}
|
|
760
730
|
sign(message) {
|
|
761
|
-
return ed25519.sign(message, this.secretKey);
|
|
731
|
+
return import_ed25519.ed25519.sign(message, this.secretKey);
|
|
762
732
|
}
|
|
763
733
|
calculateSharedSecret(other) {
|
|
764
734
|
let otherPublicKey;
|
|
@@ -771,43 +741,40 @@ var PrivateKey = class _PrivateKey {
|
|
|
771
741
|
} else {
|
|
772
742
|
throw new Error("Invalid type for calculateSharedSecret comparison");
|
|
773
743
|
}
|
|
774
|
-
return x25519.getSharedSecret(this.secretKey, otherPublicKey.toBytes());
|
|
744
|
+
return import_ed25519.x25519.getSharedSecret(this.secretKey, otherPublicKey.toBytes());
|
|
775
745
|
}
|
|
776
746
|
static generate() {
|
|
777
|
-
const { secretKey } = ed25519.keygen();
|
|
747
|
+
const { secretKey } = import_ed25519.ed25519.keygen();
|
|
778
748
|
return new _PrivateKey(secretKey);
|
|
779
749
|
}
|
|
780
750
|
};
|
|
781
751
|
var SharedSecret = class _SharedSecret {
|
|
782
752
|
constructor(secret) {
|
|
783
|
-
if (secret.length === SHARED_SECRET_SIZE / 2) {
|
|
784
|
-
const padded = new Uint8Array(SHARED_SECRET_SIZE);
|
|
785
|
-
padded.set(secret, SHARED_SECRET_SIZE - secret.length);
|
|
786
|
-
secret = padded;
|
|
787
|
-
}
|
|
788
753
|
if (secret.length !== SHARED_SECRET_SIZE) {
|
|
789
754
|
throw new Error(`Invalid shared secret length: expected ${SHARED_SECRET_SIZE} bytes, got ${secret.length}`);
|
|
790
755
|
}
|
|
791
|
-
this.secret =
|
|
756
|
+
this.secret = new Uint8Array(SHARED_SECRET_SIZE * 2);
|
|
757
|
+
this.secret.set(secret, 0);
|
|
792
758
|
}
|
|
793
759
|
toHash() {
|
|
794
|
-
|
|
760
|
+
const hash = import_sha22.sha256.create().update(this.secret.slice(0, 16)).digest();
|
|
761
|
+
return hash[0];
|
|
795
762
|
}
|
|
796
763
|
toBytes() {
|
|
797
|
-
return this.secret;
|
|
764
|
+
return this.secret.slice(0, 16);
|
|
798
765
|
}
|
|
799
766
|
toString() {
|
|
800
|
-
return bytesToHex(this.secret);
|
|
767
|
+
return (0, import_packet3.bytesToHex)(this.secret.slice(0, 16));
|
|
801
768
|
}
|
|
802
769
|
decrypt(hmac2, ciphertext) {
|
|
803
770
|
if (hmac2.length !== HMAC_SIZE) {
|
|
804
771
|
throw new Error(`Invalid HMAC length: expected ${HMAC_SIZE} bytes, got ${hmac2.length}`);
|
|
805
772
|
}
|
|
806
773
|
const expectedHmac = this.calculateHmac(ciphertext);
|
|
807
|
-
if (!equalBytes(hmac2, expectedHmac)) {
|
|
808
|
-
throw new Error(`Invalid HMAC: decryption failed: expected ${bytesToHex(expectedHmac)}, got ${bytesToHex(hmac2)}`);
|
|
774
|
+
if (!(0, import_packet3.equalBytes)(hmac2, expectedHmac)) {
|
|
775
|
+
throw new Error(`Invalid HMAC: decryption failed: expected ${(0, import_packet3.bytesToHex)(expectedHmac)}, got ${(0, import_packet3.bytesToHex)(hmac2)}`);
|
|
809
776
|
}
|
|
810
|
-
const cipher = ecb(this.secret.slice(0, 16), { disablePadding: true });
|
|
777
|
+
const cipher = (0, import_aes.ecb)(this.secret.slice(0, 16), { disablePadding: true });
|
|
811
778
|
const plaintext = new Uint8Array(ciphertext.length);
|
|
812
779
|
for (let i = 0; i < ciphertext.length; i += 16) {
|
|
813
780
|
const block = ciphertext.slice(i, i + 16);
|
|
@@ -822,7 +789,7 @@ var SharedSecret = class _SharedSecret {
|
|
|
822
789
|
}
|
|
823
790
|
encrypt(data) {
|
|
824
791
|
const key = this.secret.slice(0, 16);
|
|
825
|
-
const cipher = ecb(key, { disablePadding: true });
|
|
792
|
+
const cipher = (0, import_aes.ecb)(key, { disablePadding: true });
|
|
826
793
|
const fullBlocks = Math.floor(data.length / 16);
|
|
827
794
|
const remaining = data.length % 16;
|
|
828
795
|
const ciphertext = new Uint8Array((fullBlocks + (remaining > 0 ? 1 : 0)) * 16);
|
|
@@ -841,7 +808,7 @@ var SharedSecret = class _SharedSecret {
|
|
|
841
808
|
return { hmac: hmac2, ciphertext };
|
|
842
809
|
}
|
|
843
810
|
calculateHmac(data) {
|
|
844
|
-
return hmac(
|
|
811
|
+
return (0, import_hmac.hmac)(import_sha22.sha256, this.secret, data).slice(0, HMAC_SIZE);
|
|
845
812
|
}
|
|
846
813
|
static fromName(name) {
|
|
847
814
|
if (name === "Public") {
|
|
@@ -849,14 +816,14 @@ var SharedSecret = class _SharedSecret {
|
|
|
849
816
|
} else if (!/^#/.test(name)) {
|
|
850
817
|
throw new Error("Only the 'Public' group or groups starting with '#' are supported");
|
|
851
818
|
}
|
|
852
|
-
const hash =
|
|
819
|
+
const hash = import_sha22.sha256.create().update(new TextEncoder().encode(name)).digest();
|
|
853
820
|
return new _SharedSecret(hash.slice(0, SHARED_SECRET_SIZE));
|
|
854
821
|
}
|
|
855
822
|
};
|
|
856
823
|
var StaticSecret = class {
|
|
857
824
|
constructor(secret) {
|
|
858
825
|
if (typeof secret === "string") {
|
|
859
|
-
secret =
|
|
826
|
+
secret = (0, import_packet3.hexToBytes)(secret);
|
|
860
827
|
}
|
|
861
828
|
if (secret.length !== STATIC_SECRET_SIZE) {
|
|
862
829
|
throw new Error(`Invalid static secret length: expected ${STATIC_SECRET_SIZE} bytes, got ${secret.length}`);
|
|
@@ -864,16 +831,17 @@ var StaticSecret = class {
|
|
|
864
831
|
this.secret = secret;
|
|
865
832
|
}
|
|
866
833
|
publicKey() {
|
|
867
|
-
const publicKey = x25519.getPublicKey(this.secret);
|
|
834
|
+
const publicKey = import_ed25519.x25519.getPublicKey(this.secret);
|
|
868
835
|
return new PublicKey(publicKey);
|
|
869
836
|
}
|
|
870
837
|
diffieHellman(otherPublicKey) {
|
|
871
|
-
const sharedSecret = x25519.getSharedSecret(this.secret, otherPublicKey.toBytes());
|
|
872
|
-
return new SharedSecret(sharedSecret);
|
|
838
|
+
const sharedSecret = import_ed25519.x25519.getSharedSecret(this.secret, otherPublicKey.toBytes());
|
|
839
|
+
return new SharedSecret(sharedSecret.slice(0, 16));
|
|
873
840
|
}
|
|
874
841
|
};
|
|
875
842
|
|
|
876
843
|
// src/identity.ts
|
|
844
|
+
var import_packet4 = require("@hamradio/packet");
|
|
877
845
|
var parseNodeHash = (hash) => {
|
|
878
846
|
if (hash instanceof Uint8Array) {
|
|
879
847
|
return hash[0];
|
|
@@ -884,7 +852,7 @@ var parseNodeHash = (hash) => {
|
|
|
884
852
|
}
|
|
885
853
|
return hash;
|
|
886
854
|
} else if (typeof hash === "string") {
|
|
887
|
-
const parsed = hexToBytes(hash);
|
|
855
|
+
const parsed = (0, import_packet4.hexToBytes)(hash);
|
|
888
856
|
if (parsed.length !== 1) {
|
|
889
857
|
throw new Error("NodeHash string must represent a single byte");
|
|
890
858
|
}
|
|
@@ -900,7 +868,7 @@ var toPublicKeyBytes = (key) => {
|
|
|
900
868
|
} else if (key instanceof Uint8Array) {
|
|
901
869
|
return key;
|
|
902
870
|
} else if (typeof key === "string") {
|
|
903
|
-
return hexToBytes(key);
|
|
871
|
+
return (0, import_packet4.hexToBytes)(key);
|
|
904
872
|
} else {
|
|
905
873
|
throw new Error("Invalid type for toPublicKeyBytes");
|
|
906
874
|
}
|
|
@@ -959,7 +927,7 @@ var LocalIdentity = class extends Identity {
|
|
|
959
927
|
} else {
|
|
960
928
|
throw new Error("Invalid type for calculateSharedSecret comparison");
|
|
961
929
|
}
|
|
962
|
-
return new SharedSecret(this.privateKey.calculateSharedSecret(otherPublicKey));
|
|
930
|
+
return new SharedSecret(this.privateKey.calculateSharedSecret(otherPublicKey).slice(0, 16));
|
|
963
931
|
}
|
|
964
932
|
};
|
|
965
933
|
var Contact = class {
|
|
@@ -1003,12 +971,12 @@ var Group = class {
|
|
|
1003
971
|
if (data.length < 5) {
|
|
1004
972
|
throw new Error("Invalid ciphertext");
|
|
1005
973
|
}
|
|
1006
|
-
const reader = new
|
|
1007
|
-
const timestamp = reader.
|
|
1008
|
-
const flags = reader.
|
|
974
|
+
const reader = new import_packet4.Reader(data);
|
|
975
|
+
const timestamp = reader.date32();
|
|
976
|
+
const flags = reader.uint8();
|
|
1009
977
|
const textType = flags >> 2 & 63;
|
|
1010
978
|
const attempt = flags & 3;
|
|
1011
|
-
const message = new TextDecoder("utf-8").decode(reader.
|
|
979
|
+
const message = new TextDecoder("utf-8").decode(reader.bytes());
|
|
1012
980
|
return {
|
|
1013
981
|
timestamp,
|
|
1014
982
|
textType,
|
|
@@ -1017,11 +985,11 @@ var Group = class {
|
|
|
1017
985
|
};
|
|
1018
986
|
}
|
|
1019
987
|
encryptText(plain) {
|
|
1020
|
-
const writer = new
|
|
1021
|
-
writer.
|
|
988
|
+
const writer = new import_packet4.Writer(4 + 1 + new TextEncoder().encode(plain.message).length);
|
|
989
|
+
writer.date32(plain.timestamp);
|
|
1022
990
|
const flags = (plain.textType & 63) << 2 | plain.attempt & 3;
|
|
1023
|
-
writer.
|
|
1024
|
-
writer.
|
|
991
|
+
writer.uint8(flags);
|
|
992
|
+
writer.utf8String(plain.message);
|
|
1025
993
|
const data = writer.toBytes();
|
|
1026
994
|
return this.secret.encrypt(data);
|
|
1027
995
|
}
|
|
@@ -1030,16 +998,16 @@ var Group = class {
|
|
|
1030
998
|
if (data.length < 4) {
|
|
1031
999
|
throw new Error("Invalid ciphertext");
|
|
1032
1000
|
}
|
|
1033
|
-
const reader = new
|
|
1001
|
+
const reader = new import_packet4.Reader(data);
|
|
1034
1002
|
return {
|
|
1035
|
-
timestamp: reader.
|
|
1036
|
-
data: reader.
|
|
1003
|
+
timestamp: reader.date32(),
|
|
1004
|
+
data: reader.bytes()
|
|
1037
1005
|
};
|
|
1038
1006
|
}
|
|
1039
1007
|
encryptData(plain) {
|
|
1040
|
-
const writer = new
|
|
1041
|
-
writer.
|
|
1042
|
-
writer.
|
|
1008
|
+
const writer = new import_packet4.Writer(4 + plain.data.length);
|
|
1009
|
+
writer.date32(plain.timestamp);
|
|
1010
|
+
writer.bytes(plain.data);
|
|
1043
1011
|
const data = writer.toBytes();
|
|
1044
1012
|
return this.secret.encrypt(data);
|
|
1045
1013
|
}
|
|
@@ -1049,6 +1017,8 @@ var Contacts = class {
|
|
|
1049
1017
|
this.localIdentities = [];
|
|
1050
1018
|
this.contacts = {};
|
|
1051
1019
|
this.groups = {};
|
|
1020
|
+
this.addGroup(new Group("Public"));
|
|
1021
|
+
this.addGroup(new Group("#test"));
|
|
1052
1022
|
}
|
|
1053
1023
|
addLocalIdentity(identity) {
|
|
1054
1024
|
this.localIdentities.push({ identity, sharedSecrets: {} });
|
|
@@ -1060,6 +1030,23 @@ var Contacts = class {
|
|
|
1060
1030
|
}
|
|
1061
1031
|
this.contacts[hash].push(contact);
|
|
1062
1032
|
}
|
|
1033
|
+
hasContact(nameOrHash) {
|
|
1034
|
+
if (typeof nameOrHash === "string") {
|
|
1035
|
+
return Object.values(this.contacts).flat().some((contact) => contact.name.toLowerCase() === nameOrHash.toLowerCase());
|
|
1036
|
+
} else {
|
|
1037
|
+
const hash = parseNodeHash(nameOrHash);
|
|
1038
|
+
return (this.contacts[hash] || []).length > 0;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
getContactByName(name) {
|
|
1042
|
+
const contact = Object.values(this.contacts).flat().find((contact2) => contact2.name.toLowerCase() === name.toLowerCase());
|
|
1043
|
+
return contact || null;
|
|
1044
|
+
}
|
|
1045
|
+
getContacts() {
|
|
1046
|
+
const contacts = Object.values(this.contacts).flat();
|
|
1047
|
+
contacts.sort((a, b) => a.name.localeCompare(b.name));
|
|
1048
|
+
return contacts;
|
|
1049
|
+
}
|
|
1063
1050
|
decrypt(src, dst, hmac2, ciphertext) {
|
|
1064
1051
|
let contacts = [];
|
|
1065
1052
|
if (src instanceof PublicKey) {
|
|
@@ -1107,17 +1094,45 @@ var Contacts = class {
|
|
|
1107
1094
|
return sharedSecret;
|
|
1108
1095
|
}
|
|
1109
1096
|
addGroup(group) {
|
|
1110
|
-
const
|
|
1097
|
+
for (const key of Object.keys(this.groups)) {
|
|
1098
|
+
const hash2 = Number(key);
|
|
1099
|
+
this.groups[hash2] = this.groups[hash2].filter((g) => g.name.toLowerCase() !== group.name.toLowerCase());
|
|
1100
|
+
if (this.groups[hash2].length === 0) {
|
|
1101
|
+
delete this.groups[hash2];
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
const hash = group.hash();
|
|
1111
1105
|
if (!this.groups[hash]) {
|
|
1112
1106
|
this.groups[hash] = [];
|
|
1113
1107
|
}
|
|
1114
1108
|
this.groups[hash].push(group);
|
|
1115
1109
|
}
|
|
1110
|
+
hasGroup(nameOrHash) {
|
|
1111
|
+
if (typeof nameOrHash === "string") {
|
|
1112
|
+
return Object.values(this.groups).flat().some((group) => group.name.toLowerCase() === nameOrHash.toLowerCase());
|
|
1113
|
+
} else {
|
|
1114
|
+
const hash = parseNodeHash(nameOrHash);
|
|
1115
|
+
return (this.groups[hash] || []).length > 0;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
getGroupByName(name) {
|
|
1119
|
+
const group = Object.values(this.groups).flat().find((group2) => group2.name.toLowerCase() === name.toLowerCase());
|
|
1120
|
+
return group || null;
|
|
1121
|
+
}
|
|
1122
|
+
getGroups() {
|
|
1123
|
+
const groups = Object.values(this.groups).flat();
|
|
1124
|
+
groups.sort((a, b) => {
|
|
1125
|
+
if (a.name === "Public") return -1;
|
|
1126
|
+
if (b.name === "Public") return 1;
|
|
1127
|
+
return a.name.localeCompare(b.name);
|
|
1128
|
+
});
|
|
1129
|
+
return groups;
|
|
1130
|
+
}
|
|
1116
1131
|
decryptGroupText(channelHash, hmac2, ciphertext) {
|
|
1117
1132
|
const hash = parseNodeHash(channelHash);
|
|
1118
1133
|
const groups = this.groups[hash] || [];
|
|
1119
1134
|
if (groups.length === 0) {
|
|
1120
|
-
throw new Error(
|
|
1135
|
+
throw new Error(`Unknown group hash ${hash.toString(16).padStart(2, "0")}`);
|
|
1121
1136
|
}
|
|
1122
1137
|
for (const group of groups) {
|
|
1123
1138
|
try {
|
|
@@ -1126,13 +1141,13 @@ var Contacts = class {
|
|
|
1126
1141
|
} catch {
|
|
1127
1142
|
}
|
|
1128
1143
|
}
|
|
1129
|
-
throw new Error(
|
|
1144
|
+
throw new Error(`Decryption failed with all known groups with hash ${hash.toString(16).padStart(2, "0")}`);
|
|
1130
1145
|
}
|
|
1131
1146
|
decryptGroupData(channelHash, hmac2, ciphertext) {
|
|
1132
1147
|
const hash = parseNodeHash(channelHash);
|
|
1133
1148
|
const groups = this.groups[hash] || [];
|
|
1134
1149
|
if (groups.length === 0) {
|
|
1135
|
-
throw new Error(
|
|
1150
|
+
throw new Error(`Unknown group hash ${hash.toString(16).padStart(2, "0")}`);
|
|
1136
1151
|
}
|
|
1137
1152
|
for (const group of groups) {
|
|
1138
1153
|
try {
|
|
@@ -1141,10 +1156,11 @@ var Contacts = class {
|
|
|
1141
1156
|
} catch {
|
|
1142
1157
|
}
|
|
1143
1158
|
}
|
|
1144
|
-
throw new Error(
|
|
1159
|
+
throw new Error(`Decryption failed with all known groups with hash ${hash.toString(16).padStart(2, "0")}`);
|
|
1145
1160
|
}
|
|
1146
1161
|
};
|
|
1147
|
-
export
|
|
1162
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1163
|
+
0 && (module.exports = {
|
|
1148
1164
|
Contact,
|
|
1149
1165
|
Contacts,
|
|
1150
1166
|
Group,
|
|
@@ -1161,4 +1177,4 @@ export {
|
|
|
1161
1177
|
StaticSecret,
|
|
1162
1178
|
TextType,
|
|
1163
1179
|
parseNodeHash
|
|
1164
|
-
};
|
|
1180
|
+
});
|