@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.
- package/{double-ratchet.d.ts → dist/double-ratchet.d.ts} +26 -38
- package/dist/double-ratchet.js +345 -0
- package/{index.d.ts → dist/index.d.ts} +11 -13
- package/{index.js → dist/index.js} +12 -10
- package/dist/node.d.ts +53 -0
- package/dist/node.js +242 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +41 -0
- package/{types.d.ts → dist/types.d.ts} +33 -9
- package/{types.js → dist/types.js} +50 -33
- package/{x3dh.d.ts → dist/x3dh.d.ts} +2 -1
- package/{x3dh.js → dist/x3dh.js} +9 -8
- package/package.json +30 -6
- package/double-ratchet.js +0 -294
- package/node.d.ts +0 -27
- package/node.js +0 -105
- package/test.js +0 -25
package/package.json
CHANGED
|
@@ -1,21 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freesignal/protocol",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Signal Protocol implementation in javascript",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"author": "Christian Braghette",
|
|
7
7
|
"type": "commonjs",
|
|
8
|
-
"
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./double-ratchet": {
|
|
14
|
+
"import": "./dist/double-ratchet.js",
|
|
15
|
+
"types": "./dist/double-ratchet.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./node": {
|
|
18
|
+
"import": "./dist/node.js",
|
|
19
|
+
"types": "./dist/node.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./types": {
|
|
22
|
+
"import": "./dist/types.js",
|
|
23
|
+
"types": "./dist/types.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"./x3dh": {
|
|
26
|
+
"import": "./dist/x3dh.js",
|
|
27
|
+
"types": "./dist/x3dh.d.ts"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.js",
|
|
9
31
|
"scripts": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
32
|
+
"pretest": "tsc",
|
|
33
|
+
"test": "node ./dist/test.js",
|
|
34
|
+
"prepare": "tsc"
|
|
12
35
|
},
|
|
13
36
|
"dependencies": {
|
|
14
37
|
"@freesignal/crypto": "^0.3.0",
|
|
15
38
|
"@freesignal/interfaces": "^0.2.0",
|
|
16
|
-
"@freesignal/utils": "^1.
|
|
39
|
+
"@freesignal/utils": "^1.4.1",
|
|
40
|
+
"semaphore.ts": "^0.2.0"
|
|
17
41
|
},
|
|
18
42
|
"devDependencies": {
|
|
19
43
|
"@types/node": "^24.2.1"
|
|
20
44
|
}
|
|
21
|
-
}
|
|
45
|
+
}
|
package/double-ratchet.js
DELETED
|
@@ -1,294 +0,0 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
30
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
31
|
-
};
|
|
32
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.EncryptedDataConstructor = exports.KeySession = void 0;
|
|
34
|
-
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
35
|
-
const utils_1 = require("@freesignal/utils");
|
|
36
|
-
const types_1 = require("./types");
|
|
37
|
-
/**
|
|
38
|
-
* Represents a secure Double Ratchet session.
|
|
39
|
-
* Used for forward-secure encryption and decryption of messages.
|
|
40
|
-
*/
|
|
41
|
-
class KeySession {
|
|
42
|
-
constructor(storage, opts = {}) {
|
|
43
|
-
var _a;
|
|
44
|
-
this.sendingCount = 0;
|
|
45
|
-
this.previousCount = 0;
|
|
46
|
-
this.receivingCount = 0;
|
|
47
|
-
this.previousKeys = new KeyMap();
|
|
48
|
-
this.id = (_a = opts.id) !== null && _a !== void 0 ? _a : crypto_1.default.UUID.generate().toString();
|
|
49
|
-
this.keyPair = crypto_1.default.ECDH.keyPair(opts.secretKey);
|
|
50
|
-
if (opts.rootKey)
|
|
51
|
-
this.rootKey = opts.rootKey;
|
|
52
|
-
if (opts.remoteKey) {
|
|
53
|
-
this._remoteKey = opts.remoteKey;
|
|
54
|
-
this.sendingChain = this.ratchetKeys();
|
|
55
|
-
}
|
|
56
|
-
this.storage = storage;
|
|
57
|
-
this.storage.set(this.id, this.toJSON());
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Whether both the sending and receiving chains are initialized.
|
|
61
|
-
*/
|
|
62
|
-
get handshaked() { return this.sendingChain && this.receivingChain ? true : false; }
|
|
63
|
-
/**
|
|
64
|
-
* The public key of this session.
|
|
65
|
-
*/
|
|
66
|
-
get publicKey() { return this.keyPair.publicKey; }
|
|
67
|
-
/**
|
|
68
|
-
* The last known remote public key.
|
|
69
|
-
*/
|
|
70
|
-
get remoteKey() { return this._remoteKey; }
|
|
71
|
-
setRemoteKey(key) {
|
|
72
|
-
this._remoteKey = key;
|
|
73
|
-
this.receivingChain = this.ratchetKeys();
|
|
74
|
-
if (this.receivingCount > (EncryptedDataConstructor.maxCount - KeySession.skipLimit * 2))
|
|
75
|
-
this.receivingCount = 0;
|
|
76
|
-
this.previousCount = this.sendingCount;
|
|
77
|
-
this.keyPair = crypto_1.default.ECDH.keyPair();
|
|
78
|
-
this.sendingChain = this.ratchetKeys();
|
|
79
|
-
if (this.sendingCount > (EncryptedDataConstructor.maxCount - KeySession.skipLimit * 2))
|
|
80
|
-
this.sendingCount = 0;
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
ratchetKeys(info) {
|
|
84
|
-
if (!this._remoteKey)
|
|
85
|
-
throw new Error();
|
|
86
|
-
const sharedKey = crypto_1.default.ECDH.scalarMult(this.keyPair.secretKey, this._remoteKey);
|
|
87
|
-
if (!this.rootKey)
|
|
88
|
-
this.rootKey = crypto_1.default.hash(sharedKey);
|
|
89
|
-
const hashkey = crypto_1.default.hkdf(sharedKey, this.rootKey, info, KeySession.keyLength * 2);
|
|
90
|
-
this.rootKey = hashkey.slice(0, KeySession.keyLength);
|
|
91
|
-
return hashkey.slice(KeySession.keyLength);
|
|
92
|
-
}
|
|
93
|
-
getSendingKey() {
|
|
94
|
-
if (!this.sendingChain)
|
|
95
|
-
throw new Error;
|
|
96
|
-
const { chainKey, sharedKey } = KeySession.symmetricRatchet(this.sendingChain);
|
|
97
|
-
this.sendingChain = chainKey;
|
|
98
|
-
this.sendingCount++;
|
|
99
|
-
return sharedKey;
|
|
100
|
-
}
|
|
101
|
-
getReceivingKey() {
|
|
102
|
-
if (!this.receivingChain)
|
|
103
|
-
throw new Error();
|
|
104
|
-
const { chainKey, sharedKey } = KeySession.symmetricRatchet(this.receivingChain);
|
|
105
|
-
this.receivingChain = chainKey;
|
|
106
|
-
this.receivingCount++;
|
|
107
|
-
return sharedKey;
|
|
108
|
-
}
|
|
109
|
-
save() {
|
|
110
|
-
return this.storage.set(this.id, this.toJSON());
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Encrypts a message payload using the current sending chain.
|
|
114
|
-
*
|
|
115
|
-
* @param message - The message as a Uint8Array.
|
|
116
|
-
* @returns An EncryptedPayload or undefined if encryption fails.
|
|
117
|
-
*/
|
|
118
|
-
encrypt(message) {
|
|
119
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
-
const key = this.getSendingKey();
|
|
121
|
-
if (this.sendingCount >= EncryptedDataConstructor.maxCount || this.previousCount >= EncryptedDataConstructor.maxCount)
|
|
122
|
-
throw new Error();
|
|
123
|
-
const nonce = crypto_1.default.randomBytes(EncryptedDataConstructor.nonceLength);
|
|
124
|
-
const ciphertext = crypto_1.default.box.encrypt(message, nonce, key);
|
|
125
|
-
yield this.save();
|
|
126
|
-
return new EncryptedDataConstructor(this.sendingCount, this.previousCount, this.keyPair.publicKey, nonce, ciphertext);
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Decrypts an encrypted message.
|
|
131
|
-
*
|
|
132
|
-
* @param payload - The received encrypted message.
|
|
133
|
-
* @returns The decrypted message as a Uint8Array, or undefined if decryption fails.
|
|
134
|
-
*/
|
|
135
|
-
decrypt(payload) {
|
|
136
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
137
|
-
const encrypted = types_1.EncryptedData.from(payload);
|
|
138
|
-
const publicKey = encrypted.publicKey;
|
|
139
|
-
if (this._remoteKey && !(0, utils_1.verifyArrays)(publicKey, this._remoteKey))
|
|
140
|
-
while (this.receivingCount < encrypted.previous)
|
|
141
|
-
this.previousKeys.set(this.receivingCount, this.getReceivingKey());
|
|
142
|
-
this.setRemoteKey(publicKey);
|
|
143
|
-
let key;
|
|
144
|
-
const count = encrypted.count;
|
|
145
|
-
if (this.receivingCount < count) {
|
|
146
|
-
let i = 0;
|
|
147
|
-
while (this.receivingCount < count - 1 && i < KeySession.skipLimit) {
|
|
148
|
-
this.previousKeys.set(this.receivingCount, this.getReceivingKey());
|
|
149
|
-
}
|
|
150
|
-
key = this.getReceivingKey();
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
key = this.previousKeys.get(count);
|
|
154
|
-
}
|
|
155
|
-
if (!key)
|
|
156
|
-
return undefined;
|
|
157
|
-
yield this.save();
|
|
158
|
-
return crypto_1.default.box.decrypt(encrypted.ciphertext, encrypted.nonce, key);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Export the state of the session;
|
|
163
|
-
*/
|
|
164
|
-
toJSON() {
|
|
165
|
-
var _a, _b, _c, _d;
|
|
166
|
-
return {
|
|
167
|
-
secretKey: (0, utils_1.decodeBase64)((0, utils_1.concatArrays)(this.keyPair.secretKey)),
|
|
168
|
-
remoteKey: (0, utils_1.decodeBase64)((_a = this._remoteKey) !== null && _a !== void 0 ? _a : new Uint8Array()),
|
|
169
|
-
rootKey: (0, utils_1.decodeBase64)((_b = this.rootKey) !== null && _b !== void 0 ? _b : new Uint8Array()),
|
|
170
|
-
sendingChain: (0, utils_1.decodeBase64)((_c = this.sendingChain) !== null && _c !== void 0 ? _c : new Uint8Array()),
|
|
171
|
-
receivingChain: (0, utils_1.decodeBase64)((_d = this.receivingChain) !== null && _d !== void 0 ? _d : new Uint8Array()),
|
|
172
|
-
sendingCount: this.sendingCount,
|
|
173
|
-
receivingCount: this.receivingCount,
|
|
174
|
-
previousCount: this.previousCount,
|
|
175
|
-
previousKeys: Array.from(this.previousKeys.entries())
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Import a state.
|
|
180
|
-
*
|
|
181
|
-
* @param json string returned by `export()` method.
|
|
182
|
-
* @returns session with the state parsed.
|
|
183
|
-
*/
|
|
184
|
-
static from(data, storage) {
|
|
185
|
-
const session = new KeySession(storage, { secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: (0, utils_1.encodeBase64)(data.rootKey) });
|
|
186
|
-
session._remoteKey = (0, utils_1.encodeBase64)(data.remoteKey);
|
|
187
|
-
session.sendingChain = (0, utils_1.encodeBase64)(data.sendingChain);
|
|
188
|
-
session.receivingChain = (0, utils_1.encodeBase64)(data.receivingChain);
|
|
189
|
-
session.sendingCount = data.sendingCount;
|
|
190
|
-
session.receivingCount = data.receivingCount;
|
|
191
|
-
session.previousCount = data.previousCount;
|
|
192
|
-
session.previousKeys = new KeyMap(data.previousKeys);
|
|
193
|
-
session.save();
|
|
194
|
-
return session;
|
|
195
|
-
}
|
|
196
|
-
static symmetricRatchet(chain, salt, info) {
|
|
197
|
-
const hash = crypto_1.default.hkdf(chain, salt !== null && salt !== void 0 ? salt : new Uint8Array(), info, KeySession.keyLength * 2);
|
|
198
|
-
return {
|
|
199
|
-
chainKey: new Uint8Array(hash.buffer, 0, KeySession.keyLength),
|
|
200
|
-
sharedKey: new Uint8Array(hash.buffer, KeySession.keyLength)
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
exports.KeySession = KeySession;
|
|
205
|
-
KeySession.skipLimit = 1000;
|
|
206
|
-
KeySession.version = 1;
|
|
207
|
-
KeySession.rootKeyLength = crypto_1.default.box.keyLength;
|
|
208
|
-
/**
|
|
209
|
-
* The fixed key length (in bytes) used throughout the Double Ratchet session.
|
|
210
|
-
* Typically 32 bytes (256 bits) for symmetric keys.
|
|
211
|
-
*/
|
|
212
|
-
KeySession.keyLength = 32;
|
|
213
|
-
class EncryptedDataConstructor {
|
|
214
|
-
constructor(...arrays) {
|
|
215
|
-
arrays = arrays.filter(value => value !== undefined);
|
|
216
|
-
if (arrays[0] instanceof EncryptedDataConstructor) {
|
|
217
|
-
this.raw = arrays[0].raw;
|
|
218
|
-
return this;
|
|
219
|
-
}
|
|
220
|
-
if (typeof arrays[0] === 'number')
|
|
221
|
-
arrays[0] = (0, utils_1.numberToArray)(arrays[0], EncryptedDataConstructor.countLength);
|
|
222
|
-
if (typeof arrays[1] === 'number')
|
|
223
|
-
arrays[1] = (0, utils_1.numberToArray)(arrays[1], EncryptedDataConstructor.countLength);
|
|
224
|
-
if (arrays.length === 6) {
|
|
225
|
-
arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToArray)(arrays[5]) : arrays[5]);
|
|
226
|
-
arrays.pop();
|
|
227
|
-
}
|
|
228
|
-
else if (arrays.length > 1) {
|
|
229
|
-
arrays.unshift((0, utils_1.numberToArray)(KeySession.version));
|
|
230
|
-
}
|
|
231
|
-
this.raw = (0, utils_1.concatArrays)(...arrays);
|
|
232
|
-
}
|
|
233
|
-
get length() { return this.raw.length; }
|
|
234
|
-
get version() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
|
|
235
|
-
get count() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
|
|
236
|
-
get previous() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
|
|
237
|
-
get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
|
|
238
|
-
get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
|
|
239
|
-
get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
|
|
240
|
-
toBytes() {
|
|
241
|
-
return this.raw;
|
|
242
|
-
}
|
|
243
|
-
toString() {
|
|
244
|
-
return (0, utils_1.decodeBase64)(this.raw);
|
|
245
|
-
}
|
|
246
|
-
toJSON() {
|
|
247
|
-
return {
|
|
248
|
-
version: this.version,
|
|
249
|
-
count: this.count,
|
|
250
|
-
previous: this.previous,
|
|
251
|
-
publicKey: (0, utils_1.decodeBase64)(this.publicKey),
|
|
252
|
-
nonce: (0, utils_1.decodeBase64)(this.nonce),
|
|
253
|
-
ciphertext: (0, utils_1.decodeBase64)(this.ciphertext)
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
exports.EncryptedDataConstructor = EncryptedDataConstructor;
|
|
258
|
-
EncryptedDataConstructor.secretKeyLength = crypto_1.default.ECDH.secretKeyLength;
|
|
259
|
-
EncryptedDataConstructor.publicKeyLength = crypto_1.default.ECDH.publicKeyLength;
|
|
260
|
-
EncryptedDataConstructor.keyLength = crypto_1.default.box.keyLength;
|
|
261
|
-
EncryptedDataConstructor.nonceLength = crypto_1.default.box.nonceLength;
|
|
262
|
-
EncryptedDataConstructor.maxCount = 65536; //32768;
|
|
263
|
-
EncryptedDataConstructor.countLength = 2;
|
|
264
|
-
class Offsets {
|
|
265
|
-
static set(start, length) {
|
|
266
|
-
class Offset {
|
|
267
|
-
constructor(start, length) {
|
|
268
|
-
this.start = start;
|
|
269
|
-
this.length = length;
|
|
270
|
-
if (typeof length === 'number')
|
|
271
|
-
this.end = start + length;
|
|
272
|
-
}
|
|
273
|
-
get get() {
|
|
274
|
-
return [this.start, this.length];
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return new Offset(start, length);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
Offsets.checksum = Offsets.set(0, 0);
|
|
281
|
-
Offsets.version = Offsets.set(Offsets.checksum.end, 1);
|
|
282
|
-
Offsets.count = Offsets.set(Offsets.version.end, EncryptedDataConstructor.countLength);
|
|
283
|
-
Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.countLength);
|
|
284
|
-
Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
|
|
285
|
-
Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
|
|
286
|
-
Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
|
|
287
|
-
class KeyMap extends Map {
|
|
288
|
-
get(key) {
|
|
289
|
-
const out = super.get(key);
|
|
290
|
-
if (out && !super.delete(key))
|
|
291
|
-
throw new Error();
|
|
292
|
-
return out;
|
|
293
|
-
}
|
|
294
|
-
}
|
package/node.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
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
|
-
export declare class FreeSignalNode {
|
|
6
|
-
protected readonly privateIdentityKey: PrivateIdentityKey;
|
|
7
|
-
protected readonly sessions: Map<string, KeySession>;
|
|
8
|
-
protected readonly sessionsData: LocalStorage<string, ExportedKeySession>;
|
|
9
|
-
protected readonly users: LocalStorage<string, IdentityKey>;
|
|
10
|
-
protected readonly keyExchange: KeyExchange;
|
|
11
|
-
constructor(storage: Database<{
|
|
12
|
-
sessions: LocalStorage<string, ExportedKeySession>;
|
|
13
|
-
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
14
|
-
users: LocalStorage<string, IdentityKey>;
|
|
15
|
-
}>, privateIdentityKey?: PrivateIdentityKey);
|
|
16
|
-
get userId(): UserId;
|
|
17
|
-
get identityKey(): IdentityKey;
|
|
18
|
-
generateKeyData(): Promise<KeyExchangeData>;
|
|
19
|
-
generateKeyBundle(length?: number): Promise<KeyExchangeDataBundle>;
|
|
20
|
-
encrypt(receiverId: string, protocol: Protocols, data: Uint8Array): Promise<Datagram>;
|
|
21
|
-
sendHandshake(data: KeyExchangeData): Promise<Datagram>;
|
|
22
|
-
sendData<T>(receiverId: string, data: T): Promise<Datagram>;
|
|
23
|
-
sendRelay(receiverId: string, data: Datagram): Promise<Datagram>;
|
|
24
|
-
sendDiscover(receiverId: string, discoverId: string): Promise<Datagram>;
|
|
25
|
-
decrypt(datagram: Datagram): Promise<Uint8Array>;
|
|
26
|
-
receive<T extends Uint8Array | UserId | Datagram | UserId | void>(datagram: Datagram | Uint8Array): Promise<T>;
|
|
27
|
-
}
|
package/node.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.FreeSignalNode = void 0;
|
|
13
|
-
const types_1 = require("./types");
|
|
14
|
-
const x3dh_1 = require("./x3dh");
|
|
15
|
-
const _1 = require(".");
|
|
16
|
-
const utils_1 = require("@freesignal/utils");
|
|
17
|
-
class FreeSignalNode {
|
|
18
|
-
constructor(storage, privateIdentityKey) {
|
|
19
|
-
this.sessions = new Map();
|
|
20
|
-
this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : (0, _1.createIdentity)();
|
|
21
|
-
this.sessionsData = storage.sessions;
|
|
22
|
-
this.keyExchange = new x3dh_1.KeyExchange({ keys: storage.keyExchange, sessions: storage.sessions }, this.privateIdentityKey);
|
|
23
|
-
this.users = storage.users;
|
|
24
|
-
/*Array.from(this.sessionsData.entries()).forEach(([userId, sessionData]) => {
|
|
25
|
-
this.sessions.set(userId, KeySession.from(sessionData, this.sessionsData));
|
|
26
|
-
});*/
|
|
27
|
-
}
|
|
28
|
-
get userId() {
|
|
29
|
-
return types_1.UserId.fromKey(this.privateIdentityKey.identityKey);
|
|
30
|
-
}
|
|
31
|
-
get identityKey() {
|
|
32
|
-
return this.privateIdentityKey.identityKey;
|
|
33
|
-
}
|
|
34
|
-
generateKeyData() {
|
|
35
|
-
return this.keyExchange.generateData();
|
|
36
|
-
}
|
|
37
|
-
;
|
|
38
|
-
generateKeyBundle(length) {
|
|
39
|
-
return this.keyExchange.generateBundle(length);
|
|
40
|
-
}
|
|
41
|
-
;
|
|
42
|
-
encrypt(receiverId, protocol, data) {
|
|
43
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
-
const session = this.sessions.get(receiverId);
|
|
45
|
-
if (!session)
|
|
46
|
-
throw new Error("Session not found for user: " + receiverId);
|
|
47
|
-
return new types_1.Datagram(this.userId.toString(), receiverId, protocol, yield session.encrypt(data));
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
sendHandshake(data) {
|
|
51
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
const { session, message, identityKey } = yield this.keyExchange.digestData(data);
|
|
53
|
-
const remoteId = types_1.UserId.fromKey(identityKey);
|
|
54
|
-
this.sessions.set(remoteId.toString(), session);
|
|
55
|
-
return new types_1.Datagram(this.userId.toString(), types_1.UserId.fromKey(data.identityKey).toString(), types_1.Protocols.HANDSHAKE, (0, utils_1.encodeData)(message));
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
sendData(receiverId, data) {
|
|
59
|
-
return this.encrypt(receiverId, types_1.Protocols.MESSAGE, (0, utils_1.encodeData)(data));
|
|
60
|
-
}
|
|
61
|
-
sendRelay(receiverId, data) {
|
|
62
|
-
return this.encrypt(receiverId, types_1.Protocols.RELAY, (0, utils_1.encodeData)(data));
|
|
63
|
-
}
|
|
64
|
-
sendDiscover(receiverId, discoverId) {
|
|
65
|
-
return this.encrypt(receiverId, types_1.Protocols.DISCOVER, (0, utils_1.encodeData)(discoverId));
|
|
66
|
-
}
|
|
67
|
-
decrypt(datagram) {
|
|
68
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
-
const userId = datagram.sender;
|
|
70
|
-
const session = this.sessions.get(userId);
|
|
71
|
-
if (!session)
|
|
72
|
-
throw new Error("Session not found for user: " + userId);
|
|
73
|
-
if (!datagram.payload)
|
|
74
|
-
throw new Error("Missing payload");
|
|
75
|
-
const decrypted = yield session.decrypt(datagram.payload);
|
|
76
|
-
if (!decrypted)
|
|
77
|
-
throw new Error("Decryption failed");
|
|
78
|
-
return decrypted;
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
receive(datagram) {
|
|
82
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
-
if (datagram instanceof Uint8Array)
|
|
84
|
-
datagram = types_1.Datagram.from(datagram);
|
|
85
|
-
switch (datagram.protocol) {
|
|
86
|
-
case types_1.Protocols.HANDSHAKE:
|
|
87
|
-
if (!datagram.payload)
|
|
88
|
-
throw new Error("Missing payload");
|
|
89
|
-
const data = (0, utils_1.decodeData)(datagram.payload);
|
|
90
|
-
const { session, identityKey } = yield this.keyExchange.digestMessage(data);
|
|
91
|
-
this.sessions.set(types_1.UserId.fromKey(identityKey).toString(), session);
|
|
92
|
-
return;
|
|
93
|
-
case types_1.Protocols.MESSAGE:
|
|
94
|
-
return yield this.decrypt(datagram);
|
|
95
|
-
case types_1.Protocols.RELAY:
|
|
96
|
-
return (0, utils_1.decodeData)(yield this.decrypt(datagram));
|
|
97
|
-
case types_1.Protocols.DISCOVER:
|
|
98
|
-
return types_1.UserId.from((0, utils_1.decodeData)(yield this.decrypt(datagram)));
|
|
99
|
-
default:
|
|
100
|
-
throw new Error("Invalid protocol");
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
exports.FreeSignalNode = FreeSignalNode;
|
package/test.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const utils_1 = require("@freesignal/utils");
|
|
13
|
-
const _1 = require(".");
|
|
14
|
-
const node_1 = require("./node");
|
|
15
|
-
const bob = new node_1.FreeSignalNode({ keyExchange: new _1.AsyncMap(), sessions: new _1.AsyncMap(), users: new _1.AsyncMap() });
|
|
16
|
-
const alice = new node_1.FreeSignalNode({ keyExchange: new _1.AsyncMap(), sessions: new _1.AsyncMap(), users: new _1.AsyncMap() });
|
|
17
|
-
setImmediate(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
-
const aliceHandshake = yield alice.sendHandshake(yield bob.generateKeyData());
|
|
19
|
-
yield bob.receive(aliceHandshake);
|
|
20
|
-
console.log("Session established successfully between Alice and Bob.");
|
|
21
|
-
const data = (yield bob.encrypt(alice.userId.toString(), _1.Protocols.MESSAGE, (0, utils_1.encodeData)("Hi Alice!"))).toBytes();
|
|
22
|
-
console.log((0, utils_1.decodeData)(yield alice.receive(data)));
|
|
23
|
-
const longmsg = yield alice.sendData(bob.userId.toString(), new Uint8Array(1000000).fill(33).map(val => val + Math.floor(Math.random() * 93)));
|
|
24
|
-
console.log(longmsg.toBytes().length);
|
|
25
|
-
}));
|