@freesignal/protocol 0.2.11 → 0.3.2

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.
@@ -20,56 +20,34 @@ import { LocalStorage } from "@freesignal/interfaces";
20
20
  import { EncryptedData } from "./types";
21
21
  export interface ExportedKeySession {
22
22
  secretKey: string;
23
- remoteKey: string;
24
- rootKey: string;
25
- sendingChain: string;
26
- receivingChain: string;
27
- sendingCount: number;
28
- receivingCount: number;
29
- previousCount: number;
30
- previousKeys: [number, Uint8Array][];
23
+ rootKey?: string;
24
+ sendingChain?: ExportedKeyChain;
25
+ receivingChain?: ExportedKeyChain;
26
+ previousKeys: [string, Uint8Array][];
31
27
  }
32
28
  /**
33
29
  * Represents a secure Double Ratchet session.
34
30
  * Used for forward-secure encryption and decryption of messages.
35
31
  */
36
32
  export declare class KeySession {
37
- private static readonly skipLimit;
33
+ static readonly keyLength = 32;
38
34
  static readonly version = 1;
39
- static readonly rootKeyLength: number;
35
+ static readonly info: string;
40
36
  readonly id: string;
37
+ private readonly mutex;
38
+ private readonly storage;
41
39
  private keyPair;
42
- private _remoteKey?;
43
40
  private rootKey?;
44
41
  private sendingChain?;
45
- private sendingCount;
46
- private previousCount;
47
42
  private receivingChain?;
48
- private receivingCount;
49
43
  private previousKeys;
50
- private readonly storage;
51
44
  constructor(storage: LocalStorage<string, ExportedKeySession>, opts?: {
52
45
  id?: string;
53
46
  secretKey?: Uint8Array;
54
47
  remoteKey?: Uint8Array;
55
48
  rootKey?: Uint8Array;
56
49
  });
57
- /**
58
- * Whether both the sending and receiving chains are initialized.
59
- */
60
- get handshaked(): boolean;
61
- /**
62
- * The public key of this session.
63
- */
64
- get publicKey(): Uint8Array;
65
- /**
66
- * The last known remote public key.
67
- */
68
- get remoteKey(): Uint8Array | undefined;
69
- private setRemoteKey;
70
- private ratchetKeys;
71
- private getSendingKey;
72
- private getReceivingKey;
50
+ private getChain;
73
51
  private save;
74
52
  /**
75
53
  * Encrypts a message payload using the current sending chain.
@@ -84,7 +62,15 @@ export declare class KeySession {
84
62
  * @param payload - The received encrypted message.
85
63
  * @returns The decrypted message as a Uint8Array, or undefined if decryption fails.
86
64
  */
87
- decrypt(payload: Uint8Array | EncryptedData): Promise<Uint8Array | undefined>;
65
+ decrypt(payload: Uint8Array | EncryptedData): Promise<Uint8Array>;
66
+ /**
67
+ * Whether both the sending and receiving chains are initialized.
68
+ */
69
+ get handshaked(): boolean;
70
+ /**
71
+ * The public key of this session.
72
+ */
73
+ get publicKey(): Uint8Array;
88
74
  /**
89
75
  * Export the state of the session;
90
76
  */
@@ -96,12 +82,13 @@ export declare class KeySession {
96
82
  * @returns session with the state parsed.
97
83
  */
98
84
  static from(data: ExportedKeySession, storage: LocalStorage<string, ExportedKeySession>): KeySession;
99
- /**
100
- * The fixed key length (in bytes) used throughout the Double Ratchet session.
101
- * Typically 32 bytes (256 bits) for symmetric keys.
102
- */
103
- static readonly keyLength = 32;
104
- private static symmetricRatchet;
85
+ }
86
+ interface ExportedKeyChain {
87
+ publicKey: string;
88
+ remoteKey: string;
89
+ chainKey: string;
90
+ count: number;
91
+ previousCount: number;
105
92
  }
106
93
  export declare class EncryptedDataConstructor implements EncryptedData {
107
94
  static readonly secretKeyLength: number;
@@ -131,3 +118,4 @@ export declare class EncryptedDataConstructor implements EncryptedData {
131
118
  ciphertext: string;
132
119
  };
133
120
  }
121
+ export {};
@@ -0,0 +1,345 @@
1
+ "use strict";
2
+ /**
3
+ * FreeSignal Protocol
4
+ *
5
+ * Copyright (C) 2025 Christian Braghette
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
19
+ */
20
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
21
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
22
+ return new (P || (P = Promise))(function (resolve, reject) {
23
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
24
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
25
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
26
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
27
+ });
28
+ };
29
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
30
+ if (value !== null && value !== void 0) {
31
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
32
+ var dispose, inner;
33
+ if (async) {
34
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
35
+ dispose = value[Symbol.asyncDispose];
36
+ }
37
+ if (dispose === void 0) {
38
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
39
+ dispose = value[Symbol.dispose];
40
+ if (async) inner = dispose;
41
+ }
42
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
43
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
44
+ env.stack.push({ value: value, dispose: dispose, async: async });
45
+ }
46
+ else if (async) {
47
+ env.stack.push({ async: true });
48
+ }
49
+ return value;
50
+ };
51
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
52
+ return function (env) {
53
+ function fail(e) {
54
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
55
+ env.hasError = true;
56
+ }
57
+ var r, s = 0;
58
+ function next() {
59
+ while (r = env.stack.pop()) {
60
+ try {
61
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
62
+ if (r.dispose) {
63
+ var result = r.dispose.call(r.value);
64
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
65
+ }
66
+ else s |= 1;
67
+ }
68
+ catch (e) {
69
+ fail(e);
70
+ }
71
+ }
72
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
73
+ if (env.hasError) throw env.error;
74
+ }
75
+ return next();
76
+ };
77
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
78
+ var e = new Error(message);
79
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
80
+ });
81
+ var __importDefault = (this && this.__importDefault) || function (mod) {
82
+ return (mod && mod.__esModule) ? mod : { "default": mod };
83
+ };
84
+ Object.defineProperty(exports, "__esModule", { value: true });
85
+ exports.EncryptedDataConstructor = exports.KeySession = void 0;
86
+ const crypto_1 = __importDefault(require("@freesignal/crypto"));
87
+ const utils_1 = require("@freesignal/utils");
88
+ const types_1 = require("./types");
89
+ const semaphore_ts_1 = require("semaphore.ts");
90
+ /**
91
+ * Represents a secure Double Ratchet session.
92
+ * Used for forward-secure encryption and decryption of messages.
93
+ */
94
+ class KeySession {
95
+ constructor(storage, opts = {}) {
96
+ var _a;
97
+ this.mutex = { sending: new semaphore_ts_1.AsyncMutex(), receiving: new semaphore_ts_1.AsyncMutex() };
98
+ this.previousKeys = new KeyMap();
99
+ this.id = (_a = opts.id) !== null && _a !== void 0 ? _a : crypto_1.default.UUID.generate().toString();
100
+ this.keyPair = crypto_1.default.ECDH.keyPair(opts.secretKey);
101
+ if (opts.rootKey)
102
+ this.rootKey = opts.rootKey;
103
+ if (opts.remoteKey) {
104
+ this.sendingChain = this.getChain(opts.remoteKey);
105
+ }
106
+ this.storage = storage;
107
+ this.save();
108
+ }
109
+ getChain(remoteKey, previousCount) {
110
+ const sharedKey = crypto_1.default.ECDH.scalarMult(this.keyPair.secretKey, remoteKey);
111
+ if (!this.rootKey)
112
+ this.rootKey = crypto_1.default.hash(sharedKey);
113
+ const hashkey = crypto_1.default.hkdf(sharedKey, this.rootKey, KeySession.info, KeySession.keyLength * 2);
114
+ this.rootKey = hashkey.subarray(0, KeySession.keyLength);
115
+ return new KeyChain(this.publicKey, remoteKey, hashkey.subarray(KeySession.keyLength), previousCount);
116
+ }
117
+ save() {
118
+ return this.storage.set(this.id, this.toJSON());
119
+ }
120
+ /**
121
+ * Encrypts a message payload using the current sending chain.
122
+ *
123
+ * @param message - The message as a Uint8Array.
124
+ * @returns An EncryptedPayload or undefined if encryption fails.
125
+ */
126
+ encrypt(message) {
127
+ return __awaiter(this, void 0, void 0, function* () {
128
+ const env_1 = { stack: [], error: void 0, hasError: false };
129
+ try {
130
+ const lock = __addDisposableResource(env_1, yield this.mutex.sending.acquire(), false);
131
+ if (!this.sendingChain)
132
+ throw new Error("SendingChain not initialized");
133
+ const key = this.sendingChain.getKey();
134
+ const nonce = crypto_1.default.randomBytes(EncryptedDataConstructor.nonceLength);
135
+ const ciphertext = crypto_1.default.box.encrypt(message, nonce, key);
136
+ this.save();
137
+ return new EncryptedDataConstructor(this.sendingChain.count, this.sendingChain.previousCount, this.keyPair.publicKey, nonce, ciphertext);
138
+ }
139
+ catch (e_1) {
140
+ env_1.error = e_1;
141
+ env_1.hasError = true;
142
+ }
143
+ finally {
144
+ __disposeResources(env_1);
145
+ }
146
+ });
147
+ }
148
+ /**
149
+ * Decrypts an encrypted message.
150
+ *
151
+ * @param payload - The received encrypted message.
152
+ * @returns The decrypted message as a Uint8Array, or undefined if decryption fails.
153
+ */
154
+ decrypt(payload) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ var _a, _b, _c;
157
+ const encrypted = types_1.EncryptedData.from(payload);
158
+ if (!this.previousKeys.has((0, utils_1.decodeBase64)(encrypted.publicKey) + encrypted.count.toString())) {
159
+ const lock = yield this.mutex.receiving.acquire();
160
+ if (!(0, utils_1.compareBytes)(encrypted.publicKey, (_b = (_a = this.receivingChain) === null || _a === void 0 ? void 0 : _a.remoteKey) !== null && _b !== void 0 ? _b : new Uint8Array())) {
161
+ while (this.receivingChain && this.receivingChain.count < encrypted.previous) {
162
+ const key = this.receivingChain.getKey();
163
+ this.previousKeys.set((0, utils_1.decodeBase64)(this.receivingChain.remoteKey) + this.receivingChain.count.toString(), key);
164
+ }
165
+ this.receivingChain = this.getChain(encrypted.publicKey);
166
+ this.keyPair = crypto_1.default.ECDH.keyPair();
167
+ this.sendingChain = this.getChain(encrypted.publicKey, (_c = this.sendingChain) === null || _c === void 0 ? void 0 : _c.count);
168
+ }
169
+ if (!this.receivingChain)
170
+ throw new Error("Error initializing receivingChain");
171
+ while (this.receivingChain.count < encrypted.count) {
172
+ const key = this.receivingChain.getKey();
173
+ this.previousKeys.set((0, utils_1.decodeBase64)(this.receivingChain.remoteKey) + this.receivingChain.count.toString(), key);
174
+ }
175
+ lock.release();
176
+ }
177
+ const key = this.previousKeys.get((0, utils_1.decodeBase64)(encrypted.publicKey) + encrypted.count.toString());
178
+ if (!key)
179
+ throw new Error("Error calculating key");
180
+ this.save();
181
+ const cleartext = crypto_1.default.box.decrypt(encrypted.ciphertext, encrypted.nonce, key);
182
+ if (!cleartext)
183
+ throw new Error("Error decrypting ciphertext");
184
+ return cleartext;
185
+ });
186
+ }
187
+ /**
188
+ * Whether both the sending and receiving chains are initialized.
189
+ */
190
+ get handshaked() { return this.sendingChain && this.receivingChain ? true : false; }
191
+ /**
192
+ * The public key of this session.
193
+ */
194
+ get publicKey() { return this.keyPair.publicKey; }
195
+ /**
196
+ * Export the state of the session;
197
+ */
198
+ toJSON() {
199
+ var _a, _b;
200
+ return {
201
+ secretKey: (0, utils_1.decodeBase64)(this.keyPair.secretKey),
202
+ rootKey: this.rootKey ? (0, utils_1.decodeBase64)(this.rootKey) : undefined,
203
+ sendingChain: (_a = this.sendingChain) === null || _a === void 0 ? void 0 : _a.toJSON(),
204
+ receivingChain: (_b = this.receivingChain) === null || _b === void 0 ? void 0 : _b.toJSON(),
205
+ previousKeys: Array.from(this.previousKeys.entries())
206
+ };
207
+ }
208
+ /**
209
+ * Import a state.
210
+ *
211
+ * @param json string returned by `export()` method.
212
+ * @returns session with the state parsed.
213
+ */
214
+ static from(data, storage) {
215
+ const session = new KeySession(storage, { secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: data.rootKey ? (0, utils_1.encodeBase64)(data.rootKey) : undefined });
216
+ //session._remoteKey = data.remoteKey ? encodeBase64(data.remoteKey) : undefined;
217
+ session.sendingChain = data.sendingChain ? KeyChain.from(data.sendingChain) : undefined;
218
+ session.receivingChain = data.receivingChain ? KeyChain.from(data.receivingChain) : undefined;
219
+ session.previousKeys = new KeyMap(data.previousKeys);
220
+ session.save();
221
+ return session;
222
+ }
223
+ }
224
+ exports.KeySession = KeySession;
225
+ KeySession.keyLength = 32;
226
+ KeySession.version = 1;
227
+ KeySession.info = "/freesignal/double-ratchet/v0." + KeySession.version;
228
+ class KeyChain {
229
+ constructor(publicKey, remoteKey, chainKey, previousCount = 0) {
230
+ this.publicKey = publicKey;
231
+ this.remoteKey = remoteKey;
232
+ this.chainKey = chainKey;
233
+ this.previousCount = previousCount;
234
+ this._count = 0;
235
+ }
236
+ getKey() {
237
+ if (++this._count >= EncryptedDataConstructor.maxCount)
238
+ throw new Error("SendingChain count too big");
239
+ const hash = crypto_1.default.hkdf(this.chainKey, new Uint8Array(KeySession.keyLength).fill(0), KeySession.info, KeySession.keyLength * 2);
240
+ this.chainKey = hash.subarray(0, KeySession.keyLength);
241
+ return hash.subarray(KeySession.keyLength);
242
+ }
243
+ toString() {
244
+ return "[object KeyChain]";
245
+ }
246
+ get count() {
247
+ return this._count;
248
+ }
249
+ toJSON() {
250
+ return {
251
+ publicKey: (0, utils_1.decodeBase64)(this.publicKey),
252
+ remoteKey: (0, utils_1.decodeBase64)(this.remoteKey),
253
+ chainKey: (0, utils_1.decodeBase64)(this.chainKey),
254
+ count: this.count,
255
+ previousCount: this.previousCount
256
+ };
257
+ }
258
+ static from(obj) {
259
+ const chain = new KeyChain((0, utils_1.encodeBase64)(obj.publicKey), (0, utils_1.encodeBase64)(obj.remoteKey), (0, utils_1.encodeBase64)(obj.chainKey), obj.previousCount);
260
+ chain._count = obj.count;
261
+ return chain;
262
+ }
263
+ }
264
+ class EncryptedDataConstructor {
265
+ constructor(...arrays) {
266
+ arrays = arrays.filter(value => value !== undefined);
267
+ if (arrays[0] instanceof EncryptedDataConstructor) {
268
+ this.raw = arrays[0].raw;
269
+ return this;
270
+ }
271
+ if (typeof arrays[0] === 'number')
272
+ arrays[0] = (0, utils_1.numberToBytes)(arrays[0], EncryptedDataConstructor.countLength);
273
+ if (typeof arrays[1] === 'number')
274
+ arrays[1] = (0, utils_1.numberToBytes)(arrays[1], EncryptedDataConstructor.countLength);
275
+ if (arrays.length === 6) {
276
+ arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToBytes)(arrays[5], 1) : arrays[5]);
277
+ arrays.pop();
278
+ }
279
+ else if (arrays.length > 1) {
280
+ arrays.unshift((0, utils_1.numberToBytes)(KeySession.version, 1));
281
+ }
282
+ this.raw = (0, utils_1.concatBytes)(...arrays);
283
+ }
284
+ get length() { return this.raw.length; }
285
+ get version() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
286
+ get count() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
287
+ get previous() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
288
+ get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
289
+ get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
290
+ get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
291
+ toBytes() {
292
+ return this.raw;
293
+ }
294
+ toString() {
295
+ return (0, utils_1.decodeBase64)(this.raw);
296
+ }
297
+ toJSON() {
298
+ return {
299
+ version: this.version,
300
+ count: this.count,
301
+ previous: this.previous,
302
+ publicKey: (0, utils_1.decodeBase64)(this.publicKey),
303
+ nonce: (0, utils_1.decodeBase64)(this.nonce),
304
+ ciphertext: (0, utils_1.decodeBase64)(this.ciphertext)
305
+ };
306
+ }
307
+ }
308
+ exports.EncryptedDataConstructor = EncryptedDataConstructor;
309
+ EncryptedDataConstructor.secretKeyLength = crypto_1.default.ECDH.secretKeyLength;
310
+ EncryptedDataConstructor.publicKeyLength = crypto_1.default.ECDH.publicKeyLength;
311
+ EncryptedDataConstructor.keyLength = crypto_1.default.box.keyLength;
312
+ EncryptedDataConstructor.nonceLength = crypto_1.default.box.nonceLength;
313
+ EncryptedDataConstructor.maxCount = 65536; //32768;
314
+ EncryptedDataConstructor.countLength = 2;
315
+ class Offsets {
316
+ static set(start, length) {
317
+ class Offset {
318
+ constructor(start, length) {
319
+ this.start = start;
320
+ this.length = length;
321
+ if (typeof length === 'number')
322
+ this.end = start + length;
323
+ }
324
+ get get() {
325
+ return [this.start, this.length];
326
+ }
327
+ }
328
+ return new Offset(start, length);
329
+ }
330
+ }
331
+ Offsets.checksum = Offsets.set(0, 0);
332
+ Offsets.version = Offsets.set(Offsets.checksum.end, 1);
333
+ Offsets.count = Offsets.set(Offsets.version.end, EncryptedDataConstructor.countLength);
334
+ Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.countLength);
335
+ Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
336
+ Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
337
+ Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
338
+ class KeyMap extends Map {
339
+ get(key) {
340
+ const out = super.get(key);
341
+ if (out && !super.delete(key))
342
+ throw new Error();
343
+ return out;
344
+ }
345
+ }
@@ -16,10 +16,10 @@
16
16
  * You should have received a copy of the GNU General Public License
17
17
  * along with this program. If not, see <https://www.gnu.org/licenses/>
18
18
  */
19
- import { LocalStorage, Crypto } from "@freesignal/interfaces";
20
- import { ExportedKeySession, KeySession } from "./double-ratchet";
21
- import { KeyExchange } from "./x3dh";
22
- import { PrivateIdentityKey } from "./types";
19
+ import { LocalStorage, Crypto, Database, KeyExchangeDataBundle } from "@freesignal/interfaces";
20
+ import { ExportedKeySession } from "./double-ratchet";
21
+ import { IdentityKey, PrivateIdentityKey } from "./types";
22
+ import { FreeSignalNode } from "./node";
23
23
  /**
24
24
  * Creates a new Double Ratchet session for secure message exchange.
25
25
  *
@@ -29,21 +29,12 @@ import { PrivateIdentityKey } from "./types";
29
29
  * @param opts.rootKey - An optional root key to initialize the session.
30
30
  * @returns A new instance of {@link KeySession}.
31
31
  */
32
- export declare function createKeySession(storage: LocalStorage<string, ExportedKeySession>, opts?: {
33
- secretKey?: Uint8Array;
34
- remoteKey?: Uint8Array;
35
- rootKey?: Uint8Array;
36
- }): KeySession;
37
32
  /**
38
33
  * Creates a new X3DH (Extended Triple Diffie-Hellman) key exchange session.
39
34
  *
40
35
  * @param storage - Local storage for keys.
41
36
  * @returns A new instance of {@link KeyExchange}.
42
37
  */
43
- export declare function createKeyExchange(storage: {
44
- keys: LocalStorage<string, Crypto.KeyPair>;
45
- sessions: LocalStorage<string, ExportedKeySession>;
46
- }, privateIdentityKey?: PrivateIdentityKey): KeyExchange;
47
38
  /**
48
39
  * Generates identity key
49
40
  *
@@ -51,4 +42,11 @@ export declare function createKeyExchange(storage: {
51
42
  * @returns An object containing readonly signing and box key pairs.
52
43
  */
53
44
  export declare function createIdentity(seed?: Uint8Array): PrivateIdentityKey;
45
+ /** */
46
+ export declare function createNode(storage: Database<{
47
+ sessions: LocalStorage<string, ExportedKeySession>;
48
+ keyExchange: LocalStorage<string, Crypto.KeyPair>;
49
+ users: LocalStorage<string, IdentityKey>;
50
+ bundles: LocalStorage<string, KeyExchangeDataBundle>;
51
+ }>, privateIdentityKey?: PrivateIdentityKey): FreeSignalNode;
54
52
  export * from "./types";
@@ -35,13 +35,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.createKeySession = createKeySession;
39
- exports.createKeyExchange = createKeyExchange;
40
38
  exports.createIdentity = createIdentity;
39
+ exports.createNode = createNode;
41
40
  const crypto_1 = __importDefault(require("@freesignal/crypto"));
42
- const double_ratchet_1 = require("./double-ratchet");
43
- const x3dh_1 = require("./x3dh");
44
41
  const types_1 = require("./types");
42
+ const node_1 = require("./node");
45
43
  /**
46
44
  * Creates a new Double Ratchet session for secure message exchange.
47
45
  *
@@ -51,18 +49,18 @@ const types_1 = require("./types");
51
49
  * @param opts.rootKey - An optional root key to initialize the session.
52
50
  * @returns A new instance of {@link KeySession}.
53
51
  */
54
- function createKeySession(storage, opts) {
55
- return new double_ratchet_1.KeySession(storage, opts);
56
- }
52
+ /*export function createKeySession(storage: LocalStorage<string, ExportedKeySession>, opts?: { secretKey?: Uint8Array, remoteKey?: Uint8Array, rootKey?: Uint8Array }): KeySession {
53
+ return new KeySession(storage, opts);
54
+ }*/
57
55
  /**
58
56
  * Creates a new X3DH (Extended Triple Diffie-Hellman) key exchange session.
59
57
  *
60
58
  * @param storage - Local storage for keys.
61
59
  * @returns A new instance of {@link KeyExchange}.
62
60
  */
63
- function createKeyExchange(storage, privateIdentityKey) {
64
- return new x3dh_1.KeyExchange(storage, privateIdentityKey);
65
- }
61
+ /*export function createKeyExchange(storage: { keys: LocalStorage<string, Crypto.KeyPair>, sessions: LocalStorage<string, ExportedKeySession> }, privateIdentityKey?: PrivateIdentityKey): KeyExchange {
62
+ return new KeyExchange(storage, privateIdentityKey);
63
+ }*/
66
64
  /**
67
65
  * Generates identity key
68
66
  *
@@ -77,4 +75,8 @@ function createIdentity(seed) {
77
75
  const exchangeKeyPair = crypto_1.default.ECDH.keyPair(exchangeSeed);
78
76
  return types_1.PrivateIdentityKey.from(signatureKeyPair.secretKey, exchangeKeyPair.secretKey);
79
77
  }
78
+ /** */
79
+ function createNode(storage, privateIdentityKey) {
80
+ return new node_1.FreeSignalNode(storage, privateIdentityKey);
81
+ }
80
82
  __exportStar(require("./types"), exports);
package/dist/node.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { Database, LocalStorage, Crypto, KeyExchangeDataBundle, KeyExchangeData } from "@freesignal/interfaces";
2
+ import { Datagram, IdentityKey, PrivateIdentityKey, Protocols, UserId } from "./types";
3
+ import { KeyExchange } from "./x3dh";
4
+ import { ExportedKeySession, KeySession } from "./double-ratchet";
5
+ declare class BootstrapRequest {
6
+ #private;
7
+ readonly senderId: UserId | string;
8
+ readonly data: KeyExchangeData;
9
+ private readonly acceptFn;
10
+ constructor(senderId: UserId | string, data: KeyExchangeData, acceptFn: (data: KeyExchangeData) => Promise<Datagram>);
11
+ get status(): "pending" | "accepted" | "denied";
12
+ accept(): Promise<Datagram | undefined>;
13
+ deny(): void;
14
+ }
15
+ type OpenFnReturns = Uint8Array | UserId | Datagram | UserId | KeyExchangeData | undefined | void;
16
+ export declare class FreeSignalNode {
17
+ protected readonly privateIdentityKey: PrivateIdentityKey;
18
+ protected readonly sessions: SessionMap;
19
+ protected readonly users: LocalStorage<string, IdentityKey>;
20
+ protected readonly bundles: LocalStorage<string, KeyExchangeDataBundle>;
21
+ protected readonly keyExchange: KeyExchange;
22
+ protected readonly discovers: Set<string>;
23
+ protected readonly bootstraps: Set<BootstrapRequest>;
24
+ constructor(storage: Database<{
25
+ sessions: LocalStorage<string, ExportedKeySession>;
26
+ keyExchange: LocalStorage<string, Crypto.KeyPair>;
27
+ users: LocalStorage<string, IdentityKey>;
28
+ bundles: LocalStorage<string, KeyExchangeDataBundle>;
29
+ }>, privateIdentityKey?: PrivateIdentityKey);
30
+ get identityKey(): IdentityKey;
31
+ get userId(): UserId;
32
+ get requests(): BootstrapRequest[];
33
+ protected encrypt(receiverId: string | UserId, protocol: Protocols, data: Uint8Array): Promise<Datagram>;
34
+ packHandshake(data: KeyExchangeData): Promise<Datagram>;
35
+ packData<T>(receiverId: string | UserId, data: T): Promise<Datagram>;
36
+ packRelay(receiverId: string | UserId, data: Datagram): Promise<Datagram>;
37
+ packDiscover(receiverId: string | UserId, discoverId: string | UserId): Promise<Datagram>;
38
+ packBootstrap(receiverId: string | UserId): Promise<Datagram>;
39
+ protected decrypt(datagram: Datagram): Promise<Uint8Array>;
40
+ open<T extends OpenFnReturns>(datagram: Datagram | Uint8Array): Promise<T>;
41
+ }
42
+ declare class SessionMap implements LocalStorage<string, KeySession> {
43
+ readonly storage: LocalStorage<string, ExportedKeySession>;
44
+ readonly maxSize: number;
45
+ private readonly cache;
46
+ constructor(storage: LocalStorage<string, ExportedKeySession>, maxSize?: number);
47
+ set(key: string, value: KeySession): Promise<void>;
48
+ get(key: string): Promise<KeySession | undefined>;
49
+ has(key: string): Promise<boolean>;
50
+ delete(key: string): Promise<boolean>;
51
+ clear(): Promise<void>;
52
+ }
53
+ export {};