@freesignal/protocol 0.2.5 → 0.2.6

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/api.d.ts CHANGED
@@ -1,8 +1,25 @@
1
+ /**
2
+ * FreeSignal Protocol
3
+ *
4
+ * Copyright (C) 2025 Christian Braghette
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
18
+ */
1
19
  import { Crypto, KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage } from "@freesignal/interfaces";
2
20
  import { KeySession } from "./double-ratchet";
3
21
  import { KeyExchange } from "./x3dh";
4
22
  import { Datagram, IdentityKeys, EncryptedData, UserId } from "./types";
5
- export declare const FREESIGNAL_MIME = "application/x-freesignal";
6
23
  type DatagramId = string;
7
24
  export declare class FreeSignalAPI {
8
25
  protected readonly signKey: Crypto.KeyPair;
@@ -33,9 +50,5 @@ export declare class FreeSignalAPI {
33
50
  identityKeys: IdentityKeys;
34
51
  userId: UserId;
35
52
  }>;
36
- protected packIdList(datagramIds: DatagramId[]): Uint8Array;
37
- protected unpackIdList(data: Uint8Array): DatagramId[];
38
- protected packDatagrams(messages: Datagram[]): Uint8Array;
39
- protected unpackDatagrams(data: Uint8Array): Datagram[];
40
53
  }
41
54
  export {};
package/api.js CHANGED
@@ -1,4 +1,22 @@
1
1
  "use strict";
2
+ /**
3
+ * FreeSignal Protocol
4
+ *
5
+ * Copyright (C) 2025 Christian Braghette
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
19
+ */
2
20
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
21
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
22
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -12,13 +30,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
30
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
31
  };
14
32
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.FreeSignalAPI = exports.FREESIGNAL_MIME = void 0;
33
+ exports.FreeSignalAPI = void 0;
16
34
  const crypto_1 = __importDefault(require("@freesignal/crypto"));
17
35
  const x3dh_1 = require("./x3dh");
18
36
  const utils_1 = require("@freesignal/utils");
19
37
  const types_1 = require("./types");
20
- const fflate_1 = __importDefault(require("fflate"));
21
- exports.FREESIGNAL_MIME = "application/x-freesignal";
22
38
  class FreeSignalAPI {
23
39
  constructor(opts) {
24
40
  const { secretSignKey, secretBoxKey, sessions, keyExchange, users } = opts;
@@ -70,9 +86,9 @@ class FreeSignalAPI {
70
86
  const res = yield fetch(url, {
71
87
  method: 'POST',
72
88
  headers: {
73
- 'Content-Type': exports.FREESIGNAL_MIME
89
+ 'Content-Type': types_1.XFreeSignal.MIME
74
90
  },
75
- body: (0, utils_1.encodeJSON)(message)
91
+ body: types_1.XFreeSignal.encodeBody('data', message)
76
92
  });
77
93
  return res.status === 200;
78
94
  });
@@ -82,10 +98,10 @@ class FreeSignalAPI {
82
98
  const res = yield fetch(url, {
83
99
  method: 'PUT',
84
100
  headers: {
85
- 'Content-Type': exports.FREESIGNAL_MIME,
101
+ 'Content-Type': types_1.XFreeSignal.MIME,
86
102
  authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
87
103
  },
88
- body: (0, utils_1.encodeJSON)(bundle)
104
+ body: types_1.XFreeSignal.encodeBody('data', bundle)
89
105
  });
90
106
  return res.status === 201;
91
107
  });
@@ -109,35 +125,35 @@ class FreeSignalAPI {
109
125
  authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
110
126
  }
111
127
  });
112
- return this.unpackDatagrams(yield this.decryptData(new Uint8Array(yield res.arrayBuffer()), types_1.UserId.getUserId(publicKey).toString()));
128
+ return types_1.DataEncoder.from(yield this.decryptData(new Uint8Array(yield res.arrayBuffer()), types_1.UserId.getUserId(publicKey).toString())).data.map(array => types_1.Datagram.from(array));
113
129
  });
114
130
  }
115
131
  postDatagrams(datagrams, publicKey, url) {
116
132
  return __awaiter(this, void 0, void 0, function* () {
117
- const data = yield this.encryptData(this.packDatagrams(datagrams), types_1.UserId.getUserId(publicKey).toString());
133
+ const data = yield this.encryptData(new types_1.DataEncoder(datagrams.map(datagram => types_1.Datagram.from(datagram).encode())).encode(), types_1.UserId.getUserId(publicKey).toString());
118
134
  const res = yield fetch(url, {
119
135
  method: 'POST',
120
136
  headers: {
121
- 'Content-Type': exports.FREESIGNAL_MIME,
137
+ 'Content-Type': types_1.XFreeSignal.MIME,
122
138
  authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
123
139
  },
124
- body: data.encode()
140
+ body: types_1.XFreeSignal.encodeBody('data', data.encode())
125
141
  });
126
- return (0, utils_1.numberFromUint8Array)(yield this.decryptData(new Uint8Array(yield res.arrayBuffer()), types_1.UserId.getUserId(publicKey).toString()));
142
+ return (0, utils_1.numberFromArray)(yield this.decryptData(new Uint8Array(yield res.arrayBuffer()), types_1.UserId.getUserId(publicKey).toString()));
127
143
  });
128
144
  }
129
145
  deleteDatagrams(datagramIds, publicKey, url) {
130
146
  return __awaiter(this, void 0, void 0, function* () {
131
- const data = yield this.encryptData(this.packIdList(datagramIds), types_1.UserId.getUserId(publicKey).toString());
147
+ const data = yield this.encryptData(new types_1.DataEncoder(datagramIds.map(datagramId => crypto_1.default.UUID.parse(datagramId))).encode(), types_1.UserId.getUserId(publicKey).toString());
132
148
  const res = yield fetch(url, {
133
149
  method: 'DELETE',
134
150
  headers: {
135
- 'Content-Type': exports.FREESIGNAL_MIME,
151
+ 'Content-Type': types_1.XFreeSignal.MIME,
136
152
  authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
137
153
  },
138
- body: data.encode()
154
+ body: types_1.XFreeSignal.encodeBody('data', data.encode())
139
155
  });
140
- return (0, utils_1.numberFromUint8Array)(yield this.decryptData(new Uint8Array(yield res.arrayBuffer()), types_1.UserId.getUserId(publicKey).toString()));
156
+ return (0, utils_1.numberFromArray)(yield this.decryptData(new Uint8Array(yield res.arrayBuffer()), types_1.UserId.getUserId(publicKey).toString()));
141
157
  });
142
158
  }
143
159
  createToken(publicKey) {
@@ -160,47 +176,5 @@ class FreeSignalAPI {
160
176
  throw new Error('Authorization header is required');
161
177
  });
162
178
  }
163
- packIdList(datagramIds) {
164
- return datagramIds.map(datagramId => crypto_1.default.UUID.parse(datagramId)).reduce((prev, curr) => new Uint8Array([...prev, ...curr]), new Uint8Array());
165
- }
166
- unpackIdList(data) {
167
- const ids = [];
168
- for (let i = 0; i < data.length; i += 16) {
169
- ids.push(crypto_1.default.UUID.stringify(data.subarray(i, i + 16)));
170
- }
171
- return ids;
172
- }
173
- packDatagrams(messages) {
174
- return fflate_1.default.deflateSync((0, utils_1.concatUint8Array)(...messages.flatMap(datagram => {
175
- const encoded = types_1.Datagram.from(datagram).encode();
176
- return [(0, utils_1.numberToUint8Array)(encoded.length, 8), encoded];
177
- })));
178
- }
179
- unpackDatagrams(data) {
180
- const messages = [];
181
- let offset = 0;
182
- data = fflate_1.default.inflateSync(data);
183
- while (offset < data.length) {
184
- const length = data.subarray(offset, offset + 8);
185
- if (length.length < 8) {
186
- throw new Error('Invalid message length');
187
- }
188
- const messageLength = (0, utils_1.numberFromUint8Array)(length);
189
- offset += 8;
190
- if (offset + messageLength > data.length) {
191
- throw new Error('Invalid message length');
192
- }
193
- const messageData = data.subarray(offset, offset + messageLength);
194
- offset += messageLength;
195
- try {
196
- const datagram = types_1.Datagram.from(messageData);
197
- messages.push(datagram);
198
- }
199
- catch (error) {
200
- throw new Error('Invalid datagram format');
201
- }
202
- }
203
- return messages;
204
- }
205
179
  }
206
180
  exports.FreeSignalAPI = FreeSignalAPI;
package/double-ratchet.js CHANGED
@@ -117,7 +117,9 @@ class KeySession {
117
117
  var _a;
118
118
  const encrypted = types_1.EncryptedData.from(payload);
119
119
  const publicKey = encrypted.publicKey;
120
- if (!(0, utils_1.verifyUint8Array)(publicKey, this._remoteKey)) {
120
+ if (!this._remoteKey)
121
+ throw new Error("Missing remoteKey");
122
+ if (!(0, utils_1.verifyArrays)(publicKey, this._remoteKey)) {
121
123
  while (this.receivingCount < encrypted.previous)
122
124
  this.previousKeys.set(this.receivingCount, this.getReceivingKey());
123
125
  this.setRemoteKey(publicKey);
@@ -143,7 +145,7 @@ class KeySession {
143
145
  */
144
146
  toJSON() {
145
147
  return {
146
- secretKey: (0, utils_1.decodeBase64)((0, utils_1.concatUint8Array)(this.keyPair.secretKey)),
148
+ secretKey: (0, utils_1.decodeBase64)((0, utils_1.concatArrays)(this.keyPair.secretKey)),
147
149
  remoteKey: (0, utils_1.decodeBase64)(this._remoteKey),
148
150
  rootKey: (0, utils_1.decodeBase64)(this.rootKey),
149
151
  sendingChain: (0, utils_1.decodeBase64)(this.sendingChain),
package/index.js CHANGED
@@ -61,15 +61,6 @@ function createKeyExchange(signSecretKey, boxSecretKey, bundleStore) {
61
61
  function createIdentityKeys(signSecretKey, boxSecretKey) {
62
62
  return { sign: crypto_1.default.EdDSA.keyPair(signSecretKey), box: crypto_1.default.ECDH.keyPair(boxSecretKey) };
63
63
  }
64
- /*export function createAPI(opts: {
65
- secretSignKey: Uint8Array;
66
- secretBoxKey: Uint8Array;
67
- sessions: LocalStorage<UserId, KeySession>;
68
- keyExchange: LocalStorage<string, Crypto.KeyPair>;
69
- users: LocalStorage<UserId, IdentityKeys>;
70
- }): FreeSignalAPI {
71
- return new FreeSignalAPI(opts);
72
- }*/
73
64
  var types_1 = require("./types");
74
65
  Object.defineProperty(exports, "UserId", { enumerable: true, get: function () { return types_1.UserId; } });
75
66
  Object.defineProperty(exports, "IdentityKeys", { enumerable: true, get: function () { return types_1.IdentityKeys; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freesignal/protocol",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Signal Protocol implementation in javascript",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "author": "Christian Braghette",
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "@freesignal/crypto": "^0.3.0",
15
15
  "@freesignal/interfaces": "^0.1.1",
16
- "@freesignal/utils": "^1.1.0",
16
+ "@freesignal/utils": "^1.2.0",
17
17
  "base64-js": "^1.5.1",
18
18
  "fflate": "^0.8.2",
19
19
  "js-sha3": "^0.9.3",
package/types.d.ts CHANGED
@@ -176,3 +176,43 @@ export declare class EncryptedDataConstructor implements EncryptedData {
176
176
  toString(): string;
177
177
  toJSON(): string;
178
178
  }
179
+ declare enum DataType {
180
+ UKNOWN = -1,
181
+ RAW = 0,
182
+ NUMBER = 1,
183
+ STRING = 2,
184
+ ARRAY = 3,
185
+ OBJECT = 4
186
+ }
187
+ declare namespace DataType {
188
+ function getType(type: string): DataType;
189
+ function getName(type: DataType): string;
190
+ function from(data: any): DataType;
191
+ }
192
+ export declare class DataEncoder<T> implements Encodable {
193
+ readonly data: T;
194
+ readonly type: string;
195
+ constructor(data: T);
196
+ protected get _type(): DataType;
197
+ encode(): Uint8Array;
198
+ toString(): string;
199
+ toJSON(): string;
200
+ static from<T = any>(array: Uint8Array): DataEncoder<T>;
201
+ }
202
+ export declare namespace XFreeSignal {
203
+ export const MIME = "application/x-freesignal";
204
+ export const version = 1;
205
+ export function encodeBody(type: 'data' | 'error', data: any, compressed?: boolean): BodyInit;
206
+ export function decodeBody<T = any>(body: Uint8Array): Body<T>;
207
+ class Body<T> implements Encodable {
208
+ readonly type: 'data' | 'error';
209
+ readonly data: T;
210
+ constructor(type: 'data' | 'error', data: T);
211
+ encode(compressed?: boolean): Uint8Array;
212
+ toString(): string;
213
+ toJSON(): string;
214
+ static from<T = any>(array: Uint8Array): Body<T>;
215
+ }
216
+ export {};
217
+ }
218
+ export {};
package/types.js CHANGED
@@ -21,10 +21,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
21
21
  return (mod && mod.__esModule) ? mod : { "default": mod };
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.EncryptedDataConstructor = exports.EncryptedData = exports.Datagram = exports.Protocols = exports.IdentityKeys = exports.UserId = void 0;
24
+ exports.XFreeSignal = exports.DataEncoder = exports.EncryptedDataConstructor = exports.EncryptedData = exports.Datagram = exports.Protocols = exports.IdentityKeys = exports.UserId = void 0;
25
25
  const utils_1 = require("@freesignal/utils");
26
26
  const crypto_1 = __importDefault(require("@freesignal/crypto"));
27
27
  const double_ratchet_1 = require("./double-ratchet");
28
+ const fflate_1 = __importDefault(require("fflate"));
28
29
  var UserId;
29
30
  (function (UserId) {
30
31
  class UserIdConstructor {
@@ -68,7 +69,7 @@ var IdentityKeys;
68
69
  }
69
70
  }
70
71
  encode() {
71
- return (0, utils_1.concatUint8Array)((0, utils_1.encodeBase64)(this.publicKey), (0, utils_1.encodeBase64)(this.identityKey));
72
+ return (0, utils_1.concatArrays)((0, utils_1.encodeBase64)(this.publicKey), (0, utils_1.encodeBase64)(this.identityKey));
72
73
  }
73
74
  toString() {
74
75
  throw (0, utils_1.decodeBase64)(this.encode());
@@ -107,11 +108,11 @@ var Protocols;
107
108
  }
108
109
  Protocols.toCode = toCode;
109
110
  function encode(protocol, length) {
110
- return (0, utils_1.numberToUint8Array)(Protocols.toCode(protocol), length);
111
+ return (0, utils_1.numberToArray)(Protocols.toCode(protocol), length);
111
112
  }
112
113
  Protocols.encode = encode;
113
114
  function decode(array) {
114
- return Protocols.fromCode((0, utils_1.numberFromUint8Array)(array));
115
+ return Protocols.fromCode((0, utils_1.numberFromArray)(array));
115
116
  }
116
117
  Protocols.decode = decode;
117
118
  })(Protocols || (exports.Protocols = Protocols = {}));
@@ -125,7 +126,7 @@ var Datagram;
125
126
  this.version = data[0] & 127;
126
127
  this.protocol = Protocols.decode(data.subarray(1, 2));
127
128
  this.id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
128
- this.createdAt = (0, utils_1.numberFromUint8Array)(data.subarray(18, 26));
129
+ this.createdAt = (0, utils_1.numberFromArray)(data.subarray(18, 26));
129
130
  this.sender = (0, utils_1.decodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength));
130
131
  this.receiver = (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, DatagramConstructor.headerOffset));
131
132
  if (data[0] & 128)
@@ -177,16 +178,16 @@ var Datagram;
177
178
  }
178
179
  encode() {
179
180
  var _a, _b, _c;
180
- const data = (0, utils_1.concatUint8Array)(new Uint8Array(1).fill(this.version | (this.secretKey ? 128 : 0)), //1
181
+ const data = (0, utils_1.concatArrays)(new Uint8Array(1).fill(this.version | (this.secretKey ? 128 : 0)), //1
181
182
  Protocols.encode(this.protocol), //1
182
183
  (_a = crypto_1.default.UUID.parse(this.id)) !== null && _a !== void 0 ? _a : [], //16
183
- (0, utils_1.numberToUint8Array)(this.createdAt, 8), //8
184
+ (0, utils_1.numberToArray)(this.createdAt, 8), //8
184
185
  (0, utils_1.encodeBase64)(this.sender), //32
185
186
  (0, utils_1.encodeBase64)(this.receiver), //32
186
187
  (_b = this._payload) !== null && _b !== void 0 ? _b : new Uint8Array());
187
188
  if (this.secretKey)
188
189
  this._signature = crypto_1.default.EdDSA.sign(data, this.secretKey);
189
- return (0, utils_1.concatUint8Array)(data, (_c = this._signature) !== null && _c !== void 0 ? _c : new Uint8Array());
190
+ return (0, utils_1.concatArrays)(data, (_c = this._signature) !== null && _c !== void 0 ? _c : new Uint8Array());
190
191
  }
191
192
  sign(secretKey) {
192
193
  this.secretKey = secretKey;
@@ -238,22 +239,22 @@ class EncryptedDataConstructor {
238
239
  return this;
239
240
  }
240
241
  if (typeof arrays[0] === 'number')
241
- arrays[0] = (0, utils_1.numberToUint8Array)(arrays[0], EncryptedDataConstructor.countLength);
242
+ arrays[0] = (0, utils_1.numberToArray)(arrays[0], EncryptedDataConstructor.countLength);
242
243
  if (typeof arrays[1] === 'number')
243
- arrays[1] = (0, utils_1.numberToUint8Array)(arrays[1], EncryptedDataConstructor.countLength);
244
+ arrays[1] = (0, utils_1.numberToArray)(arrays[1], EncryptedDataConstructor.countLength);
244
245
  if (arrays.length === 6) {
245
- arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToUint8Array)(arrays[5]) : arrays[5]);
246
+ arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToArray)(arrays[5]) : arrays[5]);
246
247
  arrays.pop();
247
248
  }
248
249
  else if (arrays.length > 1) {
249
- arrays.unshift((0, utils_1.numberToUint8Array)(double_ratchet_1.KeySession.version));
250
+ arrays.unshift((0, utils_1.numberToArray)(double_ratchet_1.KeySession.version));
250
251
  }
251
- this.raw = (0, utils_1.concatUint8Array)(...arrays);
252
+ this.raw = (0, utils_1.concatArrays)(...arrays);
252
253
  }
253
254
  get length() { return this.raw.length; }
254
- get version() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
255
- get count() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
256
- get previous() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
255
+ get version() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
256
+ get count() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
257
+ get previous() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
257
258
  get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
258
259
  get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
259
260
  get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
@@ -304,3 +305,156 @@ Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.count
304
305
  Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
305
306
  Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
306
307
  Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
308
+ var DataType;
309
+ (function (DataType) {
310
+ DataType[DataType["UKNOWN"] = -1] = "UKNOWN";
311
+ DataType[DataType["RAW"] = 0] = "RAW";
312
+ DataType[DataType["NUMBER"] = 1] = "NUMBER";
313
+ DataType[DataType["STRING"] = 2] = "STRING";
314
+ DataType[DataType["ARRAY"] = 3] = "ARRAY";
315
+ DataType[DataType["OBJECT"] = 4] = "OBJECT";
316
+ })(DataType || (DataType = {}));
317
+ (function (DataType) {
318
+ function getType(type) {
319
+ return Object.values(DataType).indexOf(type.toLocaleUpperCase());
320
+ }
321
+ DataType.getType = getType;
322
+ function getName(type) {
323
+ return DataType[type].toLowerCase();
324
+ }
325
+ DataType.getName = getName;
326
+ function from(data) {
327
+ if (data instanceof Uint8Array)
328
+ return DataType.RAW;
329
+ return getType(typeof data);
330
+ }
331
+ DataType.from = from;
332
+ })(DataType || (DataType = {}));
333
+ class DataEncoder {
334
+ constructor(data) {
335
+ this.data = data;
336
+ this.type = DataType.getName(DataType.from(this.data));
337
+ }
338
+ get _type() {
339
+ return DataType.getType(this.type);
340
+ }
341
+ encode() {
342
+ let data;
343
+ switch (this._type) {
344
+ case DataType.RAW:
345
+ data = this.data;
346
+ break;
347
+ case DataType.NUMBER:
348
+ data = (0, utils_1.numberToArray)(this._type);
349
+ break;
350
+ case DataType.STRING:
351
+ data = (0, utils_1.encodeUTF8)(this.data);
352
+ break;
353
+ case DataType.ARRAY:
354
+ data = (0, utils_1.concatArrays)(...Array.from(this.data).flatMap(value => {
355
+ const data = new DataEncoder(value).encode();
356
+ return [(0, utils_1.numberToArray)(data.length, 8), data];
357
+ }));
358
+ break;
359
+ case DataType.OBJECT:
360
+ data = (0, utils_1.encodeJSON)(this.data);
361
+ break;
362
+ default:
363
+ throw new Error("Uknown type");
364
+ }
365
+ return (0, utils_1.concatArrays)((0, utils_1.numberToArray)(this._type), data);
366
+ }
367
+ toString() {
368
+ return "[Object XFreeSignalData]";
369
+ }
370
+ toJSON() {
371
+ return JSON.stringify(this.data);
372
+ }
373
+ static from(array) {
374
+ const type = array[0];
375
+ let rawData = array.subarray(1), data;
376
+ switch (type) {
377
+ case DataType.RAW:
378
+ data = rawData;
379
+ break;
380
+ case DataType.NUMBER:
381
+ data = (0, utils_1.numberFromArray)(rawData);
382
+ break;
383
+ case DataType.STRING:
384
+ data = (0, utils_1.decodeUTF8)(rawData);
385
+ break;
386
+ case DataType.ARRAY:
387
+ const arrayData = [];
388
+ let offset = 0;
389
+ while (offset < rawData.length) {
390
+ const length = rawData.subarray(offset, offset + 8);
391
+ if (length.length < 8)
392
+ throw new Error('Invalid data length');
393
+ const messageLength = (0, utils_1.numberFromArray)(length);
394
+ offset += 8;
395
+ if (offset + messageLength > rawData.length) {
396
+ throw new Error('Invalid data length');
397
+ }
398
+ arrayData.push(rawData.subarray(offset, offset + messageLength));
399
+ offset += messageLength;
400
+ }
401
+ data = arrayData;
402
+ break;
403
+ case DataType.OBJECT:
404
+ data = (0, utils_1.decodeJSON)(rawData);
405
+ break;
406
+ default:
407
+ throw new Error('Invalid data format');
408
+ }
409
+ return new DataEncoder(data);
410
+ }
411
+ }
412
+ exports.DataEncoder = DataEncoder;
413
+ var XFreeSignal;
414
+ (function (XFreeSignal) {
415
+ XFreeSignal.MIME = "application/x-freesignal";
416
+ XFreeSignal.version = 1;
417
+ function encodeBody(type, data, compressed = false) {
418
+ return new Body(type, data).encode(compressed);
419
+ }
420
+ XFreeSignal.encodeBody = encodeBody;
421
+ function decodeBody(body) {
422
+ return Body.from(body);
423
+ }
424
+ XFreeSignal.decodeBody = decodeBody;
425
+ let BodyType;
426
+ (function (BodyType) {
427
+ BodyType[BodyType["DATA"] = 0] = "DATA";
428
+ BodyType[BodyType["ERROR"] = 1] = "ERROR";
429
+ })(BodyType || (BodyType = {}));
430
+ (function (BodyType) {
431
+ function getName(type) {
432
+ return BodyType[type].toLowerCase();
433
+ }
434
+ BodyType.getName = getName;
435
+ })(BodyType || (BodyType = {}));
436
+ class Body {
437
+ constructor(type, data) {
438
+ this.type = type;
439
+ this.data = data;
440
+ }
441
+ encode(compressed = false) {
442
+ const data = new DataEncoder(this.data).encode();
443
+ return (0, utils_1.concatArrays)((0, utils_1.numberToArray)(((this.type === 'data' ? BodyType.DATA : BodyType.ERROR) << 6)
444
+ + (compressed ? 32 : 0)
445
+ + XFreeSignal.version), compressed ? fflate_1.default.deflateSync(data) : data);
446
+ }
447
+ toString() {
448
+ return "[Object XFreeSignalBody]";
449
+ }
450
+ toJSON() {
451
+ return JSON.stringify({
452
+ type: this.type,
453
+ data: this.data
454
+ });
455
+ }
456
+ static from(array) {
457
+ return new Body(BodyType.getName((array[0] & 64) >> 6), DataEncoder.from((array[0] & 32) >> 5 === 1 ? fflate_1.default.inflateSync(array.subarray(1)) : array.subarray(1)).data);
458
+ }
459
+ }
460
+ })(XFreeSignal || (exports.XFreeSignal = XFreeSignal = {}));
package/x3dh.js CHANGED
@@ -94,7 +94,7 @@ class KeyExchange {
94
94
  ...onetimePreKey ? crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
95
95
  ]), new Uint8Array(double_ratchet_1.KeySession.rootKeyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.rootKeyLength);
96
96
  const session = new double_ratchet_1.KeySession({ remoteKey: identityKey, rootKey });
97
- const cyphertext = session.encrypt((0, utils_1.concatUint8Array)(crypto_1.default.hash(this._identityKey.publicKey), crypto_1.default.hash(identityKey)));
97
+ const cyphertext = session.encrypt((0, utils_1.concatArrays)(crypto_1.default.hash(this._identityKey.publicKey), crypto_1.default.hash(identityKey)));
98
98
  if (!cyphertext)
99
99
  throw new Error("Decryption error");
100
100
  return {
@@ -135,7 +135,7 @@ class KeyExchange {
135
135
  const cleartext = session.decrypt((0, utils_1.encodeBase64)(message.associatedData));
136
136
  if (!cleartext)
137
137
  throw new Error("Error decrypting ACK message");
138
- if (!(0, utils_1.verifyUint8Array)(cleartext, (0, utils_1.concatUint8Array)(crypto_1.default.hash(identityKey), crypto_1.default.hash(this._identityKey.publicKey))))
138
+ if (!(0, utils_1.verifyArrays)(cleartext, (0, utils_1.concatArrays)(crypto_1.default.hash(identityKey), crypto_1.default.hash(this._identityKey.publicKey))))
139
139
  throw new Error("Error verifing Associated Data");
140
140
  return {
141
141
  session,