@hamradio/meshcore 1.2.1 → 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} +365 -328
- package/dist/{index.d.mts → index.d.cts} +118 -132
- package/dist/index.d.ts +118 -132
- package/dist/index.js +322 -369
- 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,60 +147,83 @@ 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}`);
|
|
238
160
|
}
|
|
239
161
|
this.structure = [
|
|
240
162
|
/* Header segment */
|
|
241
|
-
{
|
|
242
|
-
|
|
163
|
+
{
|
|
164
|
+
name: "header",
|
|
165
|
+
data: new Uint8Array([this.header]).buffer,
|
|
166
|
+
fields: [
|
|
167
|
+
/* Header flags */
|
|
168
|
+
{
|
|
169
|
+
name: "flags",
|
|
170
|
+
type: import_packet2.FieldType.BITS,
|
|
171
|
+
length: 1,
|
|
172
|
+
bits: [
|
|
173
|
+
{ name: "payload version", size: 2 },
|
|
174
|
+
{ name: "payload type", size: 4 },
|
|
175
|
+
{ name: "route type", size: 2 }
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
/* Transport codes */
|
|
181
|
+
..._Packet.hasTransportCodes(this.routeType) ? [
|
|
243
182
|
{
|
|
244
|
-
name: "
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
+
}
|
|
251
203
|
]
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
204
|
+
}
|
|
205
|
+
] : [],
|
|
206
|
+
/* Path length and hashes */
|
|
207
|
+
{
|
|
208
|
+
name: "path",
|
|
209
|
+
data: new Uint8Array([this.pathLength, ...this.path]).buffer,
|
|
210
|
+
fields: [
|
|
255
211
|
{
|
|
256
|
-
name: "
|
|
257
|
-
type:
|
|
258
|
-
|
|
212
|
+
name: "path length",
|
|
213
|
+
type: import_packet2.FieldType.UINT8,
|
|
214
|
+
length: 1,
|
|
215
|
+
bits: [
|
|
216
|
+
{ name: "path hash size", size: 2 },
|
|
217
|
+
{ name: "path hash count", size: 6 }
|
|
218
|
+
]
|
|
259
219
|
},
|
|
260
220
|
{
|
|
261
|
-
name: "
|
|
262
|
-
type:
|
|
263
|
-
|
|
221
|
+
name: "path hashes",
|
|
222
|
+
type: pathHashType,
|
|
223
|
+
length: this.path.length
|
|
264
224
|
}
|
|
265
|
-
]
|
|
266
|
-
|
|
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
|
-
] }
|
|
225
|
+
]
|
|
226
|
+
}
|
|
282
227
|
];
|
|
283
228
|
}
|
|
284
229
|
decode(withStructure) {
|
|
@@ -320,7 +265,6 @@ var Packet = class _Packet {
|
|
|
320
265
|
default:
|
|
321
266
|
throw new Error(`Unsupported payload type: ${this.payloadType}`);
|
|
322
267
|
}
|
|
323
|
-
console.log("packet decode with structure:", typeof withStructure, withStructure, { result });
|
|
324
268
|
if (typeof withStructure === "boolean" && withStructure && "segment" in result && "payload" in result) {
|
|
325
269
|
this.ensureStructure();
|
|
326
270
|
const structure = [...this.structure, result.segment];
|
|
@@ -329,17 +273,17 @@ var Packet = class _Packet {
|
|
|
329
273
|
return result;
|
|
330
274
|
}
|
|
331
275
|
decodeEncryptedPayload(reader) {
|
|
332
|
-
const cipherMAC = reader.
|
|
333
|
-
const cipherText = reader.
|
|
276
|
+
const cipherMAC = reader.bytes(2);
|
|
277
|
+
const cipherText = reader.bytes();
|
|
334
278
|
return { cipherMAC, cipherText };
|
|
335
279
|
}
|
|
336
280
|
decodeRequest(withSegment) {
|
|
337
281
|
if (this.payload.length < 4) {
|
|
338
282
|
throw new Error("Invalid request payload: too short");
|
|
339
283
|
}
|
|
340
|
-
const reader =
|
|
341
|
-
const dst = reader.
|
|
342
|
-
const src = reader.
|
|
284
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
285
|
+
const dst = reader.uint8();
|
|
286
|
+
const src = reader.uint8();
|
|
343
287
|
const encrypted = this.decodeEncryptedPayload(reader);
|
|
344
288
|
const payload = {
|
|
345
289
|
type: 0 /* REQUEST */,
|
|
@@ -350,12 +294,17 @@ var Packet = class _Packet {
|
|
|
350
294
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
351
295
|
const segment = {
|
|
352
296
|
name: "request payload",
|
|
353
|
-
data: this.payload,
|
|
297
|
+
data: new Uint8Array(this.payload).buffer,
|
|
354
298
|
fields: [
|
|
355
|
-
{ name: "destination hash", type:
|
|
356
|
-
{ name: "source hash", type:
|
|
357
|
-
{ name: "cipher MAC", type:
|
|
358
|
-
{
|
|
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
|
+
}
|
|
359
308
|
]
|
|
360
309
|
};
|
|
361
310
|
return { payload, segment };
|
|
@@ -366,9 +315,9 @@ var Packet = class _Packet {
|
|
|
366
315
|
if (this.payload.length < 4) {
|
|
367
316
|
throw new Error("Invalid response payload: too short");
|
|
368
317
|
}
|
|
369
|
-
const reader =
|
|
370
|
-
const dst = reader.
|
|
371
|
-
const src = reader.
|
|
318
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
319
|
+
const dst = reader.uint8();
|
|
320
|
+
const src = reader.uint8();
|
|
372
321
|
const encrypted = this.decodeEncryptedPayload(reader);
|
|
373
322
|
const payload = {
|
|
374
323
|
type: 1 /* RESPONSE */,
|
|
@@ -379,12 +328,17 @@ var Packet = class _Packet {
|
|
|
379
328
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
380
329
|
const segment = {
|
|
381
330
|
name: "response payload",
|
|
382
|
-
data: this.payload,
|
|
331
|
+
data: new Uint8Array(this.payload).buffer,
|
|
383
332
|
fields: [
|
|
384
|
-
{ name: "destination hash", type:
|
|
385
|
-
{ name: "source hash", type:
|
|
386
|
-
{ name: "cipher MAC", type:
|
|
387
|
-
{
|
|
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
|
+
}
|
|
388
342
|
]
|
|
389
343
|
};
|
|
390
344
|
return { payload, segment };
|
|
@@ -395,9 +349,9 @@ var Packet = class _Packet {
|
|
|
395
349
|
if (this.payload.length < 4) {
|
|
396
350
|
throw new Error("Invalid text payload: too short");
|
|
397
351
|
}
|
|
398
|
-
const reader =
|
|
399
|
-
const dst = reader.
|
|
400
|
-
const src = reader.
|
|
352
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
353
|
+
const dst = reader.uint8();
|
|
354
|
+
const src = reader.uint8();
|
|
401
355
|
const encrypted = this.decodeEncryptedPayload(reader);
|
|
402
356
|
const payload = {
|
|
403
357
|
type: 2 /* TEXT */,
|
|
@@ -408,12 +362,17 @@ var Packet = class _Packet {
|
|
|
408
362
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
409
363
|
const segment = {
|
|
410
364
|
name: "text payload",
|
|
411
|
-
data: this.payload,
|
|
365
|
+
data: new Uint8Array(this.payload).buffer,
|
|
412
366
|
fields: [
|
|
413
|
-
{ name: "destination hash", type:
|
|
414
|
-
{ name: "source hash", type:
|
|
415
|
-
{ name: "cipher MAC", type:
|
|
416
|
-
{
|
|
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
|
+
}
|
|
417
376
|
]
|
|
418
377
|
};
|
|
419
378
|
return { payload, segment };
|
|
@@ -424,8 +383,8 @@ var Packet = class _Packet {
|
|
|
424
383
|
if (this.payload.length < 4) {
|
|
425
384
|
throw new Error("Invalid ack payload: too short");
|
|
426
385
|
}
|
|
427
|
-
const reader =
|
|
428
|
-
const checksum = reader.
|
|
386
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
387
|
+
const checksum = reader.bytes(4);
|
|
429
388
|
const payload = {
|
|
430
389
|
type: 3 /* ACK */,
|
|
431
390
|
checksum
|
|
@@ -433,10 +392,8 @@ var Packet = class _Packet {
|
|
|
433
392
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
434
393
|
const segment = {
|
|
435
394
|
name: "ack payload",
|
|
436
|
-
data: this.payload,
|
|
437
|
-
fields: [
|
|
438
|
-
{ name: "checksum", type: 6 /* BYTES */, size: 4, value: checksum }
|
|
439
|
-
]
|
|
395
|
+
data: new Uint8Array(this.payload).buffer,
|
|
396
|
+
fields: [{ name: "checksum", type: import_packet2.FieldType.BYTES, length: 4, value: checksum }]
|
|
440
397
|
};
|
|
441
398
|
return { payload, segment };
|
|
442
399
|
}
|
|
@@ -446,26 +403,26 @@ var Packet = class _Packet {
|
|
|
446
403
|
if (this.payload.length < 4) {
|
|
447
404
|
throw new Error("Invalid advert payload: too short");
|
|
448
405
|
}
|
|
449
|
-
const reader =
|
|
406
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
450
407
|
const payload = {
|
|
451
408
|
type: 4 /* ADVERT */,
|
|
452
|
-
publicKey: reader.
|
|
453
|
-
timestamp: reader.
|
|
454
|
-
signature: reader.
|
|
409
|
+
publicKey: reader.bytes(32),
|
|
410
|
+
timestamp: reader.date32(),
|
|
411
|
+
signature: reader.bytes(64)
|
|
455
412
|
};
|
|
456
413
|
let segment;
|
|
457
414
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
458
415
|
segment = {
|
|
459
416
|
name: "advert payload",
|
|
460
|
-
data: this.payload,
|
|
417
|
+
data: new Uint8Array(this.payload).buffer,
|
|
461
418
|
fields: [
|
|
462
|
-
{ type:
|
|
463
|
-
{ type:
|
|
464
|
-
{ 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 }
|
|
465
422
|
]
|
|
466
423
|
};
|
|
467
424
|
}
|
|
468
|
-
const flags = reader.
|
|
425
|
+
const flags = reader.uint8();
|
|
469
426
|
const appdata = {
|
|
470
427
|
nodeType: flags & 15,
|
|
471
428
|
hasLocation: (flags & 16 /* HAS_LOCATION */) !== 0,
|
|
@@ -474,44 +431,50 @@ var Packet = class _Packet {
|
|
|
474
431
|
hasName: (flags & 128 /* HAS_NAME */) !== 0
|
|
475
432
|
};
|
|
476
433
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
477
|
-
segment.fields.push({
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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
|
+
});
|
|
484
447
|
}
|
|
485
448
|
if (appdata.hasLocation) {
|
|
486
|
-
const lat = reader.
|
|
487
|
-
const lon = reader.
|
|
449
|
+
const lat = reader.int32() / 1e6;
|
|
450
|
+
const lon = reader.int32() / 1e6;
|
|
488
451
|
appdata.location = [lat, lon];
|
|
489
452
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
490
|
-
segment.fields.push({ type:
|
|
491
|
-
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 });
|
|
492
455
|
}
|
|
493
456
|
}
|
|
494
457
|
if (appdata.hasFeature1) {
|
|
495
|
-
appdata.feature1 = reader.
|
|
458
|
+
appdata.feature1 = reader.uint16();
|
|
496
459
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
497
|
-
segment.fields.push({ type:
|
|
460
|
+
segment.fields.push({ type: import_packet2.FieldType.UINT16_LE, name: "feature1", length: 2, value: appdata.feature1 });
|
|
498
461
|
}
|
|
499
462
|
}
|
|
500
463
|
if (appdata.hasFeature2) {
|
|
501
|
-
appdata.feature2 = reader.
|
|
464
|
+
appdata.feature2 = reader.uint16();
|
|
502
465
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
503
|
-
segment.fields.push({ type:
|
|
466
|
+
segment.fields.push({ type: import_packet2.FieldType.UINT16_LE, name: "feature2", length: 2, value: appdata.feature2 });
|
|
504
467
|
}
|
|
505
468
|
}
|
|
506
469
|
if (appdata.hasName) {
|
|
507
|
-
|
|
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));
|
|
470
|
+
appdata.name = reader.cString();
|
|
513
471
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
514
|
-
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
|
+
});
|
|
515
478
|
}
|
|
516
479
|
}
|
|
517
480
|
if (typeof withSegment === "boolean" && withSegment && typeof segment !== "undefined") {
|
|
@@ -523,8 +486,8 @@ var Packet = class _Packet {
|
|
|
523
486
|
if (this.payload.length < 3) {
|
|
524
487
|
throw new Error("Invalid group text payload: too short");
|
|
525
488
|
}
|
|
526
|
-
const reader =
|
|
527
|
-
const channelHash = reader.
|
|
489
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
490
|
+
const channelHash = reader.uint8();
|
|
528
491
|
const encrypted = this.decodeEncryptedPayload(reader);
|
|
529
492
|
const payload = {
|
|
530
493
|
type: 5 /* GROUP_TEXT */,
|
|
@@ -534,11 +497,16 @@ var Packet = class _Packet {
|
|
|
534
497
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
535
498
|
const segment = {
|
|
536
499
|
name: "group text payload",
|
|
537
|
-
data: this.payload,
|
|
500
|
+
data: new Uint8Array(this.payload).buffer,
|
|
538
501
|
fields: [
|
|
539
|
-
{ name: "channel hash", type:
|
|
540
|
-
{ name: "cipher MAC", type:
|
|
541
|
-
{
|
|
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
|
+
}
|
|
542
510
|
]
|
|
543
511
|
};
|
|
544
512
|
return { payload, segment };
|
|
@@ -549,20 +517,25 @@ var Packet = class _Packet {
|
|
|
549
517
|
if (this.payload.length < 3) {
|
|
550
518
|
throw new Error("Invalid group data payload: too short");
|
|
551
519
|
}
|
|
552
|
-
const reader =
|
|
520
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
553
521
|
const payload = {
|
|
554
522
|
type: 6 /* GROUP_DATA */,
|
|
555
|
-
channelHash: reader.
|
|
523
|
+
channelHash: reader.uint8(),
|
|
556
524
|
encrypted: this.decodeEncryptedPayload(reader)
|
|
557
525
|
};
|
|
558
526
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
559
527
|
const segment = {
|
|
560
528
|
name: "group data payload",
|
|
561
|
-
data: this.payload,
|
|
529
|
+
data: new Uint8Array(this.payload).buffer,
|
|
562
530
|
fields: [
|
|
563
|
-
{ name: "channel hash", type:
|
|
564
|
-
{ name: "cipher MAC", type:
|
|
565
|
-
{
|
|
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
|
+
}
|
|
566
539
|
]
|
|
567
540
|
};
|
|
568
541
|
return { payload, segment };
|
|
@@ -573,22 +546,27 @@ var Packet = class _Packet {
|
|
|
573
546
|
if (this.payload.length < 1 + 32 + 2) {
|
|
574
547
|
throw new Error("Invalid anon req payload: too short");
|
|
575
548
|
}
|
|
576
|
-
const reader =
|
|
549
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
577
550
|
const payload = {
|
|
578
551
|
type: 7 /* ANON_REQ */,
|
|
579
|
-
dst: reader.
|
|
580
|
-
publicKey: reader.
|
|
552
|
+
dst: reader.uint8(),
|
|
553
|
+
publicKey: reader.bytes(32),
|
|
581
554
|
encrypted: this.decodeEncryptedPayload(reader)
|
|
582
555
|
};
|
|
583
556
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
584
557
|
const segment = {
|
|
585
558
|
name: "anon req payload",
|
|
586
|
-
data: this.payload,
|
|
559
|
+
data: new Uint8Array(this.payload).buffer,
|
|
587
560
|
fields: [
|
|
588
|
-
{ name: "destination hash", type:
|
|
589
|
-
{ name: "public key", type:
|
|
590
|
-
{ name: "cipher MAC", type:
|
|
591
|
-
{
|
|
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
|
+
}
|
|
592
570
|
]
|
|
593
571
|
};
|
|
594
572
|
return { payload, segment };
|
|
@@ -599,19 +577,19 @@ var Packet = class _Packet {
|
|
|
599
577
|
if (this.payload.length < 2) {
|
|
600
578
|
throw new Error("Invalid path payload: too short");
|
|
601
579
|
}
|
|
602
|
-
const reader =
|
|
580
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
603
581
|
const payload = {
|
|
604
582
|
type: 8 /* PATH */,
|
|
605
|
-
dst: reader.
|
|
606
|
-
src: reader.
|
|
583
|
+
dst: reader.uint8(),
|
|
584
|
+
src: reader.uint8()
|
|
607
585
|
};
|
|
608
586
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
609
587
|
const segment = {
|
|
610
588
|
name: "path payload",
|
|
611
|
-
data: this.payload,
|
|
589
|
+
data: new Uint8Array(this.payload).buffer,
|
|
612
590
|
fields: [
|
|
613
|
-
{ name: "destination hash", type:
|
|
614
|
-
{ 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 }
|
|
615
593
|
]
|
|
616
594
|
};
|
|
617
595
|
return { payload, segment };
|
|
@@ -622,23 +600,23 @@ var Packet = class _Packet {
|
|
|
622
600
|
if (this.payload.length < 9) {
|
|
623
601
|
throw new Error("Invalid trace payload: too short");
|
|
624
602
|
}
|
|
625
|
-
const reader =
|
|
603
|
+
const reader = import_packet2.Reader.fromBytes(this.payload);
|
|
626
604
|
const payload = {
|
|
627
605
|
type: 9 /* TRACE */,
|
|
628
|
-
tag: reader.
|
|
629
|
-
authCode: reader.
|
|
630
|
-
flags: reader.
|
|
631
|
-
nodes: reader.
|
|
606
|
+
tag: reader.uint32(),
|
|
607
|
+
authCode: reader.uint32(),
|
|
608
|
+
flags: reader.uint8() & 3,
|
|
609
|
+
nodes: reader.bytes()
|
|
632
610
|
};
|
|
633
611
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
634
612
|
const segment = {
|
|
635
613
|
name: "trace payload",
|
|
636
|
-
data: this.payload,
|
|
614
|
+
data: new Uint8Array(this.payload).buffer,
|
|
637
615
|
fields: [
|
|
638
|
-
{ name: "tag", type:
|
|
639
|
-
{ name: "auth code", type:
|
|
640
|
-
{ name: "flags", type:
|
|
641
|
-
{ 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 }
|
|
642
620
|
]
|
|
643
621
|
};
|
|
644
622
|
return { payload, segment };
|
|
@@ -653,10 +631,8 @@ var Packet = class _Packet {
|
|
|
653
631
|
if (typeof withSegment === "boolean" && withSegment) {
|
|
654
632
|
const segment = {
|
|
655
633
|
name: "raw custom payload",
|
|
656
|
-
data: this.payload,
|
|
657
|
-
fields: [
|
|
658
|
-
{ name: "data", type: 6 /* BYTES */, size: this.payload.length, value: this.payload }
|
|
659
|
-
]
|
|
634
|
+
data: new Uint8Array(this.payload).buffer,
|
|
635
|
+
fields: [{ name: "data", type: import_packet2.FieldType.BYTES, length: this.payload.length, value: this.payload }]
|
|
660
636
|
};
|
|
661
637
|
return { payload, segment };
|
|
662
638
|
}
|
|
@@ -665,35 +641,39 @@ var Packet = class _Packet {
|
|
|
665
641
|
};
|
|
666
642
|
|
|
667
643
|
// src/crypto.ts
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
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");
|
|
672
649
|
var PUBLIC_KEY_SIZE = 32;
|
|
673
650
|
var SEED_SIZE = 32;
|
|
674
651
|
var HMAC_SIZE = 2;
|
|
675
|
-
var SHARED_SECRET_SIZE =
|
|
652
|
+
var SHARED_SECRET_SIZE = 16;
|
|
676
653
|
var SIGNATURE_SIZE = 64;
|
|
677
654
|
var STATIC_SECRET_SIZE = 32;
|
|
678
|
-
var publicSecret = hexToBytes("8b3387e9c5cdea6ac9e5edbaa115cd72"
|
|
655
|
+
var publicSecret = (0, import_packet3.hexToBytes)("8b3387e9c5cdea6ac9e5edbaa115cd72");
|
|
679
656
|
var PublicKey = class _PublicKey {
|
|
680
657
|
constructor(key) {
|
|
681
658
|
if (typeof key === "string") {
|
|
682
|
-
this.key =
|
|
659
|
+
this.key = (0, import_packet3.hexToBytes)(key);
|
|
683
660
|
} else if (key instanceof Uint8Array) {
|
|
684
661
|
this.key = key;
|
|
685
662
|
} else {
|
|
686
663
|
throw new Error("Invalid type for PublicKey constructor");
|
|
687
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
|
+
}
|
|
688
668
|
}
|
|
689
669
|
toHash() {
|
|
690
|
-
return
|
|
670
|
+
return import_sha22.sha256.create().update(this.key).digest()[0];
|
|
691
671
|
}
|
|
692
672
|
toBytes() {
|
|
693
673
|
return this.key;
|
|
694
674
|
}
|
|
695
675
|
toString() {
|
|
696
|
-
return bytesToHex(this.key);
|
|
676
|
+
return (0, import_packet3.bytesToHex)(this.key);
|
|
697
677
|
}
|
|
698
678
|
equals(other) {
|
|
699
679
|
let otherKey;
|
|
@@ -702,28 +682,39 @@ var PublicKey = class _PublicKey {
|
|
|
702
682
|
} else if (other instanceof Uint8Array) {
|
|
703
683
|
otherKey = other;
|
|
704
684
|
} else if (typeof other === "string") {
|
|
705
|
-
otherKey =
|
|
685
|
+
otherKey = (0, import_packet3.hexToBytes)(other);
|
|
706
686
|
} else {
|
|
707
687
|
throw new Error("Invalid type for PublicKey comparison");
|
|
708
688
|
}
|
|
709
|
-
|
|
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);
|
|
710
695
|
}
|
|
711
696
|
verify(message, signature) {
|
|
712
697
|
if (signature.length !== SIGNATURE_SIZE) {
|
|
713
698
|
throw new Error(`Invalid signature length: expected ${SIGNATURE_SIZE} bytes, got ${signature.length}`);
|
|
714
699
|
}
|
|
715
|
-
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);
|
|
716
707
|
}
|
|
717
708
|
};
|
|
718
709
|
var PrivateKey = class _PrivateKey {
|
|
719
710
|
constructor(seed) {
|
|
720
711
|
if (typeof seed === "string") {
|
|
721
|
-
seed =
|
|
712
|
+
seed = (0, import_packet3.hexToBytes)(seed);
|
|
722
713
|
}
|
|
723
714
|
if (seed.length !== SEED_SIZE) {
|
|
724
715
|
throw new Error(`Invalid seed length: expected ${SEED_SIZE} bytes, got ${seed.length}`);
|
|
725
716
|
}
|
|
726
|
-
const { secretKey, publicKey } = ed25519.keygen(seed);
|
|
717
|
+
const { secretKey, publicKey } = import_ed25519.ed25519.keygen(seed);
|
|
727
718
|
this.secretKey = secretKey;
|
|
728
719
|
this.publicKey = new PublicKey(publicKey);
|
|
729
720
|
}
|
|
@@ -734,10 +725,10 @@ var PrivateKey = class _PrivateKey {
|
|
|
734
725
|
return this.secretKey;
|
|
735
726
|
}
|
|
736
727
|
toString() {
|
|
737
|
-
return bytesToHex(this.secretKey);
|
|
728
|
+
return (0, import_packet3.bytesToHex)(this.secretKey);
|
|
738
729
|
}
|
|
739
730
|
sign(message) {
|
|
740
|
-
return ed25519.sign(message, this.secretKey);
|
|
731
|
+
return import_ed25519.ed25519.sign(message, this.secretKey);
|
|
741
732
|
}
|
|
742
733
|
calculateSharedSecret(other) {
|
|
743
734
|
let otherPublicKey;
|
|
@@ -750,43 +741,40 @@ var PrivateKey = class _PrivateKey {
|
|
|
750
741
|
} else {
|
|
751
742
|
throw new Error("Invalid type for calculateSharedSecret comparison");
|
|
752
743
|
}
|
|
753
|
-
return x25519.getSharedSecret(this.secretKey, otherPublicKey.toBytes());
|
|
744
|
+
return import_ed25519.x25519.getSharedSecret(this.secretKey, otherPublicKey.toBytes());
|
|
754
745
|
}
|
|
755
746
|
static generate() {
|
|
756
|
-
const { secretKey } = ed25519.keygen();
|
|
747
|
+
const { secretKey } = import_ed25519.ed25519.keygen();
|
|
757
748
|
return new _PrivateKey(secretKey);
|
|
758
749
|
}
|
|
759
750
|
};
|
|
760
751
|
var SharedSecret = class _SharedSecret {
|
|
761
752
|
constructor(secret) {
|
|
762
|
-
if (secret.length === SHARED_SECRET_SIZE / 2) {
|
|
763
|
-
const padded = new Uint8Array(SHARED_SECRET_SIZE);
|
|
764
|
-
padded.set(secret, SHARED_SECRET_SIZE - secret.length);
|
|
765
|
-
secret = padded;
|
|
766
|
-
}
|
|
767
753
|
if (secret.length !== SHARED_SECRET_SIZE) {
|
|
768
754
|
throw new Error(`Invalid shared secret length: expected ${SHARED_SECRET_SIZE} bytes, got ${secret.length}`);
|
|
769
755
|
}
|
|
770
|
-
this.secret =
|
|
756
|
+
this.secret = new Uint8Array(SHARED_SECRET_SIZE * 2);
|
|
757
|
+
this.secret.set(secret, 0);
|
|
771
758
|
}
|
|
772
759
|
toHash() {
|
|
773
|
-
|
|
760
|
+
const hash = import_sha22.sha256.create().update(this.secret.slice(0, 16)).digest();
|
|
761
|
+
return hash[0];
|
|
774
762
|
}
|
|
775
763
|
toBytes() {
|
|
776
|
-
return this.secret;
|
|
764
|
+
return this.secret.slice(0, 16);
|
|
777
765
|
}
|
|
778
766
|
toString() {
|
|
779
|
-
return bytesToHex(this.secret);
|
|
767
|
+
return (0, import_packet3.bytesToHex)(this.secret.slice(0, 16));
|
|
780
768
|
}
|
|
781
769
|
decrypt(hmac2, ciphertext) {
|
|
782
770
|
if (hmac2.length !== HMAC_SIZE) {
|
|
783
771
|
throw new Error(`Invalid HMAC length: expected ${HMAC_SIZE} bytes, got ${hmac2.length}`);
|
|
784
772
|
}
|
|
785
773
|
const expectedHmac = this.calculateHmac(ciphertext);
|
|
786
|
-
if (!equalBytes(hmac2, expectedHmac)) {
|
|
787
|
-
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)}`);
|
|
788
776
|
}
|
|
789
|
-
const cipher = ecb(this.secret.slice(0, 16), { disablePadding: true });
|
|
777
|
+
const cipher = (0, import_aes.ecb)(this.secret.slice(0, 16), { disablePadding: true });
|
|
790
778
|
const plaintext = new Uint8Array(ciphertext.length);
|
|
791
779
|
for (let i = 0; i < ciphertext.length; i += 16) {
|
|
792
780
|
const block = ciphertext.slice(i, i + 16);
|
|
@@ -801,7 +789,7 @@ var SharedSecret = class _SharedSecret {
|
|
|
801
789
|
}
|
|
802
790
|
encrypt(data) {
|
|
803
791
|
const key = this.secret.slice(0, 16);
|
|
804
|
-
const cipher = ecb(key, { disablePadding: true });
|
|
792
|
+
const cipher = (0, import_aes.ecb)(key, { disablePadding: true });
|
|
805
793
|
const fullBlocks = Math.floor(data.length / 16);
|
|
806
794
|
const remaining = data.length % 16;
|
|
807
795
|
const ciphertext = new Uint8Array((fullBlocks + (remaining > 0 ? 1 : 0)) * 16);
|
|
@@ -820,7 +808,7 @@ var SharedSecret = class _SharedSecret {
|
|
|
820
808
|
return { hmac: hmac2, ciphertext };
|
|
821
809
|
}
|
|
822
810
|
calculateHmac(data) {
|
|
823
|
-
return hmac(
|
|
811
|
+
return (0, import_hmac.hmac)(import_sha22.sha256, this.secret, data).slice(0, HMAC_SIZE);
|
|
824
812
|
}
|
|
825
813
|
static fromName(name) {
|
|
826
814
|
if (name === "Public") {
|
|
@@ -828,14 +816,14 @@ var SharedSecret = class _SharedSecret {
|
|
|
828
816
|
} else if (!/^#/.test(name)) {
|
|
829
817
|
throw new Error("Only the 'Public' group or groups starting with '#' are supported");
|
|
830
818
|
}
|
|
831
|
-
const hash =
|
|
819
|
+
const hash = import_sha22.sha256.create().update(new TextEncoder().encode(name)).digest();
|
|
832
820
|
return new _SharedSecret(hash.slice(0, SHARED_SECRET_SIZE));
|
|
833
821
|
}
|
|
834
822
|
};
|
|
835
823
|
var StaticSecret = class {
|
|
836
824
|
constructor(secret) {
|
|
837
825
|
if (typeof secret === "string") {
|
|
838
|
-
secret =
|
|
826
|
+
secret = (0, import_packet3.hexToBytes)(secret);
|
|
839
827
|
}
|
|
840
828
|
if (secret.length !== STATIC_SECRET_SIZE) {
|
|
841
829
|
throw new Error(`Invalid static secret length: expected ${STATIC_SECRET_SIZE} bytes, got ${secret.length}`);
|
|
@@ -843,16 +831,17 @@ var StaticSecret = class {
|
|
|
843
831
|
this.secret = secret;
|
|
844
832
|
}
|
|
845
833
|
publicKey() {
|
|
846
|
-
const publicKey = x25519.getPublicKey(this.secret);
|
|
834
|
+
const publicKey = import_ed25519.x25519.getPublicKey(this.secret);
|
|
847
835
|
return new PublicKey(publicKey);
|
|
848
836
|
}
|
|
849
837
|
diffieHellman(otherPublicKey) {
|
|
850
|
-
const sharedSecret = x25519.getSharedSecret(this.secret, otherPublicKey.toBytes());
|
|
851
|
-
return new SharedSecret(sharedSecret);
|
|
838
|
+
const sharedSecret = import_ed25519.x25519.getSharedSecret(this.secret, otherPublicKey.toBytes());
|
|
839
|
+
return new SharedSecret(sharedSecret.slice(0, 16));
|
|
852
840
|
}
|
|
853
841
|
};
|
|
854
842
|
|
|
855
843
|
// src/identity.ts
|
|
844
|
+
var import_packet4 = require("@hamradio/packet");
|
|
856
845
|
var parseNodeHash = (hash) => {
|
|
857
846
|
if (hash instanceof Uint8Array) {
|
|
858
847
|
return hash[0];
|
|
@@ -863,7 +852,7 @@ var parseNodeHash = (hash) => {
|
|
|
863
852
|
}
|
|
864
853
|
return hash;
|
|
865
854
|
} else if (typeof hash === "string") {
|
|
866
|
-
const parsed = hexToBytes(hash);
|
|
855
|
+
const parsed = (0, import_packet4.hexToBytes)(hash);
|
|
867
856
|
if (parsed.length !== 1) {
|
|
868
857
|
throw new Error("NodeHash string must represent a single byte");
|
|
869
858
|
}
|
|
@@ -879,7 +868,7 @@ var toPublicKeyBytes = (key) => {
|
|
|
879
868
|
} else if (key instanceof Uint8Array) {
|
|
880
869
|
return key;
|
|
881
870
|
} else if (typeof key === "string") {
|
|
882
|
-
return hexToBytes(key);
|
|
871
|
+
return (0, import_packet4.hexToBytes)(key);
|
|
883
872
|
} else {
|
|
884
873
|
throw new Error("Invalid type for toPublicKeyBytes");
|
|
885
874
|
}
|
|
@@ -938,7 +927,7 @@ var LocalIdentity = class extends Identity {
|
|
|
938
927
|
} else {
|
|
939
928
|
throw new Error("Invalid type for calculateSharedSecret comparison");
|
|
940
929
|
}
|
|
941
|
-
return new SharedSecret(this.privateKey.calculateSharedSecret(otherPublicKey));
|
|
930
|
+
return new SharedSecret(this.privateKey.calculateSharedSecret(otherPublicKey).slice(0, 16));
|
|
942
931
|
}
|
|
943
932
|
};
|
|
944
933
|
var Contact = class {
|
|
@@ -982,12 +971,12 @@ var Group = class {
|
|
|
982
971
|
if (data.length < 5) {
|
|
983
972
|
throw new Error("Invalid ciphertext");
|
|
984
973
|
}
|
|
985
|
-
const reader = new
|
|
986
|
-
const timestamp = reader.
|
|
987
|
-
const flags = reader.
|
|
974
|
+
const reader = new import_packet4.Reader(data);
|
|
975
|
+
const timestamp = reader.date32();
|
|
976
|
+
const flags = reader.uint8();
|
|
988
977
|
const textType = flags >> 2 & 63;
|
|
989
978
|
const attempt = flags & 3;
|
|
990
|
-
const message = new TextDecoder("utf-8").decode(reader.
|
|
979
|
+
const message = new TextDecoder("utf-8").decode(reader.bytes());
|
|
991
980
|
return {
|
|
992
981
|
timestamp,
|
|
993
982
|
textType,
|
|
@@ -996,11 +985,11 @@ var Group = class {
|
|
|
996
985
|
};
|
|
997
986
|
}
|
|
998
987
|
encryptText(plain) {
|
|
999
|
-
const writer = new
|
|
1000
|
-
writer.
|
|
988
|
+
const writer = new import_packet4.Writer(4 + 1 + new TextEncoder().encode(plain.message).length);
|
|
989
|
+
writer.date32(plain.timestamp);
|
|
1001
990
|
const flags = (plain.textType & 63) << 2 | plain.attempt & 3;
|
|
1002
|
-
writer.
|
|
1003
|
-
writer.
|
|
991
|
+
writer.uint8(flags);
|
|
992
|
+
writer.utf8String(plain.message);
|
|
1004
993
|
const data = writer.toBytes();
|
|
1005
994
|
return this.secret.encrypt(data);
|
|
1006
995
|
}
|
|
@@ -1009,16 +998,16 @@ var Group = class {
|
|
|
1009
998
|
if (data.length < 4) {
|
|
1010
999
|
throw new Error("Invalid ciphertext");
|
|
1011
1000
|
}
|
|
1012
|
-
const reader = new
|
|
1001
|
+
const reader = new import_packet4.Reader(data);
|
|
1013
1002
|
return {
|
|
1014
|
-
timestamp: reader.
|
|
1015
|
-
data: reader.
|
|
1003
|
+
timestamp: reader.date32(),
|
|
1004
|
+
data: reader.bytes()
|
|
1016
1005
|
};
|
|
1017
1006
|
}
|
|
1018
1007
|
encryptData(plain) {
|
|
1019
|
-
const writer = new
|
|
1020
|
-
writer.
|
|
1021
|
-
writer.
|
|
1008
|
+
const writer = new import_packet4.Writer(4 + plain.data.length);
|
|
1009
|
+
writer.date32(plain.timestamp);
|
|
1010
|
+
writer.bytes(plain.data);
|
|
1022
1011
|
const data = writer.toBytes();
|
|
1023
1012
|
return this.secret.encrypt(data);
|
|
1024
1013
|
}
|
|
@@ -1028,6 +1017,8 @@ var Contacts = class {
|
|
|
1028
1017
|
this.localIdentities = [];
|
|
1029
1018
|
this.contacts = {};
|
|
1030
1019
|
this.groups = {};
|
|
1020
|
+
this.addGroup(new Group("Public"));
|
|
1021
|
+
this.addGroup(new Group("#test"));
|
|
1031
1022
|
}
|
|
1032
1023
|
addLocalIdentity(identity) {
|
|
1033
1024
|
this.localIdentities.push({ identity, sharedSecrets: {} });
|
|
@@ -1039,6 +1030,23 @@ var Contacts = class {
|
|
|
1039
1030
|
}
|
|
1040
1031
|
this.contacts[hash].push(contact);
|
|
1041
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
|
+
}
|
|
1042
1050
|
decrypt(src, dst, hmac2, ciphertext) {
|
|
1043
1051
|
let contacts = [];
|
|
1044
1052
|
if (src instanceof PublicKey) {
|
|
@@ -1086,17 +1094,45 @@ var Contacts = class {
|
|
|
1086
1094
|
return sharedSecret;
|
|
1087
1095
|
}
|
|
1088
1096
|
addGroup(group) {
|
|
1089
|
-
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();
|
|
1090
1105
|
if (!this.groups[hash]) {
|
|
1091
1106
|
this.groups[hash] = [];
|
|
1092
1107
|
}
|
|
1093
1108
|
this.groups[hash].push(group);
|
|
1094
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
|
+
}
|
|
1095
1131
|
decryptGroupText(channelHash, hmac2, ciphertext) {
|
|
1096
1132
|
const hash = parseNodeHash(channelHash);
|
|
1097
1133
|
const groups = this.groups[hash] || [];
|
|
1098
1134
|
if (groups.length === 0) {
|
|
1099
|
-
throw new Error(
|
|
1135
|
+
throw new Error(`Unknown group hash ${hash.toString(16).padStart(2, "0")}`);
|
|
1100
1136
|
}
|
|
1101
1137
|
for (const group of groups) {
|
|
1102
1138
|
try {
|
|
@@ -1105,13 +1141,13 @@ var Contacts = class {
|
|
|
1105
1141
|
} catch {
|
|
1106
1142
|
}
|
|
1107
1143
|
}
|
|
1108
|
-
throw new Error(
|
|
1144
|
+
throw new Error(`Decryption failed with all known groups with hash ${hash.toString(16).padStart(2, "0")}`);
|
|
1109
1145
|
}
|
|
1110
1146
|
decryptGroupData(channelHash, hmac2, ciphertext) {
|
|
1111
1147
|
const hash = parseNodeHash(channelHash);
|
|
1112
1148
|
const groups = this.groups[hash] || [];
|
|
1113
1149
|
if (groups.length === 0) {
|
|
1114
|
-
throw new Error(
|
|
1150
|
+
throw new Error(`Unknown group hash ${hash.toString(16).padStart(2, "0")}`);
|
|
1115
1151
|
}
|
|
1116
1152
|
for (const group of groups) {
|
|
1117
1153
|
try {
|
|
@@ -1120,10 +1156,11 @@ var Contacts = class {
|
|
|
1120
1156
|
} catch {
|
|
1121
1157
|
}
|
|
1122
1158
|
}
|
|
1123
|
-
throw new Error(
|
|
1159
|
+
throw new Error(`Decryption failed with all known groups with hash ${hash.toString(16).padStart(2, "0")}`);
|
|
1124
1160
|
}
|
|
1125
1161
|
};
|
|
1126
|
-
export
|
|
1162
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1163
|
+
0 && (module.exports = {
|
|
1127
1164
|
Contact,
|
|
1128
1165
|
Contacts,
|
|
1129
1166
|
Group,
|
|
@@ -1140,4 +1177,4 @@ export {
|
|
|
1140
1177
|
StaticSecret,
|
|
1141
1178
|
TextType,
|
|
1142
1179
|
parseNodeHash
|
|
1143
|
-
};
|
|
1180
|
+
});
|