@freesignal/protocol 0.2.4 → 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
- import { Crypto, LocalStorage } from "@freesignal/interfaces";
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
+ */
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;
@@ -10,6 +27,7 @@ export declare class FreeSignalAPI {
10
27
  protected readonly sessions: LocalStorage<UserId, KeySession>;
11
28
  protected readonly keyExchange: KeyExchange;
12
29
  protected readonly users: LocalStorage<UserId, IdentityKeys>;
30
+ readonly userId: UserId;
13
31
  constructor(opts: {
14
32
  secretSignKey: Uint8Array;
15
33
  secretBoxKey: Uint8Array;
@@ -17,10 +35,13 @@ export declare class FreeSignalAPI {
17
35
  keyExchange: LocalStorage<string, Crypto.KeyPair>;
18
36
  users: LocalStorage<UserId, IdentityKeys>;
19
37
  });
20
- get userId(): Uint8Array;
21
38
  get identityKeys(): IdentityKeys;
22
39
  encryptData(data: Uint8Array, userId: string): Promise<EncryptedData>;
23
40
  decryptData(data: Uint8Array, userId: string): Promise<Uint8Array>;
41
+ getHandshake(url: string, userId?: UserId): Promise<KeyExchangeData>;
42
+ postHandshake(url: string, message: KeyExchangeSynMessage): Promise<boolean>;
43
+ putHandshake(url: string, publicKey: string | Uint8Array, bundle: KeyExchangeDataBundle): Promise<boolean>;
44
+ deleteHandshake(url: string, publicKey: string | Uint8Array): Promise<boolean>;
24
45
  getDatagrams(publicKey: string | Uint8Array, url: string): Promise<Datagram[]>;
25
46
  postDatagrams(datagrams: Datagram[], publicKey: string | Uint8Array, url: string): Promise<number>;
26
47
  deleteDatagrams(datagramIds: DatagramId[], publicKey: string | Uint8Array, url: string): Promise<number>;
@@ -29,9 +50,5 @@ export declare class FreeSignalAPI {
29
50
  identityKeys: IdentityKeys;
30
51
  userId: UserId;
31
52
  }>;
32
- protected packIdList(datagramIds: DatagramId[]): Uint8Array;
33
- protected unpackIdList(data: Uint8Array): DatagramId[];
34
- protected packDatagrams(messages: Datagram[]): Uint8Array;
35
- protected unpackDatagrams(data: Uint8Array): Datagram[];
36
53
  }
37
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;
@@ -27,9 +43,7 @@ class FreeSignalAPI {
27
43
  this.sessions = sessions;
28
44
  this.keyExchange = new x3dh_1.KeyExchange(secretSignKey, secretBoxKey, keyExchange);
29
45
  this.users = users;
30
- }
31
- get userId() {
32
- return crypto_1.default.hash(this.signKey.publicKey);
46
+ this.userId = types_1.UserId.getUserId(this.signKey.publicKey).toString();
33
47
  }
34
48
  get identityKeys() {
35
49
  return {
@@ -59,6 +73,50 @@ class FreeSignalAPI {
59
73
  return decrypted;
60
74
  });
61
75
  }
76
+ getHandshake(url, userId) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ const res = yield fetch(`${url}/${userId !== null && userId !== void 0 ? userId : ''}`, {
79
+ method: 'GET'
80
+ });
81
+ return (0, utils_1.decodeJSON)(new Uint8Array(yield res.arrayBuffer()));
82
+ });
83
+ }
84
+ postHandshake(url, message) {
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ const res = yield fetch(url, {
87
+ method: 'POST',
88
+ headers: {
89
+ 'Content-Type': types_1.XFreeSignal.MIME
90
+ },
91
+ body: types_1.XFreeSignal.encodeBody('data', message)
92
+ });
93
+ return res.status === 200;
94
+ });
95
+ }
96
+ putHandshake(url, publicKey, bundle) {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ const res = yield fetch(url, {
99
+ method: 'PUT',
100
+ headers: {
101
+ 'Content-Type': types_1.XFreeSignal.MIME,
102
+ authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
103
+ },
104
+ body: types_1.XFreeSignal.encodeBody('data', bundle)
105
+ });
106
+ return res.status === 201;
107
+ });
108
+ }
109
+ deleteHandshake(url, publicKey) {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ const res = yield fetch(url, {
112
+ method: 'DELETE',
113
+ headers: {
114
+ authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
115
+ }
116
+ });
117
+ return res.status === 200;
118
+ });
119
+ }
62
120
  getDatagrams(publicKey, url) {
63
121
  return __awaiter(this, void 0, void 0, function* () {
64
122
  const res = yield fetch(url, {
@@ -67,50 +125,50 @@ class FreeSignalAPI {
67
125
  authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
68
126
  }
69
127
  });
70
- 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));
71
129
  });
72
130
  }
73
131
  postDatagrams(datagrams, publicKey, url) {
74
132
  return __awaiter(this, void 0, void 0, function* () {
75
- 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());
76
134
  const res = yield fetch(url, {
77
135
  method: 'POST',
78
136
  headers: {
79
- 'Content-Type': exports.FREESIGNAL_MIME,
137
+ 'Content-Type': types_1.XFreeSignal.MIME,
80
138
  authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
81
139
  },
82
- body: data.encode()
140
+ body: types_1.XFreeSignal.encodeBody('data', data.encode())
83
141
  });
84
- 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()));
85
143
  });
86
144
  }
87
145
  deleteDatagrams(datagramIds, publicKey, url) {
88
146
  return __awaiter(this, void 0, void 0, function* () {
89
- 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());
90
148
  const res = yield fetch(url, {
91
149
  method: 'DELETE',
92
150
  headers: {
93
- 'Content-Type': exports.FREESIGNAL_MIME,
151
+ 'Content-Type': types_1.XFreeSignal.MIME,
94
152
  authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
95
153
  },
96
- body: data.encode()
154
+ body: types_1.XFreeSignal.encodeBody('data', data.encode())
97
155
  });
98
- 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()));
99
157
  });
100
158
  }
101
159
  createToken(publicKey) {
102
- const sharedId = crypto_1.default.hash(crypto_1.default.ECDH.scalarMult(publicKey, this.boxKey.secretKey));
103
- return `Bearer ${(0, utils_1.decodeBase64)(this.userId)}:${(0, utils_1.decodeBase64)(sharedId)}`;
160
+ const signature = crypto_1.default.EdDSA.sign(crypto_1.default.hash(crypto_1.default.ECDH.scalarMult(publicKey, this.boxKey.secretKey)), this.signKey.secretKey);
161
+ return `Bearer ${this.userId}:${(0, utils_1.decodeBase64)(signature)}`;
104
162
  }
105
163
  ;
106
164
  digestToken(auth) {
107
165
  return __awaiter(this, void 0, void 0, function* () {
108
166
  if (auth && auth.startsWith("Bearer ")) {
109
- const [userId, sharedId] = auth.substring(7).split(":");
167
+ const [userId, signature] = auth.substring(7).split(":");
110
168
  const identityKeys = yield this.users.get(userId);
111
169
  if (!identityKeys)
112
170
  throw new Error('User not found or invalid auth token');
113
- if ((0, utils_1.verifyUint8Array)(crypto_1.default.hash(crypto_1.default.ECDH.scalarMult((0, utils_1.encodeBase64)(identityKeys.publicKey), this.boxKey.secretKey)), (0, utils_1.encodeBase64)(sharedId)))
171
+ if (crypto_1.default.EdDSA.verify(crypto_1.default.hash(crypto_1.default.ECDH.scalarMult((0, utils_1.encodeBase64)(identityKeys.identityKey), this.boxKey.secretKey)), (0, utils_1.encodeBase64)(signature), (0, utils_1.encodeBase64)(identityKeys.publicKey)))
114
172
  return { identityKeys, userId: auth };
115
173
  else
116
174
  throw new Error('Authorization token not valid');
@@ -118,47 +176,5 @@ class FreeSignalAPI {
118
176
  throw new Error('Authorization header is required');
119
177
  });
120
178
  }
121
- packIdList(datagramIds) {
122
- return datagramIds.map(datagramId => crypto_1.default.UUID.parse(datagramId)).reduce((prev, curr) => new Uint8Array([...prev, ...curr]), new Uint8Array());
123
- }
124
- unpackIdList(data) {
125
- const ids = [];
126
- for (let i = 0; i < data.length; i += 16) {
127
- ids.push(crypto_1.default.UUID.stringify(data.subarray(i, i + 16)));
128
- }
129
- return ids;
130
- }
131
- packDatagrams(messages) {
132
- return fflate_1.default.deflateSync((0, utils_1.concatUint8Array)(...messages.flatMap(datagram => {
133
- const encoded = types_1.Datagram.from(datagram).encode();
134
- return [(0, utils_1.numberToUint8Array)(encoded.length, 8), encoded];
135
- })));
136
- }
137
- unpackDatagrams(data) {
138
- const messages = [];
139
- let offset = 0;
140
- data = fflate_1.default.inflateSync(data);
141
- while (offset < data.length) {
142
- const length = data.subarray(offset, offset + 8);
143
- if (length.length < 8) {
144
- throw new Error('Invalid message length');
145
- }
146
- const messageLength = (0, utils_1.numberFromUint8Array)(length);
147
- offset += 8;
148
- if (offset + messageLength > data.length) {
149
- throw new Error('Invalid message length');
150
- }
151
- const messageData = data.subarray(offset, offset + messageLength);
152
- offset += messageLength;
153
- try {
154
- const datagram = types_1.Datagram.from(messageData);
155
- messages.push(datagram);
156
- }
157
- catch (error) {
158
- throw new Error('Invalid datagram format');
159
- }
160
- }
161
- return messages;
162
- }
163
179
  }
164
180
  exports.FreeSignalAPI = FreeSignalAPI;
@@ -83,14 +83,14 @@ export declare class KeySession {
83
83
  /**
84
84
  * Export the state of the session;
85
85
  */
86
- export(): ExportedKeySession;
86
+ toJSON(): ExportedKeySession;
87
87
  /**
88
88
  * Import a state.
89
89
  *
90
90
  * @param json string returned by `export()` method.
91
91
  * @returns session with the state parsed.
92
92
  */
93
- static import(json: string): KeySession;
93
+ static parse(json: string): KeySession;
94
94
  /**
95
95
  * The fixed key length (in bytes) used throughout the Double Ratchet session.
96
96
  * Typically 32 bytes (256 bits) for symmetric keys.
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);
@@ -141,9 +143,9 @@ class KeySession {
141
143
  /**
142
144
  * Export the state of the session;
143
145
  */
144
- export() {
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),
@@ -160,7 +162,7 @@ class KeySession {
160
162
  * @param json string returned by `export()` method.
161
163
  * @returns session with the state parsed.
162
164
  */
163
- static import(json) {
165
+ static parse(json) {
164
166
  const data = JSON.parse(json);
165
167
  const session = new KeySession({ secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: (0, utils_1.encodeBase64)(data.rootKey) });
166
168
  session._remoteKey = (0, utils_1.encodeBase64)(data.remoteKey);
package/index.d.ts CHANGED
@@ -21,12 +21,13 @@ import { LocalStorage, Crypto } from "@freesignal/interfaces";
21
21
  import { KeySession } from "./double-ratchet";
22
22
  import { KeyExchange } from "./x3dh";
23
23
  /**
24
- * Creates a new Double Ratchet session.
24
+ * Creates a new Double Ratchet session for secure message exchange.
25
25
  *
26
- * @param opts.remoteKey The public key of the remote party.
27
- * @param opts.preSharedKey An optional pre-shared key to initialize the session.
28
- *
29
- * @returns A new Double Ratchet session.
26
+ * @param opts - Optional parameters for session initialization.
27
+ * @param opts.secretKey - The local party's secret key as a Uint8Array.
28
+ * @param opts.remoteKey - The remote party's public key as a Uint8Array.
29
+ * @param opts.rootKey - An optional root key to initialize the session.
30
+ * @returns A new instance of {@link KeySession}.
30
31
  */
31
32
  export declare function createKeySession(opts?: {
32
33
  secretKey?: Uint8Array;
@@ -34,15 +35,23 @@ export declare function createKeySession(opts?: {
34
35
  rootKey?: Uint8Array;
35
36
  }): KeySession;
36
37
  /**
37
- * Creates a new X3DH session.
38
+ * Creates a new X3DH (Extended Triple Diffie-Hellman) key exchange session.
38
39
  *
39
- * @param signKeyPair
40
- * @param bundleStore
41
- * @returns A new X3DH session.
40
+ * @param signSecretKey - The EdDSA signing secret key as a Uint8Array.
41
+ * @param boxSecretKey - The ECDH box secret key as a Uint8Array.
42
+ * @param bundleStore - Optional local storage for key bundles.
43
+ * @returns A new instance of {@link KeyExchange}.
42
44
  */
43
45
  export declare function createKeyExchange(signSecretKey: Uint8Array, boxSecretKey: Uint8Array, bundleStore?: LocalStorage<string, crypto.KeyPair>): KeyExchange;
46
+ /**
47
+ * Generates identity key pairs for signing and encryption.
48
+ *
49
+ * @param signSecretKey - Optional secret key for EdDSA signing.
50
+ * @param boxSecretKey - Optional secret key for ECDH encryption.
51
+ * @returns An object containing readonly signing and box key pairs.
52
+ */
44
53
  export declare function createIdentityKeys(signSecretKey?: Uint8Array, boxSecretKey?: Uint8Array): {
45
- sign: Crypto.KeyPair;
46
- box: Crypto.KeyPair;
54
+ readonly sign: Crypto.KeyPair;
55
+ readonly box: Crypto.KeyPair;
47
56
  };
48
- export { IdentityKeys, Protocols, EncryptedData, Datagram } from "./types";
57
+ export { UserId, IdentityKeys, Protocols, Datagram, EncryptedData } from "./types";
package/index.js CHANGED
@@ -21,7 +21,7 @@ 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.Datagram = exports.EncryptedData = exports.Protocols = exports.IdentityKeys = void 0;
24
+ exports.EncryptedData = exports.Datagram = exports.Protocols = exports.IdentityKeys = exports.UserId = void 0;
25
25
  exports.createKeySession = createKeySession;
26
26
  exports.createKeyExchange = createKeyExchange;
27
27
  exports.createIdentityKeys = createIdentityKeys;
@@ -29,43 +29,41 @@ const crypto_1 = __importDefault(require("@freesignal/crypto"));
29
29
  const double_ratchet_1 = require("./double-ratchet");
30
30
  const x3dh_1 = require("./x3dh");
31
31
  /**
32
- * Creates a new Double Ratchet session.
32
+ * Creates a new Double Ratchet session for secure message exchange.
33
33
  *
34
- * @param opts.remoteKey The public key of the remote party.
35
- * @param opts.preSharedKey An optional pre-shared key to initialize the session.
36
- *
37
- * @returns A new Double Ratchet session.
34
+ * @param opts - Optional parameters for session initialization.
35
+ * @param opts.secretKey - The local party's secret key as a Uint8Array.
36
+ * @param opts.remoteKey - The remote party's public key as a Uint8Array.
37
+ * @param opts.rootKey - An optional root key to initialize the session.
38
+ * @returns A new instance of {@link KeySession}.
38
39
  */
39
40
  function createKeySession(opts) {
40
41
  return new double_ratchet_1.KeySession(opts);
41
42
  }
42
43
  /**
43
- * Creates a new X3DH session.
44
+ * Creates a new X3DH (Extended Triple Diffie-Hellman) key exchange session.
44
45
  *
45
- * @param signKeyPair
46
- * @param bundleStore
47
- * @returns A new X3DH session.
46
+ * @param signSecretKey - The EdDSA signing secret key as a Uint8Array.
47
+ * @param boxSecretKey - The ECDH box secret key as a Uint8Array.
48
+ * @param bundleStore - Optional local storage for key bundles.
49
+ * @returns A new instance of {@link KeyExchange}.
48
50
  */
49
51
  function createKeyExchange(signSecretKey, boxSecretKey, bundleStore) {
50
52
  return new x3dh_1.KeyExchange(signSecretKey, boxSecretKey, bundleStore);
51
53
  }
54
+ /**
55
+ * Generates identity key pairs for signing and encryption.
56
+ *
57
+ * @param signSecretKey - Optional secret key for EdDSA signing.
58
+ * @param boxSecretKey - Optional secret key for ECDH encryption.
59
+ * @returns An object containing readonly signing and box key pairs.
60
+ */
52
61
  function createIdentityKeys(signSecretKey, boxSecretKey) {
53
- return {
54
- sign: crypto_1.default.EdDSA.keyPair(signSecretKey),
55
- box: crypto_1.default.ECDH.keyPair(boxSecretKey)
56
- };
62
+ return { sign: crypto_1.default.EdDSA.keyPair(signSecretKey), box: crypto_1.default.ECDH.keyPair(boxSecretKey) };
57
63
  }
58
- /*export function createAPI(opts: {
59
- secretSignKey: Uint8Array;
60
- secretBoxKey: Uint8Array;
61
- sessions: LocalStorage<UserId, KeySession>;
62
- keyExchange: LocalStorage<string, Crypto.KeyPair>;
63
- users: LocalStorage<UserId, IdentityKeys>;
64
- }): FreeSignalAPI {
65
- return new FreeSignalAPI(opts);
66
- }*/
67
64
  var types_1 = require("./types");
65
+ Object.defineProperty(exports, "UserId", { enumerable: true, get: function () { return types_1.UserId; } });
68
66
  Object.defineProperty(exports, "IdentityKeys", { enumerable: true, get: function () { return types_1.IdentityKeys; } });
69
67
  Object.defineProperty(exports, "Protocols", { enumerable: true, get: function () { return types_1.Protocols; } });
70
- Object.defineProperty(exports, "EncryptedData", { enumerable: true, get: function () { return types_1.EncryptedData; } });
71
68
  Object.defineProperty(exports, "Datagram", { enumerable: true, get: function () { return types_1.Datagram; } });
69
+ Object.defineProperty(exports, "EncryptedData", { enumerable: true, get: function () { return types_1.EncryptedData; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freesignal/protocol",
3
- "version": "0.2.4",
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.0.2",
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/test.js CHANGED
@@ -24,7 +24,6 @@ bob.digestMessage(aliceack).then(({ session: bobsession, identityKeys }) => {
24
24
  console.log("Error during handshake");
25
25
  const longmsg = _1.Datagram.create(alice.signatureKey, bob.signatureKey, _1.Protocols.MESSAGE, alicesession.encrypt(new Uint8Array(1000000).fill(33).map(val => val + Math.floor(Math.random() * 93))));
26
26
  console.log(longmsg.encode().length);
27
- console.log(longmsg.encode(false).length);
28
27
  }
29
28
  else
30
29
  console.log("Error");
package/types.d.ts CHANGED
@@ -23,6 +23,7 @@ export declare namespace UserId {
23
23
  private readonly array;
24
24
  constructor(array: Uint8Array);
25
25
  toString(): string;
26
+ toJSON(): string;
26
27
  toUint8Array(): Uint8Array;
27
28
  }
28
29
  export function getUserId(publicKey: string | Uint8Array): UserIdConstructor;
@@ -49,9 +50,9 @@ export declare namespace IdentityKeys {
49
50
  }
50
51
  export declare enum Protocols {
51
52
  NULL = "",
52
- MESSAGE = "/freesignal/message/1.0.0",
53
- RELAY = "/freesignal/relay/1.0.0",
54
- HANDSHAKE = "/freesignal/handshake/1.0.0"
53
+ MESSAGE = "/freesignal/message",
54
+ RELAY = "/freesignal/relay",
55
+ HANDSHAKE = "/freesignal/handshake"
55
56
  }
56
57
  export declare namespace Protocols {
57
58
  function isProtocol(protocol: any): boolean;
@@ -68,6 +69,7 @@ export interface Datagram {
68
69
  readonly protocol: Protocols;
69
70
  readonly createdAt: number;
70
71
  payload?: Uint8Array;
72
+ readonly signature?: string;
71
73
  }
72
74
  export declare namespace Datagram {
73
75
  export const version = 1;
@@ -78,12 +80,18 @@ export declare namespace Datagram {
78
80
  readonly receiver: UserId;
79
81
  readonly protocol: Protocols;
80
82
  readonly createdAt: number;
81
- payload?: Uint8Array;
83
+ _payload?: Uint8Array;
84
+ _signature?: Uint8Array;
85
+ private secretKey?;
82
86
  private static headerOffset;
83
87
  constructor(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable);
84
88
  constructor(data: Uint8Array | Datagram);
85
- encode(compression?: boolean): Uint8Array;
86
- encodeSigned(secretKey: Uint8Array, compression?: boolean): Uint8Array;
89
+ get signed(): boolean;
90
+ get signature(): string | undefined;
91
+ set payload(data: Uint8Array);
92
+ get payload(): Uint8Array | undefined;
93
+ encode(): Uint8Array;
94
+ sign(secretKey: Uint8Array): this;
87
95
  toString(): string;
88
96
  toJSON(): string;
89
97
  }
@@ -165,14 +173,46 @@ export declare class EncryptedDataConstructor implements EncryptedData {
165
173
  get nonce(): Uint8Array;
166
174
  get ciphertext(): Uint8Array;
167
175
  encode(): Uint8Array;
168
- decode(): {
169
- version: number;
170
- count: number;
171
- previous: number;
172
- publicKey: string;
173
- nonce: string;
174
- ciphertext: string;
175
- };
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,11 +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
- const fflate_1 = __importDefault(require("fflate"));
28
27
  const double_ratchet_1 = require("./double-ratchet");
28
+ const fflate_1 = __importDefault(require("fflate"));
29
29
  var UserId;
30
30
  (function (UserId) {
31
31
  class UserIdConstructor {
@@ -36,6 +36,9 @@ var UserId;
36
36
  toString() {
37
37
  return (0, utils_1.decodeBase64)(this.array);
38
38
  }
39
+ toJSON() {
40
+ return JSON.stringify(this.toString());
41
+ }
39
42
  toUint8Array() {
40
43
  return this.array;
41
44
  }
@@ -66,13 +69,13 @@ var IdentityKeys;
66
69
  }
67
70
  }
68
71
  encode() {
69
- 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));
70
73
  }
71
74
  toString() {
72
75
  throw (0, utils_1.decodeBase64)(this.encode());
73
76
  }
74
77
  toJSON() {
75
- throw this.toString();
78
+ throw JSON.stringify(this.toString());
76
79
  }
77
80
  }
78
81
  function isIdentityKeys(obj) {
@@ -87,9 +90,9 @@ var IdentityKeys;
87
90
  var Protocols;
88
91
  (function (Protocols) {
89
92
  Protocols["NULL"] = "";
90
- Protocols["MESSAGE"] = "/freesignal/message/1.0.0";
91
- Protocols["RELAY"] = "/freesignal/relay/1.0.0";
92
- Protocols["HANDSHAKE"] = "/freesignal/handshake/1.0.0";
93
+ Protocols["MESSAGE"] = "/freesignal/message";
94
+ Protocols["RELAY"] = "/freesignal/relay";
95
+ Protocols["HANDSHAKE"] = "/freesignal/handshake";
93
96
  })(Protocols || (exports.Protocols = Protocols = {}));
94
97
  (function (Protocols) {
95
98
  function isProtocol(protocol) {
@@ -105,16 +108,11 @@ var Protocols;
105
108
  }
106
109
  Protocols.toCode = toCode;
107
110
  function encode(protocol, length) {
108
- /*const raw = numberToUint8Array(Protocols.toCode(protocol), length).reverse();
109
- raw[0] |= (raw.length - 1) << 6;
110
- return raw;*/
111
- return (0, utils_1.numberToUint8Array)(Protocols.toCode(protocol), length);
111
+ return (0, utils_1.numberToArray)(Protocols.toCode(protocol), length);
112
112
  }
113
113
  Protocols.encode = encode;
114
114
  function decode(array) {
115
- //array[0] &= 63;
116
- //array = array.reverse();
117
- return Protocols.fromCode((0, utils_1.numberFromUint8Array)(array));
115
+ return Protocols.fromCode((0, utils_1.numberFromArray)(array));
118
116
  }
119
117
  Protocols.decode = decode;
120
118
  })(Protocols || (exports.Protocols = Protocols = {}));
@@ -125,21 +123,15 @@ var Datagram;
125
123
  constructor(data, receiver, protocol, payload) {
126
124
  if (!receiver && !protocol && !payload) {
127
125
  if (data instanceof Uint8Array) {
128
- this.version = data[0] & 63;
126
+ this.version = data[0] & 127;
129
127
  this.protocol = Protocols.decode(data.subarray(1, 2));
130
128
  this.id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
131
- this.createdAt = (0, utils_1.numberFromUint8Array)(data.subarray(18, 26));
129
+ this.createdAt = (0, utils_1.numberFromArray)(data.subarray(18, 26));
132
130
  this.sender = (0, utils_1.decodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength));
133
131
  this.receiver = (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, DatagramConstructor.headerOffset));
134
- if (data[0] & 128) {
135
- const signature = data.subarray(data.length - crypto_1.default.EdDSA.signatureLength);
136
- if (!crypto_1.default.EdDSA.verify(data.subarray(0, data.length - crypto_1.default.EdDSA.signatureLength), signature, data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength)))
137
- throw new Error('Invalid signature for Datagram');
138
- }
139
- if (data[0] & 64)
140
- this.payload = fflate_1.default.inflateSync(data.subarray(DatagramConstructor.headerOffset, data.length));
141
- else
142
- this.payload = data.subarray(DatagramConstructor.headerOffset, data.length);
132
+ if (data[0] & 128)
133
+ this._signature = data.subarray(data.length - crypto_1.default.EdDSA.signatureLength);
134
+ this._payload = data.subarray(DatagramConstructor.headerOffset, data.length);
143
135
  }
144
136
  else if (Datagram.isDatagram(data)) {
145
137
  const datagram = data;
@@ -149,7 +141,8 @@ var Datagram;
149
141
  this.receiver = datagram.receiver;
150
142
  this.protocol = datagram.protocol;
151
143
  this.createdAt = datagram.createdAt;
152
- this.payload = datagram.payload;
144
+ this._payload = datagram.payload;
145
+ this._signature = (0, utils_1.encodeBase64)(datagram.signature);
153
146
  }
154
147
  else
155
148
  throw new Error('Invalid constructor arguments for Datagram');
@@ -161,45 +154,50 @@ var Datagram;
161
154
  this.receiver = typeof receiver === 'string' ? receiver : (0, utils_1.decodeBase64)(receiver);
162
155
  this.protocol = protocol;
163
156
  this.createdAt = Date.now();
164
- this.payload = payload instanceof Uint8Array ? payload : payload === null || payload === void 0 ? void 0 : payload.encode();
157
+ this._payload = payload instanceof Uint8Array ? payload : payload === null || payload === void 0 ? void 0 : payload.encode();
165
158
  }
166
159
  else
167
160
  throw new Error('Invalid constructor arguments for Datagram');
168
161
  }
169
- encode(compression = true) {
170
- var _a;
171
- compression = compression && this.payload != undefined && this.payload.length > 1024;
172
- return (0, utils_1.concatUint8Array)(new Uint8Array(1).fill(this.version | (compression ? 64 : 0)), //1
162
+ get signed() {
163
+ return !this._signature && !this.secretKey ? false : true;
164
+ }
165
+ get signature() {
166
+ if (this.signed) {
167
+ if (!this._signature)
168
+ this.encode();
169
+ return (0, utils_1.decodeBase64)(this._signature);
170
+ }
171
+ }
172
+ set payload(data) {
173
+ this._signature = undefined;
174
+ this._payload = data;
175
+ }
176
+ get payload() {
177
+ return this._payload;
178
+ }
179
+ encode() {
180
+ var _a, _b, _c;
181
+ const data = (0, utils_1.concatArrays)(new Uint8Array(1).fill(this.version | (this.secretKey ? 128 : 0)), //1
173
182
  Protocols.encode(this.protocol), //1
174
183
  (_a = crypto_1.default.UUID.parse(this.id)) !== null && _a !== void 0 ? _a : [], //16
175
- (0, utils_1.numberToUint8Array)(this.createdAt, 8), //8
184
+ (0, utils_1.numberToArray)(this.createdAt, 8), //8
176
185
  (0, utils_1.encodeBase64)(this.sender), //32
177
186
  (0, utils_1.encodeBase64)(this.receiver), //32
178
- ...(this.payload ? [compression ? fflate_1.default.deflateSync(this.payload) : this.payload] : []));
187
+ (_b = this._payload) !== null && _b !== void 0 ? _b : new Uint8Array());
188
+ if (this.secretKey)
189
+ this._signature = crypto_1.default.EdDSA.sign(data, this.secretKey);
190
+ return (0, utils_1.concatArrays)(data, (_c = this._signature) !== null && _c !== void 0 ? _c : new Uint8Array());
179
191
  }
180
- encodeSigned(secretKey, compression) {
181
- //if (!this.payload) throw new Error('Cannot sign a datagram without payload');
182
- const header = this.encode(compression);
183
- header[0] |= 128; // Set the sign bit
184
- const signature = crypto_1.default.EdDSA.sign(header, secretKey);
185
- return (0, utils_1.concatUint8Array)(header, signature);
192
+ sign(secretKey) {
193
+ this.secretKey = secretKey;
194
+ return this;
186
195
  }
187
196
  toString() {
188
197
  return (0, utils_1.decodeBase64)(this.encode());
189
198
  }
190
199
  toJSON() {
191
- /*return JSON.stringify({
192
- id: this.id,
193
- version: this.version,
194
- senderKey: this.senderKey,
195
- senderRelay: this.senderRelay,
196
- receiverKey: this.receiverKey,
197
- receiverRelay: this.receiverRelay,
198
- protocol: this.protocol,
199
- createdAt: this.createdAt,
200
- payload: this.payload ? encodeBase64(this.payload) : undefined
201
- });*/
202
- return this.toString();
200
+ return JSON.stringify(this.toString());
203
201
  }
204
202
  }
205
203
  DatagramConstructor.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
@@ -241,43 +239,40 @@ class EncryptedDataConstructor {
241
239
  return this;
242
240
  }
243
241
  if (typeof arrays[0] === 'number')
244
- arrays[0] = (0, utils_1.numberToUint8Array)(arrays[0], EncryptedDataConstructor.countLength);
242
+ arrays[0] = (0, utils_1.numberToArray)(arrays[0], EncryptedDataConstructor.countLength);
245
243
  if (typeof arrays[1] === 'number')
246
- arrays[1] = (0, utils_1.numberToUint8Array)(arrays[1], EncryptedDataConstructor.countLength);
244
+ arrays[1] = (0, utils_1.numberToArray)(arrays[1], EncryptedDataConstructor.countLength);
247
245
  if (arrays.length === 6) {
248
- 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]);
249
247
  arrays.pop();
250
248
  }
251
249
  else if (arrays.length > 1) {
252
- arrays.unshift((0, utils_1.numberToUint8Array)(double_ratchet_1.KeySession.version));
250
+ arrays.unshift((0, utils_1.numberToArray)(double_ratchet_1.KeySession.version));
253
251
  }
254
- this.raw = (0, utils_1.concatUint8Array)(...arrays);
252
+ this.raw = (0, utils_1.concatArrays)(...arrays);
255
253
  }
256
254
  get length() { return this.raw.length; }
257
- get version() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
258
- get count() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
259
- 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)); }
260
258
  get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
261
259
  get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
262
260
  get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
263
261
  encode() {
264
262
  return this.raw;
265
263
  }
266
- decode() {
267
- return {
264
+ toString() {
265
+ return (0, utils_1.decodeBase64)(this.raw);
266
+ }
267
+ toJSON() {
268
+ return JSON.stringify({
268
269
  version: this.version,
269
270
  count: this.count,
270
271
  previous: this.previous,
271
272
  publicKey: (0, utils_1.decodeBase64)(this.publicKey),
272
273
  nonce: (0, utils_1.decodeBase64)(this.nonce),
273
274
  ciphertext: (0, utils_1.decodeBase64)(this.ciphertext)
274
- };
275
- }
276
- toString() {
277
- return (0, utils_1.decodeBase64)(this.raw);
278
- }
279
- toJSON() {
280
- return JSON.stringify(this.decode());
275
+ });
281
276
  }
282
277
  }
283
278
  exports.EncryptedDataConstructor = EncryptedDataConstructor;
@@ -310,3 +305,156 @@ Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.count
310
305
  Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
311
306
  Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
312
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,