@freesignal/protocol 0.8.2 → 0.9.0

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.
@@ -21,8 +21,8 @@ export interface ExportedKeySession {
21
21
  identityKey: string;
22
22
  secretKey: string;
23
23
  rootKey: string;
24
- sendingChain?: ExportedKeyChain;
25
- receivingChain?: ExportedKeyChain;
24
+ sendingChain?: KeyChainState;
25
+ receivingChain?: KeyChainState;
26
26
  headerKey?: string;
27
27
  headerKeys: [string, Uint8Array][];
28
28
  previousKeys: [string, Uint8Array][];
@@ -87,7 +87,7 @@ export declare class KeySession {
87
87
  */
88
88
  static from(data: ExportedKeySession): KeySession;
89
89
  }
90
- interface ExportedKeyChain {
90
+ interface KeyChainState {
91
91
  publicKey: string;
92
92
  remoteKey: string;
93
93
  chainKey: string;
package/dist/index.d.ts CHANGED
@@ -16,10 +16,8 @@
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, KeyExchangeDataBundle } from "@freesignal/interfaces";
20
- import { ExportedKeySession } from "./double-ratchet.js";
21
19
  import { PrivateIdentityKey } from "./types.js";
22
- import { BootstrapRequest, FreeSignalNode } from "./node.js";
20
+ import { FreeSignalNode } from "./node.js";
23
21
  /**
24
22
  * Generates identity key
25
23
  *
@@ -28,11 +26,5 @@ import { BootstrapRequest, FreeSignalNode } from "./node.js";
28
26
  */
29
27
  export declare function createIdentity(seed?: Uint8Array): PrivateIdentityKey;
30
28
  /** */
31
- export declare function createNode(storage: Database<{
32
- sessions: LocalStorage<string, ExportedKeySession>;
33
- users: LocalStorage<string, string>;
34
- keyExchange: LocalStorage<string, Crypto.KeyPair>;
35
- bundles: LocalStorage<string, KeyExchangeDataBundle>;
36
- bootstraps: LocalStorage<string, BootstrapRequest>;
37
- }>, privateIdentityKey?: PrivateIdentityKey): FreeSignalNode;
29
+ export declare function createNode(privateIdentityKey?: PrivateIdentityKey): FreeSignalNode;
38
30
  export * from "./types.js";
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ export function createIdentity(seed) {
35
35
  return PrivateIdentityKey.from(signatureKeyPair.secretKey, exchangeKeyPair.secretKey);
36
36
  }
37
37
  /** */
38
- export function createNode(storage, privateIdentityKey) {
39
- return new FreeSignalNode(storage, privateIdentityKey);
38
+ export function createNode(privateIdentityKey) {
39
+ return new FreeSignalNode(privateIdentityKey);
40
40
  }
41
41
  export * from "./types.js";
package/dist/node.d.ts CHANGED
@@ -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 { Database, LocalStorage, Crypto, KeyExchangeDataBundle, KeyExchangeData } from "@freesignal/interfaces";
19
+ import { KeyExchangeDataBundle, KeyExchangeData } from "@freesignal/interfaces";
20
20
  import { Datagram, EncryptedDatagram, IdentityKey, PrivateIdentityKey, Protocols, UserId } from "./types.js";
21
- import { KeyExchange } from "./x3dh.js";
22
- import { ExportedKeySession, KeySession } from "./double-ratchet.js";
21
+ import { KeyExchange, KeyExchangeState } from "./x3dh.js";
22
+ import { ExportedKeySession as KeySessionState, KeySession } from "./double-ratchet.js";
23
23
  import { EventEmitter, EventCallback } from "easyemitter.ts";
24
24
  export declare class BootstrapRequest extends EventEmitter<'change', BootstrapRequest> {
25
25
  #private;
@@ -50,22 +50,22 @@ export type MessageEventData = {
50
50
  session: KeySession;
51
51
  payload: Uint8Array;
52
52
  };
53
+ export type FreeSignalNodeState = {
54
+ privateIdentityKey: string;
55
+ sessions: Array<[string, KeySessionState]>;
56
+ users: Array<[string, string]>;
57
+ bundles: Array<[string, KeyExchangeDataBundle]>;
58
+ keyExchange: KeyExchangeState;
59
+ };
53
60
  export declare class FreeSignalNode {
54
61
  protected readonly privateIdentityKey: PrivateIdentityKey;
55
- protected readonly sessions: SessionMap;
56
- protected readonly users: LocalStorage<string, string>;
57
- protected readonly bundles: LocalStorage<string, KeyExchangeDataBundle>;
62
+ protected readonly sessions: Map<string, KeySession>;
63
+ protected readonly users: Map<string, string>;
64
+ protected readonly bundles: Map<string, KeyExchangeDataBundle>;
58
65
  protected readonly keyExchange: KeyExchange;
59
- protected readonly discovers: Set<string>;
60
- protected readonly bootstraps: LocalStorage<string, BootstrapRequest>;
61
- protected readonly emitter: EventEmitter<"message" | "send" | "handshake" | "bootstrap", NodeEventData>;
62
- constructor(storage: Database<{
63
- sessions: LocalStorage<string, ExportedKeySession>;
64
- users: LocalStorage<string, string>;
65
- keyExchange: LocalStorage<string, Crypto.KeyPair>;
66
- bundles: LocalStorage<string, KeyExchangeDataBundle>;
67
- bootstraps: LocalStorage<string, BootstrapRequest>;
68
- }>, privateIdentityKey?: PrivateIdentityKey);
66
+ protected readonly bootstraps: Map<string, BootstrapRequest>;
67
+ protected readonly emitter: EventEmitter<"error" | "debug" | "send" | "handshake" | "message" | "bootstrap", NodeEventData>;
68
+ constructor(privateIdentityKey?: PrivateIdentityKey);
69
69
  protected messageHandler: EventCallback<NodeEventData, typeof this.emitter>;
70
70
  protected sendHandler: EventCallback<NodeEventData, typeof this.emitter>;
71
71
  protected handshakeHandler: EventCallback<NodeEventData, typeof this.emitter>;
@@ -74,33 +74,24 @@ export declare class FreeSignalNode {
74
74
  onSend: (data: Uint8Array) => void;
75
75
  onHandshake: (userId: UserId) => void;
76
76
  onRequest: (request: BootstrapRequest) => void;
77
- getRequest(userId: string): Promise<BootstrapRequest | undefined>;
77
+ onError: (error: any) => void;
78
+ onDebug: (...args: any[]) => void;
79
+ private error;
80
+ private debug;
81
+ getRequest(userId: string): BootstrapRequest | undefined;
78
82
  waitHandshaked(userId: UserId | string, timeout?: number): Promise<void>;
79
83
  get identityKey(): IdentityKey;
80
84
  get userId(): UserId;
81
- protected encrypt(receiverId: string | UserId, protocol: Protocols, data: Uint8Array): Promise<SendEventData>;
82
- sendHandshake(data: KeyExchangeData): Promise<void>;
83
- sendHandshake(session: KeySession): Promise<void>;
84
- sendHandshake(userId: UserId | string): Promise<void>;
85
- sendData<T>(receiverId: string | UserId, data: T): Promise<void>;
86
- sendRelay(relayId: string | UserId, receiverId: string | UserId, data: Datagram): Promise<void>;
87
- sendDiscover(receiverId: string | UserId, discoverId: string | UserId): Promise<void>;
88
- packBootstrap(): Promise<Datagram>;
89
- sendBootstrap(receiverId: string | UserId): Promise<void>;
90
- protected decrypt(datagram: EncryptedDatagram | Datagram | Uint8Array): Promise<MessageEventData>;
91
- protected openHandshake(datagram: Datagram | EncryptedDatagram | Uint8Array): Promise<'syn' | 'ack'>;
92
- protected open(datagram: Datagram | EncryptedDatagram | Uint8Array): Promise<void>;
93
- }
94
- declare class SessionMap implements LocalStorage<string, KeySession> {
95
- readonly storage: LocalStorage<string, ExportedKeySession>;
96
- readonly maxSize: number;
97
- private readonly cache;
98
- constructor(storage: LocalStorage<string, ExportedKeySession>, maxSize?: number);
99
- set(key: string, value: KeySession): Promise<void>;
100
- get(key: string): Promise<KeySession | undefined>;
101
- has(key: string): Promise<boolean>;
102
- delete(key: string): Promise<boolean>;
103
- clear(): Promise<void>;
104
- entries(): Promise<Iterable<[string, KeySession]>>;
85
+ protected encrypt(receiverId: string | UserId, protocol: Protocols, data: Uint8Array): SendEventData;
86
+ sendHandshake(data: KeyExchangeData): void;
87
+ sendHandshake(session: KeySession): void;
88
+ sendHandshake(userId: UserId | string): void;
89
+ sendData<T>(receiverId: string | UserId, data: T): void;
90
+ sendRelay(relayId: string | UserId, receiverId: string | UserId, data: Datagram): void;
91
+ packBootstrap(): Datagram;
92
+ sendBootstrap(receiverId: string | UserId): void;
93
+ protected decrypt(datagram: EncryptedDatagram | Datagram | Uint8Array): MessageEventData;
94
+ protected openHandshake(datagram: Datagram | EncryptedDatagram | Uint8Array): 'syn' | 'ack';
95
+ protected open(datagram: Datagram | EncryptedDatagram | Uint8Array): void;
96
+ toJSON(): FreeSignalNodeState;
105
97
  }
106
- export {};
package/dist/node.js CHANGED
@@ -37,7 +37,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
37
37
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
38
38
  };
39
39
  var _BootstrapRequest_status;
40
- import { Datagram, decryptData, DiscoverType, encryptData, EncryptedDatagram, IdentityKey, Protocols, UserId } from "./types.js";
40
+ import { Datagram, decryptData, encryptData, EncryptedDatagram, IdentityKey, Protocols, UserId } from "./types.js";
41
41
  import { KeyExchange } from "./x3dh.js";
42
42
  import { KeySession } from "./double-ratchet.js";
43
43
  import { createIdentity } from "./index.js";
@@ -71,8 +71,11 @@ export class BootstrapRequest extends EventEmitter {
71
71
  }
72
72
  _BootstrapRequest_status = new WeakMap();
73
73
  export class FreeSignalNode {
74
- constructor(storage, privateIdentityKey) {
75
- this.discovers = new Set();
74
+ constructor(privateIdentityKey) {
75
+ this.sessions = new Map();
76
+ this.users = new Map();
77
+ this.bundles = new Map();
78
+ this.bootstraps = new Map();
76
79
  this.emitter = new EventEmitter();
77
80
  this.messageHandler = (data) => this.onMessage({ session: data.session, payload: data.payload });
78
81
  this.sendHandler = (data) => this.onSend(data.datagram.toBytes());
@@ -82,16 +85,22 @@ export class FreeSignalNode {
82
85
  this.onSend = () => { };
83
86
  this.onHandshake = () => { };
84
87
  this.onRequest = () => { };
88
+ this.onError = () => { };
89
+ this.onDebug = () => { };
85
90
  this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : createIdentity();
86
- this.sessions = new SessionMap(storage.sessions);
87
- this.users = storage.users;
88
- this.keyExchange = new KeyExchange(storage.keyExchange, this.privateIdentityKey);
89
- this.bundles = storage.bundles;
90
- this.bootstraps = storage.bootstraps;
91
+ this.keyExchange = new KeyExchange({ privateIdentityKey: this.privateIdentityKey });
91
92
  this.emitter.on('message', this.messageHandler);
92
93
  this.emitter.on('send', this.sendHandler);
93
94
  this.emitter.on('handshake', this.handshakeHandler);
94
95
  this.emitter.on('bootstrap', this.bootstrapHandler);
96
+ this.emitter.on('error', (e) => this.onError(e.error));
97
+ this.emitter.on('debug', (e) => this.onDebug(e.debug));
98
+ }
99
+ error(error) {
100
+ this.emitter.emit('error', { error });
101
+ }
102
+ debug(...args) {
103
+ this.emitter.emit('debug', { debug: args });
95
104
  }
96
105
  getRequest(userId) {
97
106
  return this.bootstraps.get(userId);
@@ -113,53 +122,57 @@ export class FreeSignalNode {
113
122
  return this.identityKey.userId;
114
123
  }
115
124
  encrypt(receiverId, protocol, data) {
116
- return __awaiter(this, void 0, void 0, function* () {
117
- const sessionTag = yield this.users.get(receiverId.toString());
125
+ try {
126
+ const sessionTag = this.users.get(receiverId.toString());
118
127
  if (!sessionTag)
119
128
  throw new Error("User not found: " + receiverId);
120
- const session = yield this.sessions.get(sessionTag);
129
+ const session = this.sessions.get(sessionTag);
121
130
  if (!session)
122
131
  throw new Error("Session not found for sessionTag: " + sessionTag);
123
132
  const encrypted = encryptData(session, data);
124
133
  this.sessions.set(receiverId.toString(), session);
125
134
  return { session, userId: UserId.from(receiverId), datagram: new EncryptedDatagram(protocol, session.sessionTag, encrypted).sign(this.privateIdentityKey.signatureKey) };
126
- });
135
+ }
136
+ catch (error) {
137
+ this.error(error);
138
+ throw error;
139
+ }
127
140
  }
128
141
  sendHandshake(data) {
129
- return __awaiter(this, void 0, void 0, function* () {
142
+ try {
130
143
  if (data instanceof UserId || typeof data === 'string') {
131
- const session = yield this.sessions.get(data.toString());
144
+ const session = this.sessions.get(data.toString());
132
145
  if (!session)
133
146
  throw new Error("Session not found for userId: " + data.toString());
134
147
  data = session;
135
148
  }
136
149
  if (data instanceof KeySession) {
137
150
  //console.debug("Sending Handshake Ack");
138
- const session = yield this.sessions.get(data.sessionTag);
151
+ const session = this.sessions.get(data.sessionTag);
139
152
  if (!session)
140
153
  throw new Error("Session not found for sessionTag: " + data.sessionTag);
141
- this.emitter.emit('send', yield this.encrypt(session.userId, Protocols.HANDSHAKE, crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, session.identityKey.exchangeKey)));
154
+ this.emitter.emit('send', this.encrypt(session.userId, Protocols.HANDSHAKE, crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, session.identityKey.exchangeKey)));
142
155
  return;
143
156
  }
144
- //console.debug("Sending Handshake Syn");
145
- const { session, message } = yield this.keyExchange.digestData(data, encodeData(yield this.keyExchange.generateBundle()));
146
- yield this.sessions.set(session.sessionTag, session);
147
- yield this.users.set(session.userId.toString(), session.sessionTag);
148
- const datagram = new Datagram(Protocols.HANDSHAKE, encodeData(message), session.sessionTag).sign(this.privateIdentityKey.signatureKey);
149
- this.emitter.emit('send', { session, datagram, userId: session.userId });
150
- });
157
+ }
158
+ catch (error) {
159
+ this.error(error);
160
+ throw error;
161
+ }
162
+ //console.debug("Sending Handshake Syn");
163
+ const { session, message } = this.keyExchange.digestData(data, encodeData(this.keyExchange.generateBundle()));
164
+ this.sessions.set(session.sessionTag, session);
165
+ this.users.set(session.userId.toString(), session.sessionTag);
166
+ const datagram = new Datagram(Protocols.HANDSHAKE, encodeData(message), session.sessionTag).sign(this.privateIdentityKey.signatureKey);
167
+ this.emitter.emit('send', { session, datagram, userId: session.userId });
151
168
  }
152
169
  sendData(receiverId, data) {
153
- return __awaiter(this, void 0, void 0, function* () {
154
- //console.debug("Sending Data");
155
- this.emitter.emit('send', yield this.encrypt(receiverId, Protocols.MESSAGE, encodeData(data)));
156
- });
170
+ //console.debug("Sending Data");
171
+ this.emitter.emit('send', this.encrypt(receiverId, Protocols.MESSAGE, encodeData(data)));
157
172
  }
158
173
  sendRelay(relayId, receiverId, data) {
159
- return __awaiter(this, void 0, void 0, function* () {
160
- //console.debug("Sending Relay");
161
- this.emitter.emit('send', yield this.encrypt(relayId, Protocols.RELAY, concatBytes(UserId.from(receiverId).toBytes(), data.toBytes())));
162
- });
174
+ //console.debug("Sending Relay");
175
+ this.emitter.emit('send', this.encrypt(relayId, Protocols.RELAY, concatBytes(UserId.from(receiverId).toBytes(), data.toBytes())));
163
176
  }
164
177
  /*public async sendPing(receiverId: string | UserId): Promise<void> {
165
178
  //console.debug("Sending Ping");
@@ -171,42 +184,43 @@ export class FreeSignalNode {
171
184
  throw new Error("Session not found for sessionTag: " + sessionTag);
172
185
  const datagram = new Datagram(Protocols.PING, undefined, session.sessionTag);
173
186
  this.emitter.emit('send', { session, datagram, userId: session.userId });
174
- }*/
175
- sendDiscover(receiverId, discoverId) {
176
- return __awaiter(this, void 0, void 0, function* () {
177
- //console.debug("Sending Discover");
178
- if (receiverId instanceof UserId)
179
- receiverId = receiverId.toString();
180
- if (discoverId instanceof UserId)
181
- discoverId = discoverId.toString();
182
- const message = {
183
- type: DiscoverType.REQUEST,
184
- discoverId
185
- };
186
- this.discovers.add(receiverId);
187
- this.emitter.emit('send', yield this.encrypt(receiverId, Protocols.DISCOVER, encodeData(message)));
188
- });
189
187
  }
188
+
189
+ public async sendDiscover(receiverId: string | UserId, discoverId: string | UserId): Promise<void> {
190
+ //console.debug("Sending Discover");
191
+ if (receiverId instanceof UserId)
192
+ receiverId = receiverId.toString();
193
+ if (discoverId instanceof UserId)
194
+ discoverId = discoverId.toString();
195
+ const message: DiscoverMessage = {
196
+ type: DiscoverType.REQUEST,
197
+ discoverId
198
+ };
199
+ this.discovers.add(receiverId);
200
+ this.emitter.emit('send', await this.encrypt(receiverId, Protocols.DISCOVER, encodeData(message)));
201
+ }*/
190
202
  packBootstrap() {
191
- return __awaiter(this, void 0, void 0, function* () {
192
- return new Datagram(Protocols.BOOTSTRAP, encodeData(yield this.keyExchange.generateData()));
193
- });
203
+ return new Datagram(Protocols.BOOTSTRAP, encodeData(this.keyExchange.generateData()));
194
204
  }
195
205
  sendBootstrap(receiverId) {
196
- return __awaiter(this, void 0, void 0, function* () {
206
+ try {
197
207
  //console.debug("Sending Bootstrap");
198
- if (yield this.sessions.has(receiverId.toString()))
208
+ if (this.sessions.has(receiverId.toString()))
199
209
  throw new Error("Session exists");
200
- const datagram = yield this.packBootstrap();
210
+ const datagram = this.packBootstrap();
201
211
  this.emitter.emit('send', { datagram, userId: UserId.from(receiverId) });
202
- });
212
+ }
213
+ catch (error) {
214
+ this.error(error);
215
+ throw error;
216
+ }
203
217
  }
204
218
  decrypt(datagram) {
205
- return __awaiter(this, void 0, void 0, function* () {
219
+ try {
206
220
  datagram = EncryptedDatagram.from(datagram);
207
221
  if (!datagram.sessionTag)
208
222
  throw new Error("Datagram not encrypted");
209
- const session = yield this.sessions.get(datagram.sessionTag);
223
+ const session = this.sessions.get(datagram.sessionTag);
210
224
  if (!session)
211
225
  throw new Error("Session not found for sessionTag: " + datagram.sessionTag);
212
226
  if (!datagram.verify(session.identityKey.signatureKey))
@@ -216,17 +230,21 @@ export class FreeSignalNode {
216
230
  const decrypted = decryptData(session, datagram.payload);
217
231
  this.sessions.set(datagram.sessionTag, session);
218
232
  return { session, payload: decrypted };
219
- });
233
+ }
234
+ catch (error) {
235
+ this.error(error);
236
+ throw error;
237
+ }
220
238
  }
221
239
  openHandshake(datagram) {
222
- return __awaiter(this, void 0, void 0, function* () {
240
+ try {
223
241
  const encrypted = EncryptedDatagram.from(datagram);
224
242
  if (!encrypted.payload)
225
243
  throw new Error("Missing payload");
226
- if (yield this.sessions.has(encrypted.sessionTag)) {
244
+ if (this.sessions.has(encrypted.sessionTag)) {
227
245
  //console.debug("Opening Handshake Ack");
228
- const session = yield this.sessions.get(encrypted.sessionTag);
229
- const { payload } = yield this.decrypt(encrypted);
246
+ const session = this.sessions.get(encrypted.sessionTag);
247
+ const { payload } = this.decrypt(encrypted);
230
248
  if (!session)
231
249
  throw new Error("Session not found for sessionTag: " + encrypted.sessionTag);
232
250
  if (!compareBytes(payload, crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, session.identityKey.exchangeKey)))
@@ -237,15 +255,19 @@ export class FreeSignalNode {
237
255
  const data = decodeData(encrypted.payload);
238
256
  if (!encrypted.verify(IdentityKey.from(data.identityKey).signatureKey))
239
257
  throw new Error("Signature not verified");
240
- const { session, associatedData } = yield this.keyExchange.digestMessage(data);
241
- yield this.sessions.set(session.sessionTag, session);
242
- yield this.users.set(session.userId.toString(), session.sessionTag);
243
- yield this.bundles.set(session.userId.toString(), decodeData(associatedData));
258
+ const { session, associatedData } = this.keyExchange.digestMessage(data);
259
+ this.sessions.set(session.sessionTag, session);
260
+ this.users.set(session.userId.toString(), session.sessionTag);
261
+ this.bundles.set(session.userId.toString(), decodeData(associatedData));
244
262
  return 'syn';
245
- });
263
+ }
264
+ catch (error) {
265
+ this.error(error);
266
+ throw error;
267
+ }
246
268
  }
247
269
  open(datagram) {
248
- return __awaiter(this, void 0, void 0, function* () {
270
+ try {
249
271
  if (datagram instanceof Uint8Array)
250
272
  datagram = Datagram.from(datagram);
251
273
  switch (datagram.protocol) {
@@ -253,49 +275,66 @@ export class FreeSignalNode {
253
275
  const encrypted = EncryptedDatagram.from(datagram);
254
276
  if (!encrypted.payload)
255
277
  throw new Error("Missing payload");
256
- if ((yield this.openHandshake(datagram)) === 'ack')
257
- return;
258
- const session = yield this.sessions.get(encrypted.sessionTag);
278
+ const handshakeState = this.openHandshake(datagram);
279
+ const session = this.sessions.get(encrypted.sessionTag);
259
280
  if (!session)
260
281
  throw new Error("Session not found for sessionTag: " + encrypted.sessionTag);
261
- yield this.sendHandshake(session);
282
+ if (handshakeState === 'syn')
283
+ this.sendHandshake(session);
262
284
  this.emitter.emit('handshake', { session });
263
285
  return;
264
286
  }
265
287
  case Protocols.MESSAGE:
266
288
  //console.debug("Opening Message");
267
- this.emitter.emit('message', yield this.decrypt(datagram));
289
+ this.emitter.emit('message', this.decrypt(datagram));
268
290
  return;
269
291
  case Protocols.RELAY: {
270
292
  //console.debug("Opening Relay");
271
- const opened = yield this.decrypt(datagram);
293
+ const opened = this.decrypt(datagram);
272
294
  const userId = decodeBase64(opened.payload.subarray(0, UserId.keyLength));
273
- const sessionTag = yield this.users.get(userId);
295
+ const sessionTag = this.users.get(userId);
274
296
  if (!sessionTag)
275
297
  throw new Error("Session not found for user: " + userId);
276
- const session = yield this.sessions.get(sessionTag);
298
+ const session = this.sessions.get(sessionTag);
277
299
  if (!session)
278
300
  throw new Error("Session not found for sessionTag: " + datagram.sessionTag);
279
301
  this.emitter.emit('send', { session, datagram: Datagram.from(opened.payload.slice(UserId.keyLength)), userId: session.userId });
280
302
  return;
281
303
  }
282
- case Protocols.DISCOVER: {
304
+ case Protocols.BOOTSTRAP: {
305
+ //console.debug("Opening Bootstrap");
306
+ if (!datagram.payload)
307
+ throw new Error("Invalid Bootstrap");
308
+ const keyExchangeData = decodeData(datagram.payload);
309
+ const userId = UserId.fromKey(keyExchangeData.identityKey);
310
+ const request = new BootstrapRequest(userId, keyExchangeData);
311
+ let sended = false;
312
+ request.onChange = (request) => {
313
+ if (request.status === 'accepted' && !sended) {
314
+ sended = true;
315
+ this.sendHandshake(request.data);
316
+ }
317
+ };
318
+ this.bootstraps.set(userId.toString(), request);
319
+ this.emitter.emit('bootstrap', { request });
320
+ return;
321
+ }
322
+ /*case Protocols.DISCOVER: {
283
323
  //console.debug("Opening Discover");
284
- const { session, payload } = yield this.decrypt(datagram);
285
- const message = decodeData(payload);
286
- if (message.type === DiscoverType.REQUEST && message.discoverId && !(yield this.sessions.has(message.discoverId))) {
287
- let data;
324
+ const { session, payload } = await this.decrypt(datagram);
325
+ const message = decodeData<DiscoverMessage>(payload);
326
+ if (message.type === DiscoverType.REQUEST && message.discoverId && !(await this.sessions.has(message.discoverId))) {
327
+ let data: KeyExchangeData;
288
328
  if (message.discoverId === this.userId.toString()) {
289
- data = yield this.keyExchange.generateData();
290
- }
291
- else {
292
- const bundle = yield this.bundles.get(message.discoverId);
329
+ data = await this.keyExchange.generateData();
330
+ } else {
331
+ const bundle = await this.bundles.get(message.discoverId);
293
332
  if (!bundle)
294
333
  return;
295
334
  const { version, identityKey, signedPreKey, signature } = bundle;
296
335
  const onetimePreKey = bundle.onetimePreKeys.shift();
297
336
  if (!onetimePreKey) {
298
- yield this.bundles.delete(message.discoverId);
337
+ await this.bundles.delete(message.discoverId);
299
338
  return;
300
339
  }
301
340
  data = {
@@ -306,35 +345,17 @@ export class FreeSignalNode {
306
345
  onetimePreKey
307
346
  };
308
347
  }
309
- const response = { type: DiscoverType.RESPONSE, discoverId: message.discoverId, data };
310
- this.emitter.emit('send', yield this.encrypt(session.userId, Protocols.DISCOVER, encodeData(response)));
311
- }
312
- else if (message.type === DiscoverType.RESPONSE && this.discovers.has(message.discoverId)) {
348
+ const response: DiscoverMessage = { type: DiscoverType.RESPONSE, discoverId: message.discoverId, data };
349
+ this.emitter.emit('send', await this.encrypt(session.userId, Protocols.DISCOVER, encodeData(response)));
350
+ } else if (message.type === DiscoverType.RESPONSE && this.discovers.has(message.discoverId)) {
313
351
  this.discovers.delete(message.discoverId);
314
352
  if (message.data)
315
- yield this.sendHandshake(message.data);
353
+ await this.sendHandshake(message.data);
316
354
  }
317
355
  return;
318
356
  }
319
- case Protocols.BOOTSTRAP: {
320
- //console.debug("Opening Bootstrap");
321
- if (!datagram.payload)
322
- throw new Error("Invalid Bootstrap");
323
- const keyExchangeData = decodeData(datagram.payload);
324
- const userId = UserId.fromKey(keyExchangeData.identityKey);
325
- const request = new BootstrapRequest(userId, keyExchangeData);
326
- let sended = false;
327
- request.onChange = (request) => {
328
- if (request.status === 'accepted' && !sended) {
329
- sended = true;
330
- this.sendHandshake(request.data);
331
- }
332
- };
333
- yield this.bootstraps.set(userId.toString(), request);
334
- this.emitter.emit('bootstrap', { request });
335
- return;
336
- }
337
- /*case Protocols.PING:
357
+
358
+ case Protocols.PING:
338
359
  datagram = EncryptedDatagram.from(datagram);
339
360
  const session = await this.sessions.get(datagram.sessionTag!);
340
361
  if (!session)
@@ -344,46 +365,57 @@ export class FreeSignalNode {
344
365
  default:
345
366
  throw new Error("Invalid protocol");
346
367
  }
347
- });
368
+ }
369
+ catch (error) {
370
+ this.error(error);
371
+ throw error;
372
+ }
348
373
  }
349
- }
350
- class SessionMap {
351
- constructor(storage, maxSize = 50) {
352
- this.storage = storage;
353
- this.maxSize = maxSize;
354
- this.cache = new Map();
374
+ toJSON() {
375
+ return {
376
+ privateIdentityKey: this.privateIdentityKey.toString(),
377
+ sessions: Array.from(this.sessions.entries()).map(([key, session]) => [key, session.toJSON()]),
378
+ users: Array.from(this.users.entries()),
379
+ bundles: Array.from(this.bundles),
380
+ keyExchange: this.keyExchange.toJSON(),
381
+ };
355
382
  }
356
- set(key, value) {
383
+ }
384
+ /*class SessionMap implements LocalStorage<string, KeySession> {
385
+ private readonly cache = new Map<string, KeySession>()
386
+
387
+ public constructor(public readonly storage: LocalStorage<string, KeySessionState>, public readonly maxSize = 50) { }
388
+
389
+ public set(key: string, value: KeySession): Promise<void> {
357
390
  this.cache.set(key, value);
358
391
  return this.storage.set(key, value.toJSON());
359
392
  }
360
- get(key) {
361
- return __awaiter(this, void 0, void 0, function* () {
362
- const session = this.cache.get(key);
363
- if (!session) {
364
- const sessionData = yield this.storage.get(key);
365
- if (!sessionData)
366
- return undefined;
367
- return KeySession.from(sessionData);
368
- }
369
- return session;
370
- });
393
+
394
+ public async get(key: string): Promise<KeySession | undefined> {
395
+ const session = this.cache.get(key);
396
+ if (!session) {
397
+ const sessionData = await this.storage.get(key);
398
+ if (!sessionData)
399
+ return undefined;
400
+ return KeySession.from(sessionData);
401
+ }
402
+ return session;
371
403
  }
372
- has(key) {
373
- return __awaiter(this, void 0, void 0, function* () {
374
- return this.cache.has(key) || (yield this.storage.has(key));
375
- });
404
+
405
+ public async has(key: string): Promise<boolean> {
406
+ return this.cache.has(key) || await this.storage.has(key);
376
407
  }
377
- delete(key) {
378
- return __awaiter(this, void 0, void 0, function* () {
379
- return this.cache.delete(key) || (yield this.storage.delete(key));
380
- });
408
+
409
+ public async delete(key: string): Promise<boolean> {
410
+ return this.cache.delete(key) || await this.storage.delete(key);
381
411
  }
382
- clear() {
412
+
413
+ public clear(): Promise<void> {
383
414
  this.cache.clear();
384
415
  return this.storage.clear();
385
416
  }
386
- entries() {
417
+
418
+ public entries(): Promise<Iterable<[string, KeySession]>> {
387
419
  throw new Error("Method not implemented.");
388
420
  }
389
- }
421
+ }*/
package/dist/types.d.ts CHANGED
@@ -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 type { LocalStorage, Encodable, KeyExchangeData } from "@freesignal/interfaces";
19
+ import type { Encodable, KeyExchangeData } from "@freesignal/interfaces";
20
20
  import { EncryptionKeys, KeySession } from "./double-ratchet.js";
21
21
  export declare function encryptData(session: KeySession, data: Uint8Array): EncryptedData;
22
22
  export declare function decryptData(session: KeySession, encryptedData: Uint8Array): Uint8Array;
@@ -77,7 +77,6 @@ export declare enum Protocols {
77
77
  MESSAGE = "/freesignal/message",
78
78
  RELAY = "/freesignal/relay",
79
79
  HANDSHAKE = "/freesignal/handshake",
80
- DISCOVER = "/freesignal/discover",
81
80
  BOOTSTRAP = "/freesignal/bootstrap"
82
81
  }
83
82
  export declare namespace Protocols {
@@ -168,14 +167,4 @@ export declare class EncryptedData implements Encodable {
168
167
  };
169
168
  static from(data: Uint8Array | EncryptedData): EncryptedData;
170
169
  }
171
- export declare class AsyncMap<K, V> implements LocalStorage<K, V> {
172
- private readonly map;
173
- constructor(iterable?: Iterable<readonly [K, V]>);
174
- set(key: K, value: V): Promise<void>;
175
- get(key: K): Promise<V | undefined>;
176
- has(key: K): Promise<boolean>;
177
- delete(key: K): Promise<boolean>;
178
- clear(): Promise<void>;
179
- entries(): Promise<Iterable<[K, V]>>;
180
- }
181
170
  export {};
package/dist/types.js CHANGED
@@ -16,15 +16,6 @@
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
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
20
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
21
- return new (P || (P = Promise))(function (resolve, reject) {
22
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
23
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
24
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
25
- step((generator = generator.apply(thisArg, _arguments || [])).next());
26
- });
27
- };
28
19
  import { concatBytes, decodeBase64, encodeBase64, bytesToNumber, numberToBytes, compareBytes, decodeBase64URL } from "@freesignal/utils";
29
20
  import crypto from "@freesignal/crypto";
30
21
  export function encryptData(session, data) {
@@ -196,10 +187,10 @@ export var Protocols;
196
187
  (function (Protocols) {
197
188
  Protocols["NULL"] = "";
198
189
  //PING = '/freesignal/ping',
190
+ //DISCOVER = '/freesignal/discover',
199
191
  Protocols["MESSAGE"] = "/freesignal/message";
200
192
  Protocols["RELAY"] = "/freesignal/relay";
201
193
  Protocols["HANDSHAKE"] = "/freesignal/handshake";
202
- Protocols["DISCOVER"] = "/freesignal/discover";
203
194
  Protocols["BOOTSTRAP"] = "/freesignal/bootstrap";
204
195
  })(Protocols || (Protocols = {}));
205
196
  (function (Protocols) {
@@ -386,39 +377,35 @@ export class EncryptedData {
386
377
  }
387
378
  EncryptedData.version = 1;
388
379
  EncryptedData.nonceLength = crypto.box.nonceLength;
389
- export class AsyncMap {
390
- constructor(iterable) {
391
- this.map = new Map(iterable);
392
- }
393
- set(key, value) {
394
- return __awaiter(this, void 0, void 0, function* () {
395
- this.map.set(key, value);
396
- return;
397
- });
398
- }
399
- get(key) {
400
- return __awaiter(this, void 0, void 0, function* () {
401
- return this.map.get(key);
402
- });
403
- }
404
- has(key) {
405
- return __awaiter(this, void 0, void 0, function* () {
406
- return this.map.has(key);
407
- });
408
- }
409
- delete(key) {
410
- return __awaiter(this, void 0, void 0, function* () {
411
- return this.map.delete(key);
412
- });
413
- }
414
- clear() {
415
- return __awaiter(this, void 0, void 0, function* () {
416
- return this.map.clear();
417
- });
418
- }
419
- entries() {
420
- return __awaiter(this, void 0, void 0, function* () {
421
- return this.map.entries();
422
- });
423
- }
424
- }
380
+ /*class AsyncMap<K, V> implements LocalStorage<K, V> {
381
+ private readonly map: Map<K, V>;
382
+
383
+ constructor(iterable?: Iterable<readonly [K, V]>) {
384
+ this.map = new Map<K, V>(iterable);
385
+ }
386
+
387
+ async set(key: K, value: V): Promise<void> {
388
+ this.map.set(key, value);
389
+ return;
390
+ }
391
+
392
+ async get(key: K): Promise<V | undefined> {
393
+ return this.map.get(key);
394
+ }
395
+
396
+ async has(key: K): Promise<boolean> {
397
+ return this.map.has(key);
398
+ }
399
+
400
+ async delete(key: K): Promise<boolean> {
401
+ return this.map.delete(key);
402
+ }
403
+
404
+ async clear(): Promise<void> {
405
+ return this.map.clear();
406
+ }
407
+
408
+ async entries(): Promise<Iterable<[K, V]>> {
409
+ return this.map.entries();
410
+ }
411
+ }*/
package/dist/x3dh.d.ts CHANGED
@@ -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 { KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage, Crypto } from "@freesignal/interfaces";
19
+ import { KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, Crypto } from "@freesignal/interfaces";
20
20
  import { KeySession } from "./double-ratchet.js";
21
21
  import { IdentityKey, PrivateIdentityKey } from "./types.js";
22
- export interface ExportedKeyExchange {
22
+ export interface KeyExchangeState {
23
23
  privateIdentityKey: PrivateIdentityKey;
24
24
  storage: Array<[string, Crypto.KeyPair]>;
25
25
  }
@@ -29,18 +29,22 @@ export declare class KeyExchange {
29
29
  private static readonly maxOPK;
30
30
  private readonly privateIdentityKey;
31
31
  private readonly storage;
32
- constructor(storage: LocalStorage<string, Crypto.KeyPair>, privateIdentityKey?: PrivateIdentityKey);
32
+ constructor({ storage, privateIdentityKey }: {
33
+ storage?: Array<[string, Crypto.KeyPair]>;
34
+ privateIdentityKey?: PrivateIdentityKey;
35
+ });
33
36
  get identityKey(): IdentityKey;
34
37
  private generateSPK;
35
38
  private generateOPK;
36
- generateBundle(length?: number): Promise<KeyExchangeDataBundle>;
37
- generateData(): Promise<KeyExchangeData>;
38
- digestData(message: KeyExchangeData, associatedData?: Uint8Array): Promise<{
39
+ generateBundle(length?: number): KeyExchangeDataBundle;
40
+ generateData(): KeyExchangeData;
41
+ digestData(message: KeyExchangeData, associatedData?: Uint8Array): {
39
42
  session: KeySession;
40
43
  message: KeyExchangeSynMessage;
41
- }>;
42
- digestMessage(message: KeyExchangeSynMessage): Promise<{
44
+ };
45
+ digestMessage(message: KeyExchangeSynMessage): {
43
46
  session: KeySession;
44
47
  associatedData: Uint8Array;
45
- }>;
48
+ };
49
+ toJSON(): KeyExchangeState;
46
50
  }
package/dist/x3dh.js CHANGED
@@ -16,23 +16,14 @@
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
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
20
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
21
- return new (P || (P = Promise))(function (resolve, reject) {
22
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
23
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
24
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
25
- step((generator = generator.apply(thisArg, _arguments || [])).next());
26
- });
27
- };
28
19
  import crypto from "@freesignal/crypto";
29
20
  import { KeySession } from "./double-ratchet.js";
30
21
  import { concatBytes, decodeBase64, encodeBase64, compareBytes } from "@freesignal/utils";
31
22
  import { decryptData, encryptData, IdentityKey } from "./types.js";
32
23
  import { createIdentity } from "./index.js";
33
24
  export class KeyExchange {
34
- constructor(storage, privateIdentityKey) {
35
- this.storage = storage;
25
+ constructor({ storage, privateIdentityKey }) {
26
+ this.storage = new Map(storage);
36
27
  this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : createIdentity();
37
28
  }
38
29
  get identityKey() {
@@ -51,94 +42,92 @@ export class KeyExchange {
51
42
  return { onetimePreKey, onetimePreKeyHash };
52
43
  }
53
44
  generateBundle(length) {
54
- return __awaiter(this, void 0, void 0, function* () {
55
- const { signedPreKey, signedPreKeyHash } = this.generateSPK();
56
- const onetimePreKey = new Array(length !== null && length !== void 0 ? length : KeyExchange.maxOPK).fill(0).map(() => this.generateOPK(signedPreKeyHash).onetimePreKey);
57
- return {
58
- version: KeyExchange.version,
59
- identityKey: this.identityKey.toString(),
60
- signedPreKey: decodeBase64(signedPreKey.publicKey),
61
- signature: decodeBase64(crypto.EdDSA.sign(signedPreKeyHash, this.privateIdentityKey.signatureKey)),
62
- onetimePreKeys: onetimePreKey.map(opk => decodeBase64(opk.publicKey))
63
- };
64
- });
45
+ const { signedPreKey, signedPreKeyHash } = this.generateSPK();
46
+ const onetimePreKey = new Array(length !== null && length !== void 0 ? length : KeyExchange.maxOPK).fill(0).map(() => this.generateOPK(signedPreKeyHash).onetimePreKey);
47
+ return {
48
+ version: KeyExchange.version,
49
+ identityKey: this.identityKey.toString(),
50
+ signedPreKey: decodeBase64(signedPreKey.publicKey),
51
+ signature: decodeBase64(crypto.EdDSA.sign(signedPreKeyHash, this.privateIdentityKey.signatureKey)),
52
+ onetimePreKeys: onetimePreKey.map(opk => decodeBase64(opk.publicKey))
53
+ };
65
54
  }
66
55
  generateData() {
67
- return __awaiter(this, void 0, void 0, function* () {
68
- const { signedPreKey, signedPreKeyHash } = this.generateSPK();
69
- const { onetimePreKey } = this.generateOPK(signedPreKeyHash);
70
- return {
71
- version: KeyExchange.version,
72
- identityKey: this.identityKey.toString(),
73
- signedPreKey: decodeBase64(signedPreKey.publicKey),
74
- signature: decodeBase64(crypto.EdDSA.sign(signedPreKeyHash, this.privateIdentityKey.signatureKey)),
75
- onetimePreKey: decodeBase64(onetimePreKey.publicKey)
76
- };
77
- });
56
+ const { signedPreKey, signedPreKeyHash } = this.generateSPK();
57
+ const { onetimePreKey } = this.generateOPK(signedPreKeyHash);
58
+ return {
59
+ version: KeyExchange.version,
60
+ identityKey: this.identityKey.toString(),
61
+ signedPreKey: decodeBase64(signedPreKey.publicKey),
62
+ signature: decodeBase64(crypto.EdDSA.sign(signedPreKeyHash, this.privateIdentityKey.signatureKey)),
63
+ onetimePreKey: decodeBase64(onetimePreKey.publicKey)
64
+ };
78
65
  }
79
66
  digestData(message, associatedData) {
80
- return __awaiter(this, void 0, void 0, function* () {
81
- //console.debug("Digest Data")
82
- const ephemeralKey = crypto.ECDH.keyPair();
83
- const signedPreKey = encodeBase64(message.signedPreKey);
84
- const identityKey = IdentityKey.from(message.identityKey);
85
- if (!crypto.EdDSA.verify(encodeBase64(message.signature), crypto.hash(signedPreKey), identityKey.signatureKey))
86
- throw new Error("Signature verification failed");
87
- const onetimePreKey = message.onetimePreKey ? encodeBase64(message.onetimePreKey) : undefined;
88
- const signedPreKeyHash = crypto.hash(signedPreKey);
89
- const onetimePreKeyHash = onetimePreKey ? crypto.hash(onetimePreKey) : new Uint8Array();
90
- const derivedKey = crypto.hkdf(new Uint8Array([
91
- ...crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, signedPreKey),
92
- ...crypto.ECDH.scalarMult(ephemeralKey.secretKey, identityKey.exchangeKey),
93
- ...crypto.ECDH.scalarMult(ephemeralKey.secretKey, signedPreKey),
94
- ...onetimePreKey ? crypto.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
95
- ]), new Uint8Array(KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, KeySession.keyLength * 3);
96
- const session = new KeySession({ identityKey, remoteKey: identityKey.exchangeKey, rootKey: derivedKey.subarray(0, KeySession.keyLength), headerKey: derivedKey.subarray(KeySession.keyLength, KeySession.keyLength * 2), nextHeaderKey: derivedKey.subarray(KeySession.keyLength * 2) });
97
- const encrypted = encryptData(session, concatBytes(crypto.hash(this.identityKey.toBytes()), crypto.hash(identityKey.toBytes()), associatedData !== null && associatedData !== void 0 ? associatedData : new Uint8Array()));
98
- if (!encrypted)
99
- throw new Error("Decryption error");
100
- return {
101
- session,
102
- message: {
103
- version: KeyExchange.version,
104
- identityKey: this.identityKey.toString(),
105
- ephemeralKey: decodeBase64(ephemeralKey.publicKey),
106
- signedPreKeyHash: decodeBase64(signedPreKeyHash),
107
- onetimePreKeyHash: decodeBase64(onetimePreKeyHash),
108
- associatedData: decodeBase64(encrypted.toBytes())
109
- }
110
- };
111
- });
67
+ //console.debug("Digest Data")
68
+ const ephemeralKey = crypto.ECDH.keyPair();
69
+ const signedPreKey = encodeBase64(message.signedPreKey);
70
+ const identityKey = IdentityKey.from(message.identityKey);
71
+ if (!crypto.EdDSA.verify(encodeBase64(message.signature), crypto.hash(signedPreKey), identityKey.signatureKey))
72
+ throw new Error("Signature verification failed");
73
+ const onetimePreKey = message.onetimePreKey ? encodeBase64(message.onetimePreKey) : undefined;
74
+ const signedPreKeyHash = crypto.hash(signedPreKey);
75
+ const onetimePreKeyHash = onetimePreKey ? crypto.hash(onetimePreKey) : new Uint8Array();
76
+ const derivedKey = crypto.hkdf(new Uint8Array([
77
+ ...crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, signedPreKey),
78
+ ...crypto.ECDH.scalarMult(ephemeralKey.secretKey, identityKey.exchangeKey),
79
+ ...crypto.ECDH.scalarMult(ephemeralKey.secretKey, signedPreKey),
80
+ ...onetimePreKey ? crypto.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
81
+ ]), new Uint8Array(KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, KeySession.keyLength * 3);
82
+ const session = new KeySession({ identityKey, remoteKey: identityKey.exchangeKey, rootKey: derivedKey.subarray(0, KeySession.keyLength), headerKey: derivedKey.subarray(KeySession.keyLength, KeySession.keyLength * 2), nextHeaderKey: derivedKey.subarray(KeySession.keyLength * 2) });
83
+ const encrypted = encryptData(session, concatBytes(crypto.hash(this.identityKey.toBytes()), crypto.hash(identityKey.toBytes()), associatedData !== null && associatedData !== void 0 ? associatedData : new Uint8Array()));
84
+ if (!encrypted)
85
+ throw new Error("Decryption error");
86
+ return {
87
+ session,
88
+ message: {
89
+ version: KeyExchange.version,
90
+ identityKey: this.identityKey.toString(),
91
+ ephemeralKey: decodeBase64(ephemeralKey.publicKey),
92
+ signedPreKeyHash: decodeBase64(signedPreKeyHash),
93
+ onetimePreKeyHash: decodeBase64(onetimePreKeyHash),
94
+ associatedData: decodeBase64(encrypted.toBytes())
95
+ }
96
+ };
112
97
  }
113
98
  digestMessage(message) {
114
- return __awaiter(this, void 0, void 0, function* () {
115
- //console.debug("Digest Message")
116
- const signedPreKey = yield this.storage.get(message.signedPreKeyHash);
117
- const hash = message.signedPreKeyHash.concat(message.onetimePreKeyHash);
118
- const onetimePreKey = yield this.storage.get(hash);
119
- const identityKey = IdentityKey.from(message.identityKey);
120
- if (!signedPreKey || !onetimePreKey || !message.identityKey || !message.ephemeralKey)
121
- throw new Error("ACK message malformed");
122
- if (!this.storage.delete(hash))
123
- throw new Error("Bundle store deleting error");
124
- const ephemeralKey = encodeBase64(message.ephemeralKey);
125
- const derivedKey = crypto.hkdf(new Uint8Array([
126
- ...crypto.ECDH.scalarMult(signedPreKey.secretKey, identityKey.exchangeKey),
127
- ...crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, ephemeralKey),
128
- ...crypto.ECDH.scalarMult(signedPreKey.secretKey, ephemeralKey),
129
- ...onetimePreKey ? crypto.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
130
- ]), new Uint8Array(KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, KeySession.keyLength * 3);
131
- const session = new KeySession({ identityKey, secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, KeySession.keyLength), nextHeaderKey: derivedKey.subarray(KeySession.keyLength, KeySession.keyLength * 2), headerKey: derivedKey.subarray(KeySession.keyLength * 2) });
132
- const data = decryptData(session, encodeBase64(message.associatedData));
133
- if (!data)
134
- throw new Error("Error decrypting ACK message");
135
- if (!compareBytes(data.subarray(0, 64), concatBytes(crypto.hash(identityKey.toBytes()), crypto.hash(this.identityKey.toBytes()))))
136
- throw new Error("Error verifing Associated Data");
137
- return {
138
- session,
139
- associatedData: data.subarray(64)
140
- };
141
- });
99
+ //console.debug("Digest Message")
100
+ const signedPreKey = this.storage.get(message.signedPreKeyHash);
101
+ const hash = message.signedPreKeyHash.concat(message.onetimePreKeyHash);
102
+ const onetimePreKey = this.storage.get(hash);
103
+ const identityKey = IdentityKey.from(message.identityKey);
104
+ if (!signedPreKey || !onetimePreKey || !message.identityKey || !message.ephemeralKey)
105
+ throw new Error("ACK message malformed");
106
+ if (!this.storage.delete(hash))
107
+ throw new Error("Bundle store deleting error");
108
+ const ephemeralKey = encodeBase64(message.ephemeralKey);
109
+ const derivedKey = crypto.hkdf(new Uint8Array([
110
+ ...crypto.ECDH.scalarMult(signedPreKey.secretKey, identityKey.exchangeKey),
111
+ ...crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, ephemeralKey),
112
+ ...crypto.ECDH.scalarMult(signedPreKey.secretKey, ephemeralKey),
113
+ ...onetimePreKey ? crypto.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
114
+ ]), new Uint8Array(KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, KeySession.keyLength * 3);
115
+ const session = new KeySession({ identityKey, secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, KeySession.keyLength), nextHeaderKey: derivedKey.subarray(KeySession.keyLength, KeySession.keyLength * 2), headerKey: derivedKey.subarray(KeySession.keyLength * 2) });
116
+ const data = decryptData(session, encodeBase64(message.associatedData));
117
+ if (!data)
118
+ throw new Error("Error decrypting ACK message");
119
+ if (!compareBytes(data.subarray(0, 64), concatBytes(crypto.hash(identityKey.toBytes()), crypto.hash(this.identityKey.toBytes()))))
120
+ throw new Error("Error verifing Associated Data");
121
+ return {
122
+ session,
123
+ associatedData: data.subarray(64)
124
+ };
125
+ }
126
+ toJSON() {
127
+ return {
128
+ privateIdentityKey: this.privateIdentityKey,
129
+ storage: Array.from(this.storage.entries())
130
+ };
142
131
  }
143
132
  }
144
133
  KeyExchange.version = 1;
package/package.json CHANGED
@@ -1,45 +1,45 @@
1
- {
2
- "name": "@freesignal/protocol",
3
- "version": "0.8.2",
4
- "description": "Signal Protocol implementation in javascript",
5
- "license": "GPL-3.0-or-later",
6
- "author": "Christian Braghette",
7
- "type": "module",
8
- "exports": {
9
- ".": {
10
- "import": "./dist/index.js",
11
- "require": "./dist/index.js",
12
- "types": "./dist/index.d.ts"
13
- },
14
- "./double-ratchet": {
15
- "import": "./dist/double-ratchet.js",
16
- "require": "./dist/double-ratchet.js",
17
- "types": "./dist/double-ratchet.d.ts"
18
- },
19
- "./node": {
20
- "import": "./dist/node.js",
21
- "require": "./dist/node.js",
22
- "types": "./dist/node.d.ts"
23
- },
24
- "./x3dh": {
25
- "import": "./dist/x3dh.js",
26
- "require": "./dist/x3dh.js",
27
- "types": "./dist/x3dh.d.ts"
28
- }
29
- },
30
- "main": "./dist/index.js",
31
- "scripts": {
32
- "pretest": "tsc",
33
- "test": "node ./dist/test.js",
34
- "prepare": "tsc"
35
- },
36
- "dependencies": {
37
- "@freesignal/crypto": "^0.4.2",
38
- "@freesignal/interfaces": "^0.3.0",
39
- "@freesignal/utils": "^1.5.1",
40
- "easyemitter.ts": "^1.1.0"
41
- },
42
- "devDependencies": {
43
- "@types/node": "^24.2.1"
44
- }
45
- }
1
+ {
2
+ "name": "@freesignal/protocol",
3
+ "version": "0.9.0",
4
+ "description": "Signal Protocol implementation in TypeScript",
5
+ "license": "GPL-3.0-or-later",
6
+ "author": "Christian Braghette",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./double-ratchet": {
15
+ "import": "./dist/double-ratchet.js",
16
+ "require": "./dist/double-ratchet.js",
17
+ "types": "./dist/double-ratchet.d.ts"
18
+ },
19
+ "./node": {
20
+ "import": "./dist/node.js",
21
+ "require": "./dist/node.js",
22
+ "types": "./dist/node.d.ts"
23
+ },
24
+ "./x3dh": {
25
+ "import": "./dist/x3dh.js",
26
+ "require": "./dist/x3dh.js",
27
+ "types": "./dist/x3dh.d.ts"
28
+ }
29
+ },
30
+ "main": "./dist/index.js",
31
+ "scripts": {
32
+ "pretest": "tsc",
33
+ "test": "node ./dist/test.js",
34
+ "prepare": "tsc"
35
+ },
36
+ "dependencies": {
37
+ "@freesignal/crypto": "^0.4.2",
38
+ "@freesignal/interfaces": "^0.3.0",
39
+ "@freesignal/utils": "^1.5.1",
40
+ "easyemitter.ts": "^1.1.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^24.2.1"
44
+ }
45
+ }