@freesignal/protocol 0.3.0 → 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.js → dist/double-ratchet.js} +24 -31
- package/{index.d.ts → dist/index.d.ts} +2 -1
- 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} +49 -32
- package/{x3dh.d.ts → dist/x3dh.d.ts} +2 -1
- package/{x3dh.js → dist/x3dh.js} +6 -5
- package/package.json +29 -6
- package/node.d.ts +0 -38
- package/node.js +0 -139
- package/test.js +0 -28
- /package/{double-ratchet.d.ts → dist/double-ratchet.d.ts} +0 -0
- /package/{index.js → dist/index.js} +0 -0
|
@@ -94,7 +94,7 @@ const semaphore_ts_1 = require("semaphore.ts");
|
|
|
94
94
|
class KeySession {
|
|
95
95
|
constructor(storage, opts = {}) {
|
|
96
96
|
var _a;
|
|
97
|
-
this.mutex = new semaphore_ts_1.AsyncMutex();
|
|
97
|
+
this.mutex = { sending: new semaphore_ts_1.AsyncMutex(), receiving: new semaphore_ts_1.AsyncMutex() };
|
|
98
98
|
this.previousKeys = new KeyMap();
|
|
99
99
|
this.id = (_a = opts.id) !== null && _a !== void 0 ? _a : crypto_1.default.UUID.generate().toString();
|
|
100
100
|
this.keyPair = crypto_1.default.ECDH.keyPair(opts.secretKey);
|
|
@@ -127,13 +127,13 @@ class KeySession {
|
|
|
127
127
|
return __awaiter(this, void 0, void 0, function* () {
|
|
128
128
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
129
129
|
try {
|
|
130
|
-
const lock = __addDisposableResource(env_1, yield this.mutex.acquire(), false);
|
|
130
|
+
const lock = __addDisposableResource(env_1, yield this.mutex.sending.acquire(), false);
|
|
131
131
|
if (!this.sendingChain)
|
|
132
132
|
throw new Error("SendingChain not initialized");
|
|
133
133
|
const key = this.sendingChain.getKey();
|
|
134
134
|
const nonce = crypto_1.default.randomBytes(EncryptedDataConstructor.nonceLength);
|
|
135
135
|
const ciphertext = crypto_1.default.box.encrypt(message, nonce, key);
|
|
136
|
-
|
|
136
|
+
this.save();
|
|
137
137
|
return new EncryptedDataConstructor(this.sendingChain.count, this.sendingChain.previousCount, this.keyPair.publicKey, nonce, ciphertext);
|
|
138
138
|
}
|
|
139
139
|
catch (e_1) {
|
|
@@ -154,11 +154,10 @@ class KeySession {
|
|
|
154
154
|
decrypt(payload) {
|
|
155
155
|
return __awaiter(this, void 0, void 0, function* () {
|
|
156
156
|
var _a, _b, _c;
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const lock =
|
|
160
|
-
|
|
161
|
-
if (!(0, utils_1.verifyArrays)(encrypted.publicKey, (_b = (_a = this.receivingChain) === null || _a === void 0 ? void 0 : _a.remoteKey) !== null && _b !== void 0 ? _b : new Uint8Array())) {
|
|
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())) {
|
|
162
161
|
while (this.receivingChain && this.receivingChain.count < encrypted.previous) {
|
|
163
162
|
const key = this.receivingChain.getKey();
|
|
164
163
|
this.previousKeys.set((0, utils_1.decodeBase64)(this.receivingChain.remoteKey) + this.receivingChain.count.toString(), key);
|
|
@@ -173,22 +172,16 @@ class KeySession {
|
|
|
173
172
|
const key = this.receivingChain.getKey();
|
|
174
173
|
this.previousKeys.set((0, utils_1.decodeBase64)(this.receivingChain.remoteKey) + this.receivingChain.count.toString(), key);
|
|
175
174
|
}
|
|
176
|
-
|
|
177
|
-
if (!key)
|
|
178
|
-
throw new Error("Error calculating key");
|
|
179
|
-
yield this.save();
|
|
180
|
-
const cleartext = crypto_1.default.box.decrypt(encrypted.ciphertext, encrypted.nonce, key);
|
|
181
|
-
if (!cleartext)
|
|
182
|
-
throw new Error("Error decrypting ciphertext");
|
|
183
|
-
return cleartext;
|
|
184
|
-
}
|
|
185
|
-
catch (e_2) {
|
|
186
|
-
env_2.error = e_2;
|
|
187
|
-
env_2.hasError = true;
|
|
188
|
-
}
|
|
189
|
-
finally {
|
|
190
|
-
__disposeResources(env_2);
|
|
175
|
+
lock.release();
|
|
191
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;
|
|
192
185
|
});
|
|
193
186
|
}
|
|
194
187
|
/**
|
|
@@ -276,22 +269,22 @@ class EncryptedDataConstructor {
|
|
|
276
269
|
return this;
|
|
277
270
|
}
|
|
278
271
|
if (typeof arrays[0] === 'number')
|
|
279
|
-
arrays[0] = (0, utils_1.
|
|
272
|
+
arrays[0] = (0, utils_1.numberToBytes)(arrays[0], EncryptedDataConstructor.countLength);
|
|
280
273
|
if (typeof arrays[1] === 'number')
|
|
281
|
-
arrays[1] = (0, utils_1.
|
|
274
|
+
arrays[1] = (0, utils_1.numberToBytes)(arrays[1], EncryptedDataConstructor.countLength);
|
|
282
275
|
if (arrays.length === 6) {
|
|
283
|
-
arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.
|
|
276
|
+
arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToBytes)(arrays[5], 1) : arrays[5]);
|
|
284
277
|
arrays.pop();
|
|
285
278
|
}
|
|
286
279
|
else if (arrays.length > 1) {
|
|
287
|
-
arrays.unshift((0, utils_1.
|
|
280
|
+
arrays.unshift((0, utils_1.numberToBytes)(KeySession.version, 1));
|
|
288
281
|
}
|
|
289
|
-
this.raw = (0, utils_1.
|
|
282
|
+
this.raw = (0, utils_1.concatBytes)(...arrays);
|
|
290
283
|
}
|
|
291
284
|
get length() { return this.raw.length; }
|
|
292
|
-
get version() { return (0, utils_1.
|
|
293
|
-
get count() { return (0, utils_1.
|
|
294
|
-
get previous() { return (0, utils_1.
|
|
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)); }
|
|
295
288
|
get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
|
|
296
289
|
get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
|
|
297
290
|
get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
|
|
@@ -16,7 +16,7 @@
|
|
|
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, Database } from "@freesignal/interfaces";
|
|
19
|
+
import { LocalStorage, Crypto, Database, KeyExchangeDataBundle } from "@freesignal/interfaces";
|
|
20
20
|
import { ExportedKeySession } from "./double-ratchet";
|
|
21
21
|
import { IdentityKey, PrivateIdentityKey } from "./types";
|
|
22
22
|
import { FreeSignalNode } from "./node";
|
|
@@ -47,5 +47,6 @@ export declare function createNode(storage: Database<{
|
|
|
47
47
|
sessions: LocalStorage<string, ExportedKeySession>;
|
|
48
48
|
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
49
49
|
users: LocalStorage<string, IdentityKey>;
|
|
50
|
+
bundles: LocalStorage<string, KeyExchangeDataBundle>;
|
|
50
51
|
}>, privateIdentityKey?: PrivateIdentityKey): FreeSignalNode;
|
|
51
52
|
export * from "./types";
|
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 {};
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
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
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
12
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
13
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
14
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
15
|
+
};
|
|
16
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
17
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
18
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
19
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
20
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
21
|
+
};
|
|
22
|
+
var _BootstrapRequest_status;
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.FreeSignalNode = void 0;
|
|
25
|
+
const types_1 = require("./types");
|
|
26
|
+
const x3dh_1 = require("./x3dh");
|
|
27
|
+
const double_ratchet_1 = require("./double-ratchet");
|
|
28
|
+
const _1 = require(".");
|
|
29
|
+
const utils_1 = require("@freesignal/utils");
|
|
30
|
+
class BootstrapRequest {
|
|
31
|
+
constructor(senderId, data, acceptFn) {
|
|
32
|
+
this.senderId = senderId;
|
|
33
|
+
this.data = data;
|
|
34
|
+
this.acceptFn = acceptFn;
|
|
35
|
+
_BootstrapRequest_status.set(this, 'pending');
|
|
36
|
+
}
|
|
37
|
+
get status() {
|
|
38
|
+
return __classPrivateFieldGet(this, _BootstrapRequest_status, "f");
|
|
39
|
+
}
|
|
40
|
+
accept() {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
if (this.status === 'pending')
|
|
43
|
+
__classPrivateFieldSet(this, _BootstrapRequest_status, 'accepted', "f");
|
|
44
|
+
if (__classPrivateFieldGet(this, _BootstrapRequest_status, "f") === 'accepted')
|
|
45
|
+
return yield this.acceptFn(this.data);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
deny() {
|
|
49
|
+
if (this.status === 'pending')
|
|
50
|
+
__classPrivateFieldSet(this, _BootstrapRequest_status, 'denied', "f");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
_BootstrapRequest_status = new WeakMap();
|
|
55
|
+
class FreeSignalNode {
|
|
56
|
+
constructor(storage, privateIdentityKey) {
|
|
57
|
+
this.discovers = new Set();
|
|
58
|
+
this.bootstraps = new Set();
|
|
59
|
+
this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : (0, _1.createIdentity)();
|
|
60
|
+
this.sessions = new SessionMap(storage.sessions);
|
|
61
|
+
this.keyExchange = new x3dh_1.KeyExchange({ keys: storage.keyExchange, sessions: storage.sessions }, this.privateIdentityKey);
|
|
62
|
+
this.users = storage.users;
|
|
63
|
+
this.bundles = storage.bundles;
|
|
64
|
+
}
|
|
65
|
+
get identityKey() {
|
|
66
|
+
return this.privateIdentityKey.identityKey;
|
|
67
|
+
}
|
|
68
|
+
get userId() {
|
|
69
|
+
return types_1.UserId.fromKey(this.identityKey);
|
|
70
|
+
}
|
|
71
|
+
get requests() {
|
|
72
|
+
return Array.from(this.bootstraps.values());
|
|
73
|
+
}
|
|
74
|
+
encrypt(receiverId, protocol, data) {
|
|
75
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
+
if (receiverId instanceof types_1.UserId)
|
|
77
|
+
receiverId = receiverId.toString();
|
|
78
|
+
const session = yield this.sessions.get(receiverId);
|
|
79
|
+
if (!session)
|
|
80
|
+
throw new Error("Session not found for user: " + receiverId);
|
|
81
|
+
return new types_1.Datagram(this.userId.toString(), receiverId, protocol, yield session.encrypt(data)).sign(this.privateIdentityKey.signatureKey);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
packHandshake(data) {
|
|
85
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
const { session, message, identityKey } = yield this.keyExchange.digestData(data, (0, utils_1.encodeData)(yield this.keyExchange.generateBundle()));
|
|
87
|
+
const remoteId = types_1.UserId.fromKey(identityKey);
|
|
88
|
+
yield this.users.set(remoteId.toString(), identityKey);
|
|
89
|
+
yield this.sessions.set(remoteId.toString(), session);
|
|
90
|
+
return new types_1.Datagram(this.userId.toString(), types_1.UserId.fromKey(data.identityKey).toString(), types_1.Protocols.HANDSHAKE, (0, utils_1.encodeData)(message)).sign(this.privateIdentityKey.signatureKey);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
packData(receiverId, data) {
|
|
94
|
+
return this.encrypt(receiverId, types_1.Protocols.MESSAGE, (0, utils_1.encodeData)(data));
|
|
95
|
+
}
|
|
96
|
+
packRelay(receiverId, data) {
|
|
97
|
+
return this.encrypt(receiverId, types_1.Protocols.RELAY, (0, utils_1.encodeData)(data));
|
|
98
|
+
}
|
|
99
|
+
packDiscover(receiverId, discoverId) {
|
|
100
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
if (receiverId instanceof types_1.UserId)
|
|
102
|
+
receiverId = receiverId.toString();
|
|
103
|
+
if (discoverId instanceof types_1.UserId)
|
|
104
|
+
discoverId = discoverId.toString();
|
|
105
|
+
const message = {
|
|
106
|
+
type: types_1.DiscoverType.REQUEST,
|
|
107
|
+
discoverId
|
|
108
|
+
};
|
|
109
|
+
this.discovers.add(receiverId);
|
|
110
|
+
return this.encrypt(receiverId, types_1.Protocols.DISCOVER, (0, utils_1.encodeData)(message));
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
packBootstrap(receiverId) {
|
|
114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
115
|
+
return new types_1.Datagram(this.userId.toString(), receiverId.toString(), types_1.Protocols.BOOTSTRAP, (0, utils_1.encodeData)(yield this.keyExchange.generateData()));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
decrypt(datagram) {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
const signatureKey = yield this.users.get(datagram.sender);
|
|
121
|
+
if (!signatureKey)
|
|
122
|
+
throw new Error("User IdentityKey not found");
|
|
123
|
+
if (!types_1.Datagram.verify(datagram, signatureKey.signatureKey))
|
|
124
|
+
throw new Error("Signature not verified");
|
|
125
|
+
const session = yield this.sessions.get(datagram.sender);
|
|
126
|
+
if (!session)
|
|
127
|
+
throw new Error("Session not found for user: " + datagram.sender);
|
|
128
|
+
if (!datagram.payload)
|
|
129
|
+
throw new Error("Missing payload");
|
|
130
|
+
const decrypted = yield session.decrypt(datagram.payload);
|
|
131
|
+
if (!decrypted)
|
|
132
|
+
throw new Error("Decryption failed");
|
|
133
|
+
return decrypted;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
open(datagram) {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
if (datagram instanceof Uint8Array)
|
|
139
|
+
datagram = types_1.Datagram.from(datagram);
|
|
140
|
+
switch (datagram.protocol) {
|
|
141
|
+
case types_1.Protocols.HANDSHAKE:
|
|
142
|
+
if (!datagram.payload)
|
|
143
|
+
throw new Error("Missing payload");
|
|
144
|
+
const data = (0, utils_1.decodeData)(datagram.payload);
|
|
145
|
+
if (!types_1.Datagram.verify(datagram, types_1.IdentityKey.from(data.identityKey).signatureKey))
|
|
146
|
+
throw new Error("Signature not verified");
|
|
147
|
+
const { session, identityKey, associatedData } = yield this.keyExchange.digestMessage(data);
|
|
148
|
+
const userId = types_1.UserId.fromKey(identityKey);
|
|
149
|
+
yield this.users.set(userId.toString(), identityKey);
|
|
150
|
+
yield this.sessions.set(userId.toString(), session);
|
|
151
|
+
yield this.bundles.set(userId.toString(), (0, utils_1.decodeData)(associatedData));
|
|
152
|
+
return;
|
|
153
|
+
case types_1.Protocols.MESSAGE:
|
|
154
|
+
return (0, utils_1.decodeData)(yield this.decrypt(datagram));
|
|
155
|
+
case types_1.Protocols.RELAY:
|
|
156
|
+
return (0, utils_1.decodeData)(yield this.decrypt(datagram));
|
|
157
|
+
case types_1.Protocols.DISCOVER:
|
|
158
|
+
const message = (0, utils_1.decodeData)(yield this.decrypt(datagram));
|
|
159
|
+
if (message.type === types_1.DiscoverType.REQUEST && message.discoverId && !(yield this.users.has(message.discoverId))) {
|
|
160
|
+
let data;
|
|
161
|
+
if (message.discoverId === this.userId.toString()) {
|
|
162
|
+
data = yield this.keyExchange.generateData();
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const bundle = yield this.bundles.get(message.discoverId);
|
|
166
|
+
if (!bundle)
|
|
167
|
+
return;
|
|
168
|
+
const { version, identityKey, signedPreKey, signature } = bundle;
|
|
169
|
+
const onetimePreKey = bundle.onetimePreKeys.shift();
|
|
170
|
+
if (!onetimePreKey) {
|
|
171
|
+
yield this.bundles.delete(message.discoverId);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
data = {
|
|
175
|
+
version,
|
|
176
|
+
identityKey,
|
|
177
|
+
signedPreKey,
|
|
178
|
+
signature,
|
|
179
|
+
onetimePreKey
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const response = { type: types_1.DiscoverType.RESPONSE, discoverId: message.discoverId, data };
|
|
183
|
+
return yield this.encrypt(datagram.sender, types_1.Protocols.DISCOVER, (0, utils_1.encodeData)(response));
|
|
184
|
+
}
|
|
185
|
+
else if (message.type === types_1.DiscoverType.RESPONSE && this.discovers.has(message.discoverId)) {
|
|
186
|
+
this.discovers.delete(message.discoverId);
|
|
187
|
+
return message.data;
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
case types_1.Protocols.BOOTSTRAP:
|
|
191
|
+
if (datagram.payload) {
|
|
192
|
+
const data = (0, utils_1.decodeData)(datagram.payload);
|
|
193
|
+
if (!(0, utils_1.compareBytes)(types_1.UserId.fromKey(data.identityKey).toBytes(), (0, utils_1.encodeBase64)(datagram.sender)))
|
|
194
|
+
return;
|
|
195
|
+
this.bootstraps.add(new BootstrapRequest(datagram.sender, data, (data) => this.packHandshake(data)));
|
|
196
|
+
}
|
|
197
|
+
;
|
|
198
|
+
return;
|
|
199
|
+
default:
|
|
200
|
+
throw new Error("Invalid protocol");
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.FreeSignalNode = FreeSignalNode;
|
|
206
|
+
class SessionMap {
|
|
207
|
+
constructor(storage, maxSize = 50) {
|
|
208
|
+
this.storage = storage;
|
|
209
|
+
this.maxSize = maxSize;
|
|
210
|
+
this.cache = new Map();
|
|
211
|
+
}
|
|
212
|
+
set(key, value) {
|
|
213
|
+
this.cache.set(key, value);
|
|
214
|
+
return this.storage.set(key, value.toJSON());
|
|
215
|
+
}
|
|
216
|
+
get(key) {
|
|
217
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
+
const session = this.cache.get(key);
|
|
219
|
+
if (!session) {
|
|
220
|
+
const sessionData = yield this.storage.get(key);
|
|
221
|
+
if (!sessionData)
|
|
222
|
+
return undefined;
|
|
223
|
+
return double_ratchet_1.KeySession.from(sessionData, this.storage);
|
|
224
|
+
}
|
|
225
|
+
return session;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
has(key) {
|
|
229
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
230
|
+
return this.cache.has(key) || (yield this.storage.has(key));
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
delete(key) {
|
|
234
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
235
|
+
return this.cache.delete(key) || (yield this.storage.delete(key));
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
clear() {
|
|
239
|
+
this.cache.clear();
|
|
240
|
+
return this.storage.clear();
|
|
241
|
+
}
|
|
242
|
+
}
|
package/dist/test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/test.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const utils_1 = require("@freesignal/utils");
|
|
16
|
+
const _1 = require(".");
|
|
17
|
+
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
18
|
+
console.log("FreeSignal protocol test");
|
|
19
|
+
const bob = (0, _1.createNode)({ keyExchange: new _1.AsyncMap(), sessions: new _1.AsyncMap(), users: new _1.AsyncMap(), bundles: new _1.AsyncMap() });
|
|
20
|
+
const alice = (0, _1.createNode)({ keyExchange: new _1.AsyncMap(), sessions: new _1.AsyncMap(), users: new _1.AsyncMap(), bundles: new _1.AsyncMap() });
|
|
21
|
+
setImmediate(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
const bobBootstrap = yield bob.packBootstrap(alice.userId);
|
|
23
|
+
yield alice.open(bobBootstrap);
|
|
24
|
+
const bootstraps = yield Promise.all(alice.requests.map(request => request.accept()));
|
|
25
|
+
const aliceHandshake = bootstraps.filter(value => (value === null || value === void 0 ? void 0 : value.receiver) === bob.userId.toString())[0];
|
|
26
|
+
if (!aliceHandshake)
|
|
27
|
+
throw new Error("Bootstrap Failed");
|
|
28
|
+
yield bob.open(aliceHandshake);
|
|
29
|
+
const first = (yield bob.packData(alice.userId, "Hi Alice!")).toBytes();
|
|
30
|
+
console.log("Bob: ", yield alice.open(first));
|
|
31
|
+
const second = yield alice.packData(bob.userId, "Hi Bob!");
|
|
32
|
+
console.log("Alice: ", yield bob.open(second));
|
|
33
|
+
const third = yield Promise.all(["How are you?", "How are this days?", "For me it's a good time"].map(msg => bob.packData(alice.userId, msg)));
|
|
34
|
+
third.forEach((data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
|
+
console.log("Bob: ", yield alice.open(data));
|
|
36
|
+
}));
|
|
37
|
+
const fourth = yield alice.packData(bob.userId, "Not so bad my man");
|
|
38
|
+
console.log("Alice: ", yield bob.open(fourth));
|
|
39
|
+
const testone = yield Promise.all(Array(400).fill(0).map(() => alice.packData(bob.userId, (0, utils_1.decodeBase64)(crypto_1.default.randomBytes(64)))));
|
|
40
|
+
console.log((yield bob.open(testone[350])));
|
|
41
|
+
}));
|
|
@@ -16,13 +16,13 @@
|
|
|
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, Encodable } from "@freesignal/interfaces";
|
|
20
|
-
export declare class UserId {
|
|
19
|
+
import { LocalStorage, Encodable, KeyExchangeData } from "@freesignal/interfaces";
|
|
20
|
+
export declare class UserId implements Encodable {
|
|
21
21
|
private readonly array;
|
|
22
22
|
private constructor();
|
|
23
23
|
toString(): string;
|
|
24
24
|
toJSON(): string;
|
|
25
|
-
|
|
25
|
+
toBytes(): Uint8Array;
|
|
26
26
|
static fromKey(identityKey: string | Uint8Array | IdentityKey): UserId;
|
|
27
27
|
static from(userId: string | Uint8Array | UserId): UserId;
|
|
28
28
|
}
|
|
@@ -51,12 +51,22 @@ export declare namespace PrivateIdentityKey {
|
|
|
51
51
|
function from(identityKey: PrivateIdentityKey | Uint8Array | string): PrivateIdentityKey;
|
|
52
52
|
function from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): PrivateIdentityKey;
|
|
53
53
|
}
|
|
54
|
+
export declare enum DiscoverType {
|
|
55
|
+
REQUEST = 0,
|
|
56
|
+
RESPONSE = 1
|
|
57
|
+
}
|
|
58
|
+
export interface DiscoverMessage {
|
|
59
|
+
type: DiscoverType;
|
|
60
|
+
discoverId: string;
|
|
61
|
+
data?: KeyExchangeData;
|
|
62
|
+
}
|
|
54
63
|
export declare enum Protocols {
|
|
55
64
|
NULL = "",
|
|
56
65
|
MESSAGE = "/freesignal/message",
|
|
57
66
|
RELAY = "/freesignal/relay",
|
|
58
67
|
HANDSHAKE = "/freesignal/handshake",
|
|
59
|
-
DISCOVER = "/freesignal/discover"
|
|
68
|
+
DISCOVER = "/freesignal/discover",
|
|
69
|
+
BOOTSTRAP = "/freesignal/bootstrap"
|
|
60
70
|
}
|
|
61
71
|
export declare namespace Protocols {
|
|
62
72
|
function isProtocol(protocol: any): boolean;
|
|
@@ -65,6 +75,19 @@ export declare namespace Protocols {
|
|
|
65
75
|
function encode(protocol: Protocols, length?: number): Uint8Array;
|
|
66
76
|
function decode(array: Uint8Array): Protocols;
|
|
67
77
|
}
|
|
78
|
+
interface DatagramJSON {
|
|
79
|
+
id: string;
|
|
80
|
+
version: number;
|
|
81
|
+
sender: string;
|
|
82
|
+
receiver: string;
|
|
83
|
+
protocol: Protocols;
|
|
84
|
+
createdAt: number;
|
|
85
|
+
payload: string | undefined;
|
|
86
|
+
signature: string | undefined;
|
|
87
|
+
}
|
|
88
|
+
export interface SignedDatagram extends Datagram {
|
|
89
|
+
signature: string;
|
|
90
|
+
}
|
|
68
91
|
export declare class Datagram implements Encodable {
|
|
69
92
|
static version: number;
|
|
70
93
|
private _id;
|
|
@@ -75,20 +98,20 @@ export declare class Datagram implements Encodable {
|
|
|
75
98
|
private _createdAt;
|
|
76
99
|
private _payload?;
|
|
77
100
|
private _signature?;
|
|
78
|
-
private secretKey?;
|
|
79
101
|
private static headerOffset;
|
|
80
102
|
constructor(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable);
|
|
81
103
|
get id(): string;
|
|
82
104
|
get version(): number;
|
|
83
105
|
get createdAt(): number;
|
|
84
|
-
get signed(): boolean;
|
|
85
|
-
get signature(): string | undefined;
|
|
86
106
|
set payload(data: Uint8Array);
|
|
87
107
|
get payload(): Uint8Array | undefined;
|
|
108
|
+
get signature(): string | undefined;
|
|
109
|
+
private get unsigned();
|
|
88
110
|
toBytes(): Uint8Array;
|
|
89
|
-
sign(secretKey: Uint8Array):
|
|
111
|
+
sign(secretKey: Uint8Array): SignedDatagram;
|
|
90
112
|
toString(): string;
|
|
91
|
-
toJSON():
|
|
113
|
+
toJSON(): DatagramJSON;
|
|
114
|
+
static verify(datagram: Datagram, publicKey: Uint8Array): boolean;
|
|
92
115
|
static from(data: Uint8Array | Datagram | string): Datagram;
|
|
93
116
|
}
|
|
94
117
|
/**
|
|
@@ -163,3 +186,4 @@ export declare class AsyncMap<K, V> implements LocalStorage<K, V> {
|
|
|
163
186
|
clear(): Promise<void>;
|
|
164
187
|
entries(): ArrayIterator<[K, V]>;
|
|
165
188
|
}
|
|
189
|
+
export {};
|
|
@@ -30,7 +30,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
30
30
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
31
31
|
};
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.AsyncMap = exports.EncryptedData = exports.Datagram = exports.Protocols = exports.PrivateIdentityKey = exports.IdentityKey = exports.UserId = void 0;
|
|
33
|
+
exports.AsyncMap = exports.EncryptedData = exports.Datagram = exports.Protocols = exports.DiscoverType = exports.PrivateIdentityKey = exports.IdentityKey = exports.UserId = void 0;
|
|
34
34
|
const utils_1 = require("@freesignal/utils");
|
|
35
35
|
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
36
36
|
const double_ratchet_1 = require("./double-ratchet");
|
|
@@ -45,7 +45,7 @@ class UserId {
|
|
|
45
45
|
toJSON() {
|
|
46
46
|
return this.toString();
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
toBytes() {
|
|
49
49
|
return this.array;
|
|
50
50
|
}
|
|
51
51
|
static fromKey(identityKey) {
|
|
@@ -88,7 +88,7 @@ var IdentityKey;
|
|
|
88
88
|
return UserId.fromKey(this.toBytes()).toString();
|
|
89
89
|
}
|
|
90
90
|
toBytes() {
|
|
91
|
-
return (0, utils_1.
|
|
91
|
+
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
92
92
|
}
|
|
93
93
|
toString() {
|
|
94
94
|
return (0, utils_1.decodeBase64)(this.toBytes());
|
|
@@ -110,7 +110,7 @@ var IdentityKey;
|
|
|
110
110
|
else
|
|
111
111
|
return key;
|
|
112
112
|
});
|
|
113
|
-
return new IdentityKeyConstructor(keys.length === 2 ? (0, utils_1.
|
|
113
|
+
return new IdentityKeyConstructor(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(info + IdentityKey.version, 1), ...keys) : keys[0]);
|
|
114
114
|
}
|
|
115
115
|
IdentityKey.from = from;
|
|
116
116
|
})(IdentityKey || (exports.IdentityKey = IdentityKey = {}));
|
|
@@ -121,8 +121,8 @@ var PrivateIdentityKey;
|
|
|
121
121
|
PrivateIdentityKey.version = 1;
|
|
122
122
|
class PrivateIdentityKeyConstructor {
|
|
123
123
|
constructor(privateIdentityKey) {
|
|
124
|
-
this.info = info + PrivateIdentityKey.version;
|
|
125
124
|
if (privateIdentityKey instanceof PrivateIdentityKeyConstructor) {
|
|
125
|
+
this.info = privateIdentityKey.info;
|
|
126
126
|
this.signatureKey = privateIdentityKey.signatureKey;
|
|
127
127
|
this.exchangeKey = privateIdentityKey.exchangeKey;
|
|
128
128
|
this.identityKey = privateIdentityKey.identityKey;
|
|
@@ -132,6 +132,7 @@ var PrivateIdentityKey;
|
|
|
132
132
|
privateIdentityKey = (0, utils_1.encodeBase64)(privateIdentityKey);
|
|
133
133
|
if (!isIdentityKeys(privateIdentityKey))
|
|
134
134
|
throw new Error("Invalid key length");
|
|
135
|
+
this.info = privateIdentityKey[0];
|
|
135
136
|
this.signatureKey = privateIdentityKey.subarray(1, crypto_1.default.EdDSA.secretKeyLength + 1);
|
|
136
137
|
this.exchangeKey = privateIdentityKey.subarray(crypto_1.default.EdDSA.secretKeyLength + 1, PrivateIdentityKey.keyLength);
|
|
137
138
|
this.identityKey = IdentityKey.from(crypto_1.default.EdDSA.keyPair(this.signatureKey).publicKey, crypto_1.default.ECDH.keyPair(this.exchangeKey).publicKey);
|
|
@@ -141,7 +142,7 @@ var PrivateIdentityKey;
|
|
|
141
142
|
return UserId.fromKey(this.identityKey.toBytes()).toString();
|
|
142
143
|
}
|
|
143
144
|
toBytes() {
|
|
144
|
-
return (0, utils_1.
|
|
145
|
+
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
145
146
|
}
|
|
146
147
|
toString() {
|
|
147
148
|
return (0, utils_1.decodeBase64)(this.toBytes());
|
|
@@ -163,10 +164,15 @@ var PrivateIdentityKey;
|
|
|
163
164
|
else
|
|
164
165
|
return key;
|
|
165
166
|
});
|
|
166
|
-
return new PrivateIdentityKeyConstructor(keys.length === 2 ? (0, utils_1.
|
|
167
|
+
return new PrivateIdentityKeyConstructor(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(info + PrivateIdentityKey.version, 1), ...keys) : keys[0]);
|
|
167
168
|
}
|
|
168
169
|
PrivateIdentityKey.from = from;
|
|
169
170
|
})(PrivateIdentityKey || (exports.PrivateIdentityKey = PrivateIdentityKey = {}));
|
|
171
|
+
var DiscoverType;
|
|
172
|
+
(function (DiscoverType) {
|
|
173
|
+
DiscoverType[DiscoverType["REQUEST"] = 0] = "REQUEST";
|
|
174
|
+
DiscoverType[DiscoverType["RESPONSE"] = 1] = "RESPONSE";
|
|
175
|
+
})(DiscoverType || (exports.DiscoverType = DiscoverType = {}));
|
|
170
176
|
var Protocols;
|
|
171
177
|
(function (Protocols) {
|
|
172
178
|
Protocols["NULL"] = "";
|
|
@@ -174,6 +180,7 @@ var Protocols;
|
|
|
174
180
|
Protocols["RELAY"] = "/freesignal/relay";
|
|
175
181
|
Protocols["HANDSHAKE"] = "/freesignal/handshake";
|
|
176
182
|
Protocols["DISCOVER"] = "/freesignal/discover";
|
|
183
|
+
Protocols["BOOTSTRAP"] = "/freesignal/bootstrap";
|
|
177
184
|
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
178
185
|
(function (Protocols) {
|
|
179
186
|
function isProtocol(protocol) {
|
|
@@ -188,15 +195,16 @@ var Protocols;
|
|
|
188
195
|
return Object.values(Protocols).indexOf(protocol);
|
|
189
196
|
}
|
|
190
197
|
Protocols.toCode = toCode;
|
|
191
|
-
function encode(protocol, length) {
|
|
192
|
-
return (0, utils_1.
|
|
198
|
+
function encode(protocol, length = 1) {
|
|
199
|
+
return (0, utils_1.numberToBytes)(Protocols.toCode(protocol), length);
|
|
193
200
|
}
|
|
194
201
|
Protocols.encode = encode;
|
|
195
202
|
function decode(array) {
|
|
196
|
-
return Protocols.fromCode((0, utils_1.
|
|
203
|
+
return Protocols.fromCode((0, utils_1.bytesToNumber)(array));
|
|
197
204
|
}
|
|
198
205
|
Protocols.decode = decode;
|
|
199
206
|
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
207
|
+
;
|
|
200
208
|
class Datagram {
|
|
201
209
|
constructor(sender, receiver, protocol, payload) {
|
|
202
210
|
this._id = crypto_1.default.UUID.generate().toString();
|
|
@@ -216,16 +224,6 @@ class Datagram {
|
|
|
216
224
|
get createdAt() {
|
|
217
225
|
return this._createdAt;
|
|
218
226
|
}
|
|
219
|
-
get signed() {
|
|
220
|
-
return !!this._signature || !!this.secretKey;
|
|
221
|
-
}
|
|
222
|
-
get signature() {
|
|
223
|
-
if (this.signed) {
|
|
224
|
-
if (!this._signature)
|
|
225
|
-
this.toBytes();
|
|
226
|
-
return (0, utils_1.decodeBase64)(this._signature);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
227
|
set payload(data) {
|
|
230
228
|
this._signature = undefined;
|
|
231
229
|
this._payload = data;
|
|
@@ -233,37 +231,56 @@ class Datagram {
|
|
|
233
231
|
get payload() {
|
|
234
232
|
return this._payload;
|
|
235
233
|
}
|
|
234
|
+
get signature() {
|
|
235
|
+
return this._signature ? (0, utils_1.decodeBase64)(this._signature) : undefined;
|
|
236
|
+
}
|
|
237
|
+
get unsigned() {
|
|
238
|
+
const data = this.toBytes();
|
|
239
|
+
data[0] &= 127;
|
|
240
|
+
return data.subarray(0, data.length - (this._signature ? crypto_1.default.EdDSA.signatureLength : 0));
|
|
241
|
+
}
|
|
236
242
|
toBytes() {
|
|
237
243
|
var _a, _b, _c;
|
|
238
|
-
|
|
244
|
+
return (0, utils_1.concatBytes)(new Uint8Array(1).fill(this.version | (this.signature ? 128 : 0)), //1
|
|
239
245
|
Protocols.encode(this.protocol), //1
|
|
240
246
|
(_a = crypto_1.default.UUID.parse(this.id)) !== null && _a !== void 0 ? _a : [], //16
|
|
241
|
-
(0, utils_1.
|
|
247
|
+
new Uint8Array((0, utils_1.numberToBytes)(this._createdAt, 8)), //8
|
|
242
248
|
(0, utils_1.encodeBase64)(this.sender), //32
|
|
243
249
|
(0, utils_1.encodeBase64)(this.receiver), //32
|
|
244
|
-
(_b = this._payload) !== null && _b !== void 0 ? _b : new Uint8Array());
|
|
245
|
-
if (this.secretKey)
|
|
246
|
-
this._signature = crypto_1.default.EdDSA.sign(data, this.secretKey);
|
|
247
|
-
return (0, utils_1.concatArrays)(data, (_c = this._signature) !== null && _c !== void 0 ? _c : new Uint8Array());
|
|
250
|
+
(_b = this._payload) !== null && _b !== void 0 ? _b : new Uint8Array(), (_c = this._signature) !== null && _c !== void 0 ? _c : new Uint8Array());
|
|
248
251
|
}
|
|
249
252
|
sign(secretKey) {
|
|
250
|
-
this.
|
|
253
|
+
this._signature = crypto_1.default.EdDSA.sign(this.unsigned, secretKey);
|
|
251
254
|
return this;
|
|
252
255
|
}
|
|
253
256
|
toString() {
|
|
254
257
|
return (0, utils_1.decodeBase64)(this.toBytes());
|
|
255
258
|
}
|
|
256
259
|
toJSON() {
|
|
257
|
-
return
|
|
260
|
+
return {
|
|
261
|
+
id: this.id,
|
|
262
|
+
version: this.version,
|
|
263
|
+
sender: this.sender,
|
|
264
|
+
receiver: this.receiver,
|
|
265
|
+
protocol: this.protocol,
|
|
266
|
+
createdAt: this.createdAt,
|
|
267
|
+
payload: this.payload ? (0, utils_1.decodeBase64)(this.payload) : undefined,
|
|
268
|
+
signature: this._signature ? (0, utils_1.decodeBase64)(this._signature) : undefined
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
static verify(datagram, publicKey) {
|
|
272
|
+
if (!datagram._signature)
|
|
273
|
+
throw new Error("Datagram not signed");
|
|
274
|
+
return crypto_1.default.EdDSA.verify(datagram.unsigned, datagram._signature, publicKey);
|
|
258
275
|
}
|
|
259
276
|
static from(data) {
|
|
260
277
|
if (typeof data === 'string')
|
|
261
278
|
data = (0, utils_1.encodeBase64)(data);
|
|
262
279
|
if (data instanceof Uint8Array) {
|
|
263
|
-
const datagram = new Datagram((0, utils_1.decodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength)), (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, Datagram.headerOffset)), Protocols.decode(data.subarray(1, 2)), data.subarray(Datagram.headerOffset, data.length));
|
|
280
|
+
const datagram = new Datagram((0, utils_1.decodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength)), (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, Datagram.headerOffset)), Protocols.decode(data.subarray(1, 2)), data.subarray(Datagram.headerOffset, data.length - (data[0] & 128 ? crypto_1.default.EdDSA.signatureLength : 0)));
|
|
264
281
|
datagram._version = data[0] & 127;
|
|
265
282
|
datagram._id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
|
|
266
|
-
datagram._createdAt = (0, utils_1.
|
|
283
|
+
datagram._createdAt = (0, utils_1.bytesToNumber)(data.subarray(18, 26));
|
|
267
284
|
if (data[0] & 128)
|
|
268
285
|
datagram._signature = data.subarray(data.length - crypto_1.default.EdDSA.signatureLength);
|
|
269
286
|
return datagram;
|
|
@@ -272,8 +289,8 @@ class Datagram {
|
|
|
272
289
|
const datagram = new Datagram(data.sender, data.receiver, data.protocol, data.payload);
|
|
273
290
|
datagram._id = datagram.id;
|
|
274
291
|
datagram._version = datagram.version;
|
|
275
|
-
datagram._createdAt = datagram.
|
|
276
|
-
datagram._signature = datagram.
|
|
292
|
+
datagram._createdAt = datagram._createdAt;
|
|
293
|
+
datagram._signature = datagram._signature;
|
|
277
294
|
return datagram;
|
|
278
295
|
}
|
|
279
296
|
else
|
|
@@ -39,7 +39,7 @@ export declare class KeyExchange {
|
|
|
39
39
|
private generateOPK;
|
|
40
40
|
generateBundle(length?: number): Promise<KeyExchangeDataBundle>;
|
|
41
41
|
generateData(): Promise<KeyExchangeData>;
|
|
42
|
-
digestData(message: KeyExchangeData): Promise<{
|
|
42
|
+
digestData(message: KeyExchangeData, associatedData?: Uint8Array): Promise<{
|
|
43
43
|
session: KeySession;
|
|
44
44
|
message: KeyExchangeSynMessage;
|
|
45
45
|
identityKey: IdentityKey;
|
|
@@ -47,5 +47,6 @@ export declare class KeyExchange {
|
|
|
47
47
|
digestMessage(message: KeyExchangeSynMessage): Promise<{
|
|
48
48
|
session: KeySession;
|
|
49
49
|
identityKey: IdentityKey;
|
|
50
|
+
associatedData: Uint8Array;
|
|
50
51
|
}>;
|
|
51
52
|
}
|
package/{x3dh.js → dist/x3dh.js}
RENAMED
|
@@ -66,7 +66,7 @@ class KeyExchange {
|
|
|
66
66
|
identityKey: this.identityKey.toString(),
|
|
67
67
|
signedPreKey: (0, utils_1.decodeBase64)(signedPreKey.publicKey),
|
|
68
68
|
signature: (0, utils_1.decodeBase64)(crypto_1.default.EdDSA.sign(signedPreKeyHash, this.privateIdentityKey.signatureKey)),
|
|
69
|
-
|
|
69
|
+
onetimePreKeys: onetimePreKey.map(opk => (0, utils_1.decodeBase64)(opk.publicKey))
|
|
70
70
|
};
|
|
71
71
|
});
|
|
72
72
|
}
|
|
@@ -83,7 +83,7 @@ class KeyExchange {
|
|
|
83
83
|
};
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
|
-
digestData(message) {
|
|
86
|
+
digestData(message, associatedData) {
|
|
87
87
|
return __awaiter(this, void 0, void 0, function* () {
|
|
88
88
|
const ephemeralKey = crypto_1.default.ECDH.keyPair();
|
|
89
89
|
const signedPreKey = (0, utils_1.encodeBase64)(message.signedPreKey);
|
|
@@ -100,7 +100,7 @@ class KeyExchange {
|
|
|
100
100
|
...onetimePreKey ? crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
|
|
101
101
|
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength);
|
|
102
102
|
const session = new double_ratchet_1.KeySession(this.sessions, { remoteKey: identityKey.exchangeKey, rootKey });
|
|
103
|
-
const cyphertext = yield session.encrypt((0, utils_1.
|
|
103
|
+
const cyphertext = yield session.encrypt((0, utils_1.concatBytes)(crypto_1.default.hash(this.identityKey.toBytes()), crypto_1.default.hash(identityKey.toBytes()), associatedData !== null && associatedData !== void 0 ? associatedData : new Uint8Array()));
|
|
104
104
|
if (!cyphertext)
|
|
105
105
|
throw new Error("Decryption error");
|
|
106
106
|
return {
|
|
@@ -138,11 +138,12 @@ class KeyExchange {
|
|
|
138
138
|
const cleartext = yield session.decrypt((0, utils_1.encodeBase64)(message.associatedData));
|
|
139
139
|
if (!cleartext)
|
|
140
140
|
throw new Error("Error decrypting ACK message");
|
|
141
|
-
if (!(0, utils_1.
|
|
141
|
+
if (!(0, utils_1.compareBytes)(cleartext.subarray(0, 64), (0, utils_1.concatBytes)(crypto_1.default.hash(identityKey.toBytes()), crypto_1.default.hash(this.identityKey.toBytes()))))
|
|
142
142
|
throw new Error("Error verifing Associated Data");
|
|
143
143
|
return {
|
|
144
144
|
session,
|
|
145
|
-
identityKey
|
|
145
|
+
identityKey,
|
|
146
|
+
associatedData: cleartext.subarray(64)
|
|
146
147
|
};
|
|
147
148
|
});
|
|
148
149
|
}
|
package/package.json
CHANGED
|
@@ -1,22 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freesignal/protocol",
|
|
3
|
-
"version": "0.3.
|
|
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",
|
|
17
40
|
"semaphore.ts": "^0.2.0"
|
|
18
41
|
},
|
|
19
42
|
"devDependencies": {
|
|
20
43
|
"@types/node": "^24.2.1"
|
|
21
44
|
}
|
|
22
|
-
}
|
|
45
|
+
}
|
package/node.d.ts
DELETED
|
@@ -1,38 +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: SessionMap;
|
|
8
|
-
protected readonly users: LocalStorage<string, IdentityKey>;
|
|
9
|
-
protected readonly keyExchange: KeyExchange;
|
|
10
|
-
constructor(storage: Database<{
|
|
11
|
-
sessions: LocalStorage<string, ExportedKeySession>;
|
|
12
|
-
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
13
|
-
users: LocalStorage<string, IdentityKey>;
|
|
14
|
-
}>, privateIdentityKey?: PrivateIdentityKey);
|
|
15
|
-
get userId(): UserId;
|
|
16
|
-
get identityKey(): IdentityKey;
|
|
17
|
-
generateKeyData(): Promise<KeyExchangeData>;
|
|
18
|
-
generateKeyBundle(length?: number): Promise<KeyExchangeDataBundle>;
|
|
19
|
-
encrypt(receiverId: string, protocol: Protocols, data: Uint8Array): Promise<Datagram>;
|
|
20
|
-
sendHandshake(data: KeyExchangeData): Promise<Datagram>;
|
|
21
|
-
sendData<T>(receiverId: string, data: T): Promise<Datagram>;
|
|
22
|
-
sendRelay(receiverId: string, data: Datagram): Promise<Datagram>;
|
|
23
|
-
sendDiscover(receiverId: string, discoverId: string): Promise<Datagram>;
|
|
24
|
-
decrypt(datagram: Datagram): Promise<Uint8Array>;
|
|
25
|
-
receive<T extends Uint8Array | UserId | Datagram | UserId | void>(datagram: Datagram | Uint8Array): Promise<T>;
|
|
26
|
-
}
|
|
27
|
-
declare class SessionMap implements LocalStorage<string, KeySession> {
|
|
28
|
-
readonly storage: LocalStorage<string, ExportedKeySession>;
|
|
29
|
-
readonly maxSize: number;
|
|
30
|
-
private readonly cache;
|
|
31
|
-
constructor(storage: LocalStorage<string, ExportedKeySession>, maxSize?: number);
|
|
32
|
-
set(key: string, value: KeySession): Promise<void>;
|
|
33
|
-
get(key: string): Promise<KeySession | undefined>;
|
|
34
|
-
has(key: string): Promise<boolean>;
|
|
35
|
-
delete(key: string): Promise<boolean>;
|
|
36
|
-
clear(): Promise<void>;
|
|
37
|
-
}
|
|
38
|
-
export {};
|
package/node.js
DELETED
|
@@ -1,139 +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 double_ratchet_1 = require("./double-ratchet");
|
|
16
|
-
const _1 = require(".");
|
|
17
|
-
const utils_1 = require("@freesignal/utils");
|
|
18
|
-
class FreeSignalNode {
|
|
19
|
-
constructor(storage, privateIdentityKey) {
|
|
20
|
-
this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : (0, _1.createIdentity)();
|
|
21
|
-
this.sessions = new SessionMap(storage.sessions);
|
|
22
|
-
this.keyExchange = new x3dh_1.KeyExchange({ keys: storage.keyExchange, sessions: storage.sessions }, this.privateIdentityKey);
|
|
23
|
-
this.users = storage.users;
|
|
24
|
-
}
|
|
25
|
-
get userId() {
|
|
26
|
-
return types_1.UserId.fromKey(this.privateIdentityKey.identityKey);
|
|
27
|
-
}
|
|
28
|
-
get identityKey() {
|
|
29
|
-
return this.privateIdentityKey.identityKey;
|
|
30
|
-
}
|
|
31
|
-
generateKeyData() {
|
|
32
|
-
return this.keyExchange.generateData();
|
|
33
|
-
}
|
|
34
|
-
;
|
|
35
|
-
generateKeyBundle(length) {
|
|
36
|
-
return this.keyExchange.generateBundle(length);
|
|
37
|
-
}
|
|
38
|
-
;
|
|
39
|
-
encrypt(receiverId, protocol, data) {
|
|
40
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
-
const session = yield this.sessions.get(receiverId);
|
|
42
|
-
if (!session)
|
|
43
|
-
throw new Error("Session not found for user: " + receiverId);
|
|
44
|
-
return new types_1.Datagram(this.userId.toString(), receiverId, protocol, yield session.encrypt(data));
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
sendHandshake(data) {
|
|
48
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
-
const { session, message, identityKey } = yield this.keyExchange.digestData(data);
|
|
50
|
-
const remoteId = types_1.UserId.fromKey(identityKey);
|
|
51
|
-
this.sessions.set(remoteId.toString(), session);
|
|
52
|
-
return new types_1.Datagram(this.userId.toString(), types_1.UserId.fromKey(data.identityKey).toString(), types_1.Protocols.HANDSHAKE, (0, utils_1.encodeData)(message));
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
sendData(receiverId, data) {
|
|
56
|
-
return this.encrypt(receiverId, types_1.Protocols.MESSAGE, (0, utils_1.encodeData)(data));
|
|
57
|
-
}
|
|
58
|
-
sendRelay(receiverId, data) {
|
|
59
|
-
return this.encrypt(receiverId, types_1.Protocols.RELAY, (0, utils_1.encodeData)(data));
|
|
60
|
-
}
|
|
61
|
-
sendDiscover(receiverId, discoverId) {
|
|
62
|
-
return this.encrypt(receiverId, types_1.Protocols.DISCOVER, (0, utils_1.encodeData)(discoverId));
|
|
63
|
-
}
|
|
64
|
-
decrypt(datagram) {
|
|
65
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
-
const userId = datagram.sender;
|
|
67
|
-
const session = yield this.sessions.get(userId);
|
|
68
|
-
if (!session)
|
|
69
|
-
throw new Error("Session not found for user: " + userId);
|
|
70
|
-
if (!datagram.payload)
|
|
71
|
-
throw new Error("Missing payload");
|
|
72
|
-
const decrypted = yield session.decrypt(datagram.payload);
|
|
73
|
-
if (!decrypted)
|
|
74
|
-
throw new Error("Decryption failed");
|
|
75
|
-
return decrypted;
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
receive(datagram) {
|
|
79
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
80
|
-
if (datagram instanceof Uint8Array)
|
|
81
|
-
datagram = types_1.Datagram.from(datagram);
|
|
82
|
-
switch (datagram.protocol) {
|
|
83
|
-
case types_1.Protocols.HANDSHAKE:
|
|
84
|
-
if (!datagram.payload)
|
|
85
|
-
throw new Error("Missing payload");
|
|
86
|
-
const data = (0, utils_1.decodeData)(datagram.payload);
|
|
87
|
-
const { session, identityKey } = yield this.keyExchange.digestMessage(data);
|
|
88
|
-
this.sessions.set(types_1.UserId.fromKey(identityKey).toString(), session);
|
|
89
|
-
return;
|
|
90
|
-
case types_1.Protocols.MESSAGE:
|
|
91
|
-
return yield this.decrypt(datagram);
|
|
92
|
-
case types_1.Protocols.RELAY:
|
|
93
|
-
return (0, utils_1.decodeData)(yield this.decrypt(datagram));
|
|
94
|
-
case types_1.Protocols.DISCOVER:
|
|
95
|
-
return types_1.UserId.from((0, utils_1.decodeData)(yield this.decrypt(datagram)));
|
|
96
|
-
default:
|
|
97
|
-
throw new Error("Invalid protocol");
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
exports.FreeSignalNode = FreeSignalNode;
|
|
103
|
-
class SessionMap {
|
|
104
|
-
constructor(storage, maxSize = 50) {
|
|
105
|
-
this.storage = storage;
|
|
106
|
-
this.maxSize = maxSize;
|
|
107
|
-
this.cache = new Map();
|
|
108
|
-
}
|
|
109
|
-
set(key, value) {
|
|
110
|
-
this.cache.set(key, value);
|
|
111
|
-
return this.storage.set(key, value.toJSON());
|
|
112
|
-
}
|
|
113
|
-
get(key) {
|
|
114
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
115
|
-
const session = this.cache.get(key);
|
|
116
|
-
if (!session) {
|
|
117
|
-
const sessionData = yield this.storage.get(key);
|
|
118
|
-
if (!sessionData)
|
|
119
|
-
return undefined;
|
|
120
|
-
return double_ratchet_1.KeySession.from(sessionData, this.storage);
|
|
121
|
-
}
|
|
122
|
-
return session;
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
has(key) {
|
|
126
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
127
|
-
return this.cache.has(key) || (yield this.storage.has(key));
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
delete(key) {
|
|
131
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
132
|
-
return this.cache.delete(key) || (yield this.storage.delete(key));
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
clear() {
|
|
136
|
-
this.cache.clear();
|
|
137
|
-
return this.storage.clear();
|
|
138
|
-
}
|
|
139
|
-
}
|
package/test.js
DELETED
|
@@ -1,28 +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 bob = (0, _1.createNode)({ keyExchange: new _1.AsyncMap(), sessions: new _1.AsyncMap(), users: new _1.AsyncMap() });
|
|
15
|
-
const alice = (0, _1.createNode)({ keyExchange: new _1.AsyncMap(), sessions: new _1.AsyncMap(), users: new _1.AsyncMap() });
|
|
16
|
-
setImmediate(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
|
-
const aliceHandshake = yield alice.sendHandshake(yield bob.generateKeyData());
|
|
18
|
-
yield bob.receive(aliceHandshake);
|
|
19
|
-
console.log("Session established successfully between Alice and Bob.");
|
|
20
|
-
const first = (yield bob.sendData(alice.userId.toString(), (0, utils_1.encodeData)("Hi Alice!"))).toBytes();
|
|
21
|
-
console.log("Bob: ", (0, utils_1.decodeData)(yield alice.receive(first)));
|
|
22
|
-
const second = yield alice.sendData(bob.userId.toString(), (0, utils_1.encodeData)("Hi Bob!"));
|
|
23
|
-
console.log("Alice: ", (0, utils_1.decodeData)(yield bob.receive(second)));
|
|
24
|
-
const third = yield Promise.all(["How are you?", "How are this days?", "For me it's a good time"].map(msg => bob.sendData(alice.userId.toString(), (0, utils_1.encodeData)(msg))));
|
|
25
|
-
third.forEach((value) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
-
console.log("Bob: ", (0, utils_1.decodeData)(yield alice.receive(value)));
|
|
27
|
-
}));
|
|
28
|
-
}));
|
|
File without changes
|
|
File without changes
|