@freesignal/protocol 0.3.0 → 0.4.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.
@@ -16,13 +16,16 @@
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
+ import { KeySession } from "./double-ratchet";
21
+ export declare function encryptData(session: KeySession, data: Uint8Array): EncryptedData;
22
+ export declare function decryptData(session: KeySession, encryptedData: Uint8Array): Uint8Array;
23
+ export declare class UserId implements Encodable {
21
24
  private readonly array;
22
25
  private constructor();
23
26
  toString(): string;
24
27
  toJSON(): string;
25
- toUint8Array(): Uint8Array;
28
+ toBytes(): Uint8Array;
26
29
  static fromKey(identityKey: string | Uint8Array | IdentityKey): UserId;
27
30
  static from(userId: string | Uint8Array | UserId): UserId;
28
31
  }
@@ -51,12 +54,22 @@ export declare namespace PrivateIdentityKey {
51
54
  function from(identityKey: PrivateIdentityKey | Uint8Array | string): PrivateIdentityKey;
52
55
  function from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): PrivateIdentityKey;
53
56
  }
57
+ export declare enum DiscoverType {
58
+ REQUEST = 0,
59
+ RESPONSE = 1
60
+ }
61
+ export interface DiscoverMessage {
62
+ type: DiscoverType;
63
+ discoverId: string;
64
+ data?: KeyExchangeData;
65
+ }
54
66
  export declare enum Protocols {
55
67
  NULL = "",
56
68
  MESSAGE = "/freesignal/message",
57
69
  RELAY = "/freesignal/relay",
58
70
  HANDSHAKE = "/freesignal/handshake",
59
- DISCOVER = "/freesignal/discover"
71
+ DISCOVER = "/freesignal/discover",
72
+ BOOTSTRAP = "/freesignal/bootstrap"
60
73
  }
61
74
  export declare namespace Protocols {
62
75
  function isProtocol(protocol: any): boolean;
@@ -65,6 +78,19 @@ export declare namespace Protocols {
65
78
  function encode(protocol: Protocols, length?: number): Uint8Array;
66
79
  function decode(array: Uint8Array): Protocols;
67
80
  }
81
+ interface DatagramJSON {
82
+ id: string;
83
+ version: number;
84
+ sender: string;
85
+ receiver: string;
86
+ protocol: Protocols;
87
+ createdAt: number;
88
+ payload: string | undefined;
89
+ signature: string | undefined;
90
+ }
91
+ export interface SignedDatagram extends Datagram {
92
+ signature: string;
93
+ }
68
94
  export declare class Datagram implements Encodable {
69
95
  static version: number;
70
96
  private _id;
@@ -75,20 +101,20 @@ export declare class Datagram implements Encodable {
75
101
  private _createdAt;
76
102
  private _payload?;
77
103
  private _signature?;
78
- private secretKey?;
79
104
  private static headerOffset;
80
105
  constructor(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable);
81
106
  get id(): string;
82
107
  get version(): number;
83
108
  get createdAt(): number;
84
- get signed(): boolean;
85
- get signature(): string | undefined;
86
109
  set payload(data: Uint8Array);
87
110
  get payload(): Uint8Array | undefined;
111
+ get signature(): string | undefined;
112
+ private get unsigned();
88
113
  toBytes(): Uint8Array;
89
- sign(secretKey: Uint8Array): this;
114
+ sign(secretKey: Uint8Array): SignedDatagram;
90
115
  toString(): string;
91
- toJSON(): string;
116
+ toJSON(): DatagramJSON;
117
+ static verify(datagram: Datagram, publicKey: Uint8Array): boolean;
92
118
  static from(data: Uint8Array | Datagram | string): Datagram;
93
119
  }
94
120
  /**
@@ -144,14 +170,14 @@ export interface EncryptedData extends Encodable {
144
170
  ciphertext: string;
145
171
  };
146
172
  }
147
- export declare class EncryptedData {
173
+ export declare namespace EncryptedData {
148
174
  /**
149
175
  * Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
150
176
  *
151
177
  * @param array - A previously serialized encrypted payload.
152
178
  * @returns An instance of `EncryptedPayload`.
153
179
  */
154
- static from(array: Uint8Array | EncryptedData): EncryptedData;
180
+ function from(array: Uint8Array | EncryptedData): EncryptedData;
155
181
  }
156
182
  export declare class AsyncMap<K, V> implements LocalStorage<K, V> {
157
183
  private readonly map;
@@ -163,3 +189,4 @@ export declare class AsyncMap<K, V> implements LocalStorage<K, V> {
163
189
  clear(): Promise<void>;
164
190
  entries(): ArrayIterator<[K, V]>;
165
191
  }
192
+ export {};
@@ -30,10 +30,30 @@ 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
+ exports.encryptData = encryptData;
35
+ exports.decryptData = decryptData;
34
36
  const utils_1 = require("@freesignal/utils");
35
37
  const crypto_1 = __importDefault(require("@freesignal/crypto"));
36
38
  const double_ratchet_1 = require("./double-ratchet");
39
+ function encryptData(session, data) {
40
+ const key = session.getSendingKey();
41
+ if (!key)
42
+ throw new Error("Error generating key");
43
+ const nonce = crypto_1.default.randomBytes(EncryptedDataConstructor.nonceLength);
44
+ const ciphertext = crypto_1.default.box.encrypt(data, nonce, key.secretKey);
45
+ return new EncryptedDataConstructor(key.count, key.previous, key.publicKey, nonce, ciphertext);
46
+ }
47
+ function decryptData(session, encryptedData) {
48
+ const encrypted = EncryptedData.from(encryptedData);
49
+ const key = session.getReceivingKey(encrypted);
50
+ if (!key)
51
+ throw new Error("Error calculating key");
52
+ const decrypted = crypto_1.default.box.decrypt(encrypted.ciphertext, encrypted.nonce, key);
53
+ if (!decrypted)
54
+ throw new Error("Error decrypting data");
55
+ return decrypted;
56
+ }
37
57
  class UserId {
38
58
  constructor(array) {
39
59
  this.array = array;
@@ -45,7 +65,7 @@ class UserId {
45
65
  toJSON() {
46
66
  return this.toString();
47
67
  }
48
- toUint8Array() {
68
+ toBytes() {
49
69
  return this.array;
50
70
  }
51
71
  static fromKey(identityKey) {
@@ -88,7 +108,7 @@ var IdentityKey;
88
108
  return UserId.fromKey(this.toBytes()).toString();
89
109
  }
90
110
  toBytes() {
91
- return (0, utils_1.concatArrays)((0, utils_1.numberToArray)(this.info), this.signatureKey, this.exchangeKey);
111
+ return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
92
112
  }
93
113
  toString() {
94
114
  return (0, utils_1.decodeBase64)(this.toBytes());
@@ -110,7 +130,7 @@ var IdentityKey;
110
130
  else
111
131
  return key;
112
132
  });
113
- return new IdentityKeyConstructor(keys.length === 2 ? (0, utils_1.concatArrays)((0, utils_1.numberToArray)(info + IdentityKey.version), ...keys) : keys[0]);
133
+ return new IdentityKeyConstructor(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(info + IdentityKey.version, 1), ...keys) : keys[0]);
114
134
  }
115
135
  IdentityKey.from = from;
116
136
  })(IdentityKey || (exports.IdentityKey = IdentityKey = {}));
@@ -121,8 +141,8 @@ var PrivateIdentityKey;
121
141
  PrivateIdentityKey.version = 1;
122
142
  class PrivateIdentityKeyConstructor {
123
143
  constructor(privateIdentityKey) {
124
- this.info = info + PrivateIdentityKey.version;
125
144
  if (privateIdentityKey instanceof PrivateIdentityKeyConstructor) {
145
+ this.info = privateIdentityKey.info;
126
146
  this.signatureKey = privateIdentityKey.signatureKey;
127
147
  this.exchangeKey = privateIdentityKey.exchangeKey;
128
148
  this.identityKey = privateIdentityKey.identityKey;
@@ -132,6 +152,7 @@ var PrivateIdentityKey;
132
152
  privateIdentityKey = (0, utils_1.encodeBase64)(privateIdentityKey);
133
153
  if (!isIdentityKeys(privateIdentityKey))
134
154
  throw new Error("Invalid key length");
155
+ this.info = privateIdentityKey[0];
135
156
  this.signatureKey = privateIdentityKey.subarray(1, crypto_1.default.EdDSA.secretKeyLength + 1);
136
157
  this.exchangeKey = privateIdentityKey.subarray(crypto_1.default.EdDSA.secretKeyLength + 1, PrivateIdentityKey.keyLength);
137
158
  this.identityKey = IdentityKey.from(crypto_1.default.EdDSA.keyPair(this.signatureKey).publicKey, crypto_1.default.ECDH.keyPair(this.exchangeKey).publicKey);
@@ -141,7 +162,7 @@ var PrivateIdentityKey;
141
162
  return UserId.fromKey(this.identityKey.toBytes()).toString();
142
163
  }
143
164
  toBytes() {
144
- return (0, utils_1.concatArrays)((0, utils_1.numberToArray)(this.info), this.signatureKey, this.exchangeKey);
165
+ return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
145
166
  }
146
167
  toString() {
147
168
  return (0, utils_1.decodeBase64)(this.toBytes());
@@ -163,10 +184,15 @@ var PrivateIdentityKey;
163
184
  else
164
185
  return key;
165
186
  });
166
- return new PrivateIdentityKeyConstructor(keys.length === 2 ? (0, utils_1.concatArrays)((0, utils_1.numberToArray)(info + PrivateIdentityKey.version), ...keys) : keys[0]);
187
+ return new PrivateIdentityKeyConstructor(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(info + PrivateIdentityKey.version, 1), ...keys) : keys[0]);
167
188
  }
168
189
  PrivateIdentityKey.from = from;
169
190
  })(PrivateIdentityKey || (exports.PrivateIdentityKey = PrivateIdentityKey = {}));
191
+ var DiscoverType;
192
+ (function (DiscoverType) {
193
+ DiscoverType[DiscoverType["REQUEST"] = 0] = "REQUEST";
194
+ DiscoverType[DiscoverType["RESPONSE"] = 1] = "RESPONSE";
195
+ })(DiscoverType || (exports.DiscoverType = DiscoverType = {}));
170
196
  var Protocols;
171
197
  (function (Protocols) {
172
198
  Protocols["NULL"] = "";
@@ -174,6 +200,7 @@ var Protocols;
174
200
  Protocols["RELAY"] = "/freesignal/relay";
175
201
  Protocols["HANDSHAKE"] = "/freesignal/handshake";
176
202
  Protocols["DISCOVER"] = "/freesignal/discover";
203
+ Protocols["BOOTSTRAP"] = "/freesignal/bootstrap";
177
204
  })(Protocols || (exports.Protocols = Protocols = {}));
178
205
  (function (Protocols) {
179
206
  function isProtocol(protocol) {
@@ -188,15 +215,16 @@ var Protocols;
188
215
  return Object.values(Protocols).indexOf(protocol);
189
216
  }
190
217
  Protocols.toCode = toCode;
191
- function encode(protocol, length) {
192
- return (0, utils_1.numberToArray)(Protocols.toCode(protocol), length);
218
+ function encode(protocol, length = 1) {
219
+ return (0, utils_1.numberToBytes)(Protocols.toCode(protocol), length);
193
220
  }
194
221
  Protocols.encode = encode;
195
222
  function decode(array) {
196
- return Protocols.fromCode((0, utils_1.numberFromArray)(array));
223
+ return Protocols.fromCode((0, utils_1.bytesToNumber)(array));
197
224
  }
198
225
  Protocols.decode = decode;
199
226
  })(Protocols || (exports.Protocols = Protocols = {}));
227
+ ;
200
228
  class Datagram {
201
229
  constructor(sender, receiver, protocol, payload) {
202
230
  this._id = crypto_1.default.UUID.generate().toString();
@@ -216,16 +244,6 @@ class Datagram {
216
244
  get createdAt() {
217
245
  return this._createdAt;
218
246
  }
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
247
  set payload(data) {
230
248
  this._signature = undefined;
231
249
  this._payload = data;
@@ -233,37 +251,56 @@ class Datagram {
233
251
  get payload() {
234
252
  return this._payload;
235
253
  }
254
+ get signature() {
255
+ return this._signature ? (0, utils_1.decodeBase64)(this._signature) : undefined;
256
+ }
257
+ get unsigned() {
258
+ const data = this.toBytes();
259
+ data[0] &= 127;
260
+ return data.subarray(0, data.length - (this._signature ? crypto_1.default.EdDSA.signatureLength : 0));
261
+ }
236
262
  toBytes() {
237
263
  var _a, _b, _c;
238
- const data = (0, utils_1.concatArrays)(new Uint8Array(1).fill(this.version | (this.secretKey ? 128 : 0)), //1
264
+ return (0, utils_1.concatBytes)(new Uint8Array(1).fill(this.version | (this.signature ? 128 : 0)), //1
239
265
  Protocols.encode(this.protocol), //1
240
266
  (_a = crypto_1.default.UUID.parse(this.id)) !== null && _a !== void 0 ? _a : [], //16
241
- (0, utils_1.numberToArray)(this.createdAt, 8), //8
267
+ new Uint8Array((0, utils_1.numberToBytes)(this._createdAt, 8)), //8
242
268
  (0, utils_1.encodeBase64)(this.sender), //32
243
269
  (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());
270
+ (_b = this._payload) !== null && _b !== void 0 ? _b : new Uint8Array(), (_c = this._signature) !== null && _c !== void 0 ? _c : new Uint8Array());
248
271
  }
249
272
  sign(secretKey) {
250
- this.secretKey = secretKey;
273
+ this._signature = crypto_1.default.EdDSA.sign(this.unsigned, secretKey);
251
274
  return this;
252
275
  }
253
276
  toString() {
254
277
  return (0, utils_1.decodeBase64)(this.toBytes());
255
278
  }
256
279
  toJSON() {
257
- return this.toString();
280
+ return {
281
+ id: this.id,
282
+ version: this.version,
283
+ sender: this.sender,
284
+ receiver: this.receiver,
285
+ protocol: this.protocol,
286
+ createdAt: this.createdAt,
287
+ payload: this.payload ? (0, utils_1.decodeBase64)(this.payload) : undefined,
288
+ signature: this._signature ? (0, utils_1.decodeBase64)(this._signature) : undefined
289
+ };
290
+ }
291
+ static verify(datagram, publicKey) {
292
+ if (!datagram._signature)
293
+ throw new Error("Datagram not signed");
294
+ return crypto_1.default.EdDSA.verify(datagram.unsigned, datagram._signature, publicKey);
258
295
  }
259
296
  static from(data) {
260
297
  if (typeof data === 'string')
261
298
  data = (0, utils_1.encodeBase64)(data);
262
299
  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));
300
+ 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
301
  datagram._version = data[0] & 127;
265
302
  datagram._id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
266
- datagram._createdAt = (0, utils_1.numberFromArray)(data.subarray(18, 26));
303
+ datagram._createdAt = (0, utils_1.bytesToNumber)(data.subarray(18, 26));
267
304
  if (data[0] & 128)
268
305
  datagram._signature = data.subarray(data.length - crypto_1.default.EdDSA.signatureLength);
269
306
  return datagram;
@@ -272,8 +309,8 @@ class Datagram {
272
309
  const datagram = new Datagram(data.sender, data.receiver, data.protocol, data.payload);
273
310
  datagram._id = datagram.id;
274
311
  datagram._version = datagram.version;
275
- datagram._createdAt = datagram.createdAt;
276
- datagram._signature = datagram.signature ? (0, utils_1.encodeBase64)(datagram.signature) : undefined;
312
+ datagram._createdAt = datagram._createdAt;
313
+ datagram._signature = datagram._signature;
277
314
  return datagram;
278
315
  }
279
316
  else
@@ -283,18 +320,91 @@ class Datagram {
283
320
  exports.Datagram = Datagram;
284
321
  Datagram.version = 1;
285
322
  Datagram.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
286
- class EncryptedData {
323
+ var EncryptedData;
324
+ (function (EncryptedData) {
287
325
  /**
288
326
  * Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
289
327
  *
290
328
  * @param array - A previously serialized encrypted payload.
291
329
  * @returns An instance of `EncryptedPayload`.
292
330
  */
293
- static from(array) {
294
- return new double_ratchet_1.EncryptedDataConstructor(array);
331
+ function from(array) {
332
+ return new EncryptedDataConstructor(array);
333
+ }
334
+ EncryptedData.from = from;
335
+ })(EncryptedData || (exports.EncryptedData = EncryptedData = {}));
336
+ class EncryptedDataConstructor {
337
+ constructor(...arrays) {
338
+ arrays = arrays.filter(value => value !== undefined);
339
+ if (arrays[0] instanceof EncryptedDataConstructor) {
340
+ this.raw = arrays[0].raw;
341
+ return this;
342
+ }
343
+ if (typeof arrays[0] === 'number')
344
+ arrays[0] = (0, utils_1.numberToBytes)(arrays[0], EncryptedDataConstructor.countLength);
345
+ if (typeof arrays[1] === 'number')
346
+ arrays[1] = (0, utils_1.numberToBytes)(arrays[1], EncryptedDataConstructor.countLength);
347
+ if (arrays.length === 6) {
348
+ arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToBytes)(arrays[5], 1) : arrays[5]);
349
+ arrays.pop();
350
+ }
351
+ else if (arrays.length > 1) {
352
+ arrays.unshift((0, utils_1.numberToBytes)(double_ratchet_1.KeySession.version, 1));
353
+ }
354
+ this.raw = (0, utils_1.concatBytes)(...arrays);
355
+ }
356
+ get length() { return this.raw.length; }
357
+ get version() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
358
+ get count() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
359
+ get previous() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
360
+ get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
361
+ get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
362
+ get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
363
+ toBytes() {
364
+ return this.raw;
365
+ }
366
+ toString() {
367
+ return (0, utils_1.decodeBase64)(this.raw);
368
+ }
369
+ toJSON() {
370
+ return {
371
+ version: this.version,
372
+ count: this.count,
373
+ previous: this.previous,
374
+ publicKey: (0, utils_1.decodeBase64)(this.publicKey),
375
+ nonce: (0, utils_1.decodeBase64)(this.nonce),
376
+ ciphertext: (0, utils_1.decodeBase64)(this.ciphertext)
377
+ };
378
+ }
379
+ }
380
+ EncryptedDataConstructor.secretKeyLength = crypto_1.default.ECDH.secretKeyLength;
381
+ EncryptedDataConstructor.publicKeyLength = crypto_1.default.ECDH.publicKeyLength;
382
+ EncryptedDataConstructor.keyLength = crypto_1.default.box.keyLength;
383
+ EncryptedDataConstructor.nonceLength = crypto_1.default.box.nonceLength;
384
+ EncryptedDataConstructor.countLength = 2;
385
+ class Offsets {
386
+ static set(start, length) {
387
+ class Offset {
388
+ constructor(start, length) {
389
+ this.start = start;
390
+ this.length = length;
391
+ if (typeof length === 'number')
392
+ this.end = start + length;
393
+ }
394
+ get get() {
395
+ return [this.start, this.length];
396
+ }
397
+ }
398
+ return new Offset(start, length);
295
399
  }
296
400
  }
297
- exports.EncryptedData = EncryptedData;
401
+ Offsets.checksum = Offsets.set(0, 0);
402
+ Offsets.version = Offsets.set(Offsets.checksum.end, 1);
403
+ Offsets.count = Offsets.set(Offsets.version.end, EncryptedDataConstructor.countLength);
404
+ Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.countLength);
405
+ Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
406
+ Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
407
+ Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
298
408
  class AsyncMap {
299
409
  constructor(iterable) {
300
410
  this.map = new Map(iterable);
@@ -17,7 +17,7 @@
17
17
  * along with this program. If not, see <https://www.gnu.org/licenses/>
18
18
  */
19
19
  import { KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage, Crypto } from "@freesignal/interfaces";
20
- import { ExportedKeySession, KeySession } from "./double-ratchet";
20
+ import { KeySession } from "./double-ratchet";
21
21
  import { IdentityKey, PrivateIdentityKey } from "./types";
22
22
  export interface ExportedKeyExchange {
23
23
  privateIdentityKey: PrivateIdentityKey;
@@ -29,17 +29,13 @@ export declare class KeyExchange {
29
29
  private static readonly maxOPK;
30
30
  private readonly privateIdentityKey;
31
31
  private readonly storage;
32
- private readonly sessions;
33
- constructor(storage: {
34
- keys: LocalStorage<string, Crypto.KeyPair>;
35
- sessions: LocalStorage<string, ExportedKeySession>;
36
- }, privateIdentityKey?: PrivateIdentityKey);
32
+ constructor(storage: LocalStorage<string, Crypto.KeyPair>, privateIdentityKey?: PrivateIdentityKey);
37
33
  get identityKey(): IdentityKey;
38
34
  private generateSPK;
39
35
  private generateOPK;
40
36
  generateBundle(length?: number): Promise<KeyExchangeDataBundle>;
41
37
  generateData(): Promise<KeyExchangeData>;
42
- digestData(message: KeyExchangeData): Promise<{
38
+ digestData(message: KeyExchangeData, associatedData?: Uint8Array): Promise<{
43
39
  session: KeySession;
44
40
  message: KeyExchangeSynMessage;
45
41
  identityKey: IdentityKey;
@@ -47,5 +43,6 @@ export declare class KeyExchange {
47
43
  digestMessage(message: KeyExchangeSynMessage): Promise<{
48
44
  session: KeySession;
49
45
  identityKey: IdentityKey;
46
+ associatedData: Uint8Array;
50
47
  }>;
51
48
  }
@@ -38,8 +38,7 @@ const types_1 = require("./types");
38
38
  const _1 = require(".");
39
39
  class KeyExchange {
40
40
  constructor(storage, privateIdentityKey) {
41
- this.storage = storage.keys;
42
- this.sessions = storage.sessions;
41
+ this.storage = storage;
43
42
  this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : (0, _1.createIdentity)();
44
43
  }
45
44
  get identityKey() {
@@ -66,7 +65,7 @@ class KeyExchange {
66
65
  identityKey: this.identityKey.toString(),
67
66
  signedPreKey: (0, utils_1.decodeBase64)(signedPreKey.publicKey),
68
67
  signature: (0, utils_1.decodeBase64)(crypto_1.default.EdDSA.sign(signedPreKeyHash, this.privateIdentityKey.signatureKey)),
69
- onetimePreKey: onetimePreKey.map(opk => (0, utils_1.decodeBase64)(opk.publicKey))
68
+ onetimePreKeys: onetimePreKey.map(opk => (0, utils_1.decodeBase64)(opk.publicKey))
70
69
  };
71
70
  });
72
71
  }
@@ -83,7 +82,7 @@ class KeyExchange {
83
82
  };
84
83
  });
85
84
  }
86
- digestData(message) {
85
+ digestData(message, associatedData) {
87
86
  return __awaiter(this, void 0, void 0, function* () {
88
87
  const ephemeralKey = crypto_1.default.ECDH.keyPair();
89
88
  const signedPreKey = (0, utils_1.encodeBase64)(message.signedPreKey);
@@ -99,9 +98,9 @@ class KeyExchange {
99
98
  ...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, signedPreKey),
100
99
  ...onetimePreKey ? crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
101
100
  ]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength);
102
- const session = new double_ratchet_1.KeySession(this.sessions, { remoteKey: identityKey.exchangeKey, rootKey });
103
- const cyphertext = yield session.encrypt((0, utils_1.concatArrays)(crypto_1.default.hash(this.identityKey.toBytes()), crypto_1.default.hash(identityKey.toBytes())));
104
- if (!cyphertext)
101
+ const session = new double_ratchet_1.KeySession({ remoteKey: identityKey.exchangeKey, rootKey });
102
+ const encrypted = (0, types_1.encryptData)(session, (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()));
103
+ if (!encrypted)
105
104
  throw new Error("Decryption error");
106
105
  return {
107
106
  session,
@@ -111,7 +110,7 @@ class KeyExchange {
111
110
  ephemeralKey: (0, utils_1.decodeBase64)(ephemeralKey.publicKey),
112
111
  signedPreKeyHash: (0, utils_1.decodeBase64)(signedPreKeyHash),
113
112
  onetimePreKeyHash: (0, utils_1.decodeBase64)(onetimePreKeyHash),
114
- associatedData: (0, utils_1.decodeBase64)(cyphertext.toBytes())
113
+ associatedData: (0, utils_1.decodeBase64)(encrypted.toBytes())
115
114
  },
116
115
  identityKey
117
116
  };
@@ -134,15 +133,16 @@ class KeyExchange {
134
133
  ...crypto_1.default.ECDH.scalarMult(signedPreKey.secretKey, ephemeralKey),
135
134
  ...onetimePreKey ? crypto_1.default.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
136
135
  ]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength);
137
- const session = new double_ratchet_1.KeySession(this.sessions, { secretKey: this.privateIdentityKey.exchangeKey, rootKey });
138
- const cleartext = yield session.decrypt((0, utils_1.encodeBase64)(message.associatedData));
139
- if (!cleartext)
136
+ const session = new double_ratchet_1.KeySession({ secretKey: this.privateIdentityKey.exchangeKey, rootKey });
137
+ const data = (0, types_1.decryptData)(session, (0, utils_1.encodeBase64)(message.associatedData));
138
+ if (!data)
140
139
  throw new Error("Error decrypting ACK message");
141
- if (!(0, utils_1.verifyArrays)(cleartext, (0, utils_1.concatArrays)(crypto_1.default.hash(identityKey.toBytes()), crypto_1.default.hash(this.identityKey.toBytes()))))
140
+ if (!(0, utils_1.compareBytes)(data.subarray(0, 64), (0, utils_1.concatBytes)(crypto_1.default.hash(identityKey.toBytes()), crypto_1.default.hash(this.identityKey.toBytes()))))
142
141
  throw new Error("Error verifing Associated Data");
143
142
  return {
144
143
  session,
145
- identityKey
144
+ identityKey,
145
+ associatedData: data.subarray(64)
146
146
  };
147
147
  });
148
148
  }
package/package.json CHANGED
@@ -1,22 +1,45 @@
1
1
  {
2
2
  "name": "@freesignal/protocol",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
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
- "main": "index.js",
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",
9
31
  "scripts": {
10
- "test": "tsc && node ./build/test.js",
11
- "build": "tsc && node ./build/build.js"
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.3.0",
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
+ }