@freesignal/protocol 0.2.11 → 0.3.2

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