@freesignal/protocol 0.7.11 → 0.8.1
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/double-ratchet.d.ts +1 -1
- package/dist/double-ratchet.js +28 -35
- package/dist/index.d.ts +4 -4
- package/dist/index.js +14 -34
- package/dist/node.d.ts +5 -4
- package/dist/node.js +60 -62
- package/dist/types.d.ts +1 -1
- package/dist/types.js +75 -91
- package/dist/x3dh.d.ts +2 -2
- package/dist/x3dh.js +49 -56
- package/package.json +45 -45
package/dist/types.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* FreeSignal Protocol
|
|
4
3
|
*
|
|
@@ -26,38 +25,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
26
25
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
27
26
|
});
|
|
28
27
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.AsyncMap = exports.EncryptedData = exports.EncryptionHeader = exports.EncryptedDatagram = exports.Datagram = exports.Protocols = exports.DiscoverType = exports.PrivateIdentityKey = exports.IdentityKey = exports.UserId = void 0;
|
|
34
|
-
exports.encryptData = encryptData;
|
|
35
|
-
exports.decryptData = decryptData;
|
|
36
|
-
const utils_1 = require("@freesignal/utils");
|
|
37
|
-
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
38
|
-
function encryptData(session, data) {
|
|
28
|
+
import { concatBytes, decodeBase64, encodeBase64, bytesToNumber, numberToBytes, compareBytes } from "@freesignal/utils";
|
|
29
|
+
import crypto from "@freesignal/crypto";
|
|
30
|
+
export function encryptData(session, data) {
|
|
39
31
|
const key = session.getSendingKey();
|
|
40
32
|
if (!key)
|
|
41
33
|
throw new Error("Error generating key");
|
|
42
|
-
const nonce =
|
|
43
|
-
const payload =
|
|
34
|
+
const nonce = crypto.randomBytes(EncryptionHeader.nonceLength);
|
|
35
|
+
const payload = crypto.box.encrypt(data, nonce, key.secretKey);
|
|
44
36
|
let header = new EncryptionHeader(key, nonce).toBytes();
|
|
45
37
|
const headerKey = session.getHeaderKey();
|
|
46
38
|
if (!headerKey)
|
|
47
39
|
return new EncryptedData({ header, payload });
|
|
48
|
-
const headerNonce =
|
|
40
|
+
const headerNonce = crypto.randomBytes(EncryptionHeader.nonceLength);
|
|
49
41
|
if (headerKey)
|
|
50
|
-
header =
|
|
51
|
-
return new EncryptedData({ hashkey:
|
|
42
|
+
header = crypto.box.encrypt(header, headerNonce, headerKey);
|
|
43
|
+
return new EncryptedData({ hashkey: crypto.hash(headerKey !== null && headerKey !== void 0 ? headerKey : new Uint8Array(32).fill(0)), header, nonce: headerNonce, payload });
|
|
52
44
|
}
|
|
53
|
-
function decryptData(session, encryptedData) {
|
|
45
|
+
export function decryptData(session, encryptedData) {
|
|
54
46
|
const encrypted = EncryptedData.from(encryptedData);
|
|
55
47
|
let headerData = encrypted.header;
|
|
56
48
|
if (encrypted.hashkey && encrypted.nonce) {
|
|
57
|
-
const headerKey = session.getHeaderKey(
|
|
49
|
+
const headerKey = session.getHeaderKey(decodeBase64(encrypted.hashkey));
|
|
58
50
|
if (!headerKey)
|
|
59
51
|
throw new Error("Error getting key");
|
|
60
|
-
const data =
|
|
52
|
+
const data = crypto.box.decrypt(encrypted.header, encrypted.nonce, headerKey);
|
|
61
53
|
if (!data)
|
|
62
54
|
throw new Error("Error calculating header");
|
|
63
55
|
headerData = data;
|
|
@@ -66,18 +58,18 @@ function decryptData(session, encryptedData) {
|
|
|
66
58
|
const key = session.getReceivingKey(header);
|
|
67
59
|
if (!key)
|
|
68
60
|
throw new Error("Error calculating key");
|
|
69
|
-
const decrypted =
|
|
61
|
+
const decrypted = crypto.box.decrypt(encrypted.payload, header.nonce, key);
|
|
70
62
|
if (!decrypted)
|
|
71
63
|
throw new Error("Error decrypting data");
|
|
72
64
|
return decrypted;
|
|
73
65
|
}
|
|
74
|
-
class UserId {
|
|
66
|
+
export class UserId {
|
|
75
67
|
constructor(array) {
|
|
76
68
|
this.array = array;
|
|
77
69
|
}
|
|
78
70
|
;
|
|
79
71
|
toString() {
|
|
80
|
-
return
|
|
72
|
+
return decodeBase64(this.array);
|
|
81
73
|
}
|
|
82
74
|
toJSON() {
|
|
83
75
|
return this.toString();
|
|
@@ -87,20 +79,19 @@ class UserId {
|
|
|
87
79
|
}
|
|
88
80
|
static fromKey(identityKey) {
|
|
89
81
|
if (typeof identityKey === 'string')
|
|
90
|
-
identityKey =
|
|
82
|
+
identityKey = encodeBase64(identityKey);
|
|
91
83
|
else if (identityKey instanceof IdentityKey)
|
|
92
84
|
identityKey = (identityKey).toBytes();
|
|
93
|
-
return new UserId(
|
|
85
|
+
return new UserId(crypto.hkdf(identityKey, new Uint8Array(32).fill(0), "/freesignal/userid", UserId.keyLength));
|
|
94
86
|
}
|
|
95
87
|
static from(userId) {
|
|
96
88
|
if (typeof userId === 'string')
|
|
97
|
-
userId =
|
|
89
|
+
userId = encodeBase64(userId);
|
|
98
90
|
return new UserId(userId instanceof Uint8Array ? userId : userId.array);
|
|
99
91
|
}
|
|
100
92
|
}
|
|
101
|
-
exports.UserId = UserId;
|
|
102
93
|
UserId.keyLength = 32;
|
|
103
|
-
class IdentityKey {
|
|
94
|
+
export class IdentityKey {
|
|
104
95
|
constructor(identityKey) {
|
|
105
96
|
if (identityKey instanceof IdentityKey) {
|
|
106
97
|
this.info = identityKey.info;
|
|
@@ -109,22 +100,22 @@ class IdentityKey {
|
|
|
109
100
|
}
|
|
110
101
|
else {
|
|
111
102
|
if (typeof identityKey === 'string')
|
|
112
|
-
identityKey =
|
|
103
|
+
identityKey = encodeBase64(identityKey);
|
|
113
104
|
if (identityKey.length !== IdentityKey.keyLength)
|
|
114
105
|
throw new Error("Invalid key length");
|
|
115
106
|
this.info = identityKey[0];
|
|
116
|
-
this.signatureKey = identityKey.subarray(1,
|
|
117
|
-
this.exchangeKey = identityKey.subarray(
|
|
107
|
+
this.signatureKey = identityKey.subarray(1, crypto.EdDSA.publicKeyLength + 1);
|
|
108
|
+
this.exchangeKey = identityKey.subarray(crypto.EdDSA.publicKeyLength + 1, IdentityKey.keyLength);
|
|
118
109
|
}
|
|
119
110
|
}
|
|
120
111
|
get userId() {
|
|
121
112
|
return UserId.fromKey(this.toBytes());
|
|
122
113
|
}
|
|
123
114
|
toBytes() {
|
|
124
|
-
return
|
|
115
|
+
return concatBytes(numberToBytes(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
125
116
|
}
|
|
126
117
|
toString() {
|
|
127
|
-
return
|
|
118
|
+
return decodeBase64(this.toBytes());
|
|
128
119
|
}
|
|
129
120
|
toJSON() {
|
|
130
121
|
return this.toString();
|
|
@@ -134,18 +125,17 @@ class IdentityKey {
|
|
|
134
125
|
if (key instanceof IdentityKey)
|
|
135
126
|
return key.toBytes();
|
|
136
127
|
else if (typeof key === 'string')
|
|
137
|
-
return
|
|
128
|
+
return encodeBase64(key);
|
|
138
129
|
else
|
|
139
130
|
return key;
|
|
140
131
|
});
|
|
141
|
-
return new IdentityKey(keys.length === 2 ?
|
|
132
|
+
return new IdentityKey(keys.length === 2 ? concatBytes(numberToBytes(IdentityKey.info + IdentityKey.version, 1), ...keys) : keys[0]);
|
|
142
133
|
}
|
|
143
134
|
}
|
|
144
|
-
|
|
145
|
-
IdentityKey.keyLength = crypto_1.default.EdDSA.publicKeyLength + crypto_1.default.ECDH.publicKeyLength + 1;
|
|
135
|
+
IdentityKey.keyLength = crypto.EdDSA.publicKeyLength + crypto.ECDH.publicKeyLength + 1;
|
|
146
136
|
IdentityKey.version = 1;
|
|
147
137
|
IdentityKey.info = 0x70;
|
|
148
|
-
class PrivateIdentityKey {
|
|
138
|
+
export class PrivateIdentityKey {
|
|
149
139
|
constructor(privateIdentityKey) {
|
|
150
140
|
if (privateIdentityKey instanceof PrivateIdentityKey) {
|
|
151
141
|
this.info = privateIdentityKey.info;
|
|
@@ -155,23 +145,23 @@ class PrivateIdentityKey {
|
|
|
155
145
|
}
|
|
156
146
|
else {
|
|
157
147
|
if (typeof privateIdentityKey === 'string')
|
|
158
|
-
privateIdentityKey =
|
|
148
|
+
privateIdentityKey = encodeBase64(privateIdentityKey);
|
|
159
149
|
if (!PrivateIdentityKey.isIdentityKeys(privateIdentityKey))
|
|
160
150
|
throw new Error("Invalid key length");
|
|
161
151
|
this.info = privateIdentityKey[0];
|
|
162
|
-
this.signatureKey = privateIdentityKey.subarray(1,
|
|
163
|
-
this.exchangeKey = privateIdentityKey.subarray(
|
|
164
|
-
this.identityKey = IdentityKey.from(
|
|
152
|
+
this.signatureKey = privateIdentityKey.subarray(1, crypto.EdDSA.secretKeyLength + 1);
|
|
153
|
+
this.exchangeKey = privateIdentityKey.subarray(crypto.EdDSA.secretKeyLength + 1, PrivateIdentityKey.keyLength);
|
|
154
|
+
this.identityKey = IdentityKey.from(crypto.EdDSA.keyPair(this.signatureKey).publicKey, crypto.ECDH.keyPair(this.exchangeKey).publicKey);
|
|
165
155
|
}
|
|
166
156
|
}
|
|
167
157
|
get userId() {
|
|
168
158
|
return UserId.fromKey(this.identityKey.toBytes()).toString();
|
|
169
159
|
}
|
|
170
160
|
toBytes() {
|
|
171
|
-
return
|
|
161
|
+
return concatBytes(numberToBytes(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
172
162
|
}
|
|
173
163
|
toString() {
|
|
174
|
-
return
|
|
164
|
+
return decodeBase64(this.toBytes());
|
|
175
165
|
}
|
|
176
166
|
toJSON() {
|
|
177
167
|
return this.toString();
|
|
@@ -184,23 +174,22 @@ class PrivateIdentityKey {
|
|
|
184
174
|
if (key instanceof PrivateIdentityKey)
|
|
185
175
|
return key.toBytes();
|
|
186
176
|
else if (typeof key === 'string')
|
|
187
|
-
return
|
|
177
|
+
return encodeBase64(key);
|
|
188
178
|
else
|
|
189
179
|
return key;
|
|
190
180
|
});
|
|
191
|
-
return new PrivateIdentityKey(keys.length === 2 ?
|
|
181
|
+
return new PrivateIdentityKey(keys.length === 2 ? concatBytes(numberToBytes(PrivateIdentityKey.info + PrivateIdentityKey.version, 1), ...keys) : keys[0]);
|
|
192
182
|
}
|
|
193
183
|
}
|
|
194
|
-
|
|
195
|
-
PrivateIdentityKey.keyLength = crypto_1.default.EdDSA.secretKeyLength + crypto_1.default.ECDH.secretKeyLength + 1;
|
|
184
|
+
PrivateIdentityKey.keyLength = crypto.EdDSA.secretKeyLength + crypto.ECDH.secretKeyLength + 1;
|
|
196
185
|
PrivateIdentityKey.version = 1;
|
|
197
186
|
PrivateIdentityKey.info = 0x4E;
|
|
198
|
-
var DiscoverType;
|
|
187
|
+
export var DiscoverType;
|
|
199
188
|
(function (DiscoverType) {
|
|
200
189
|
DiscoverType[DiscoverType["REQUEST"] = 0] = "REQUEST";
|
|
201
190
|
DiscoverType[DiscoverType["RESPONSE"] = 1] = "RESPONSE";
|
|
202
|
-
})(DiscoverType || (
|
|
203
|
-
var Protocols;
|
|
191
|
+
})(DiscoverType || (DiscoverType = {}));
|
|
192
|
+
export var Protocols;
|
|
204
193
|
(function (Protocols) {
|
|
205
194
|
Protocols["PING"] = "/freesignal/ping";
|
|
206
195
|
Protocols["MESSAGE"] = "/freesignal/message";
|
|
@@ -208,7 +197,7 @@ var Protocols;
|
|
|
208
197
|
Protocols["HANDSHAKE"] = "/freesignal/handshake";
|
|
209
198
|
Protocols["DISCOVER"] = "/freesignal/discover";
|
|
210
199
|
Protocols["BOOTSTRAP"] = "/freesignal/bootstrap";
|
|
211
|
-
})(Protocols || (
|
|
200
|
+
})(Protocols || (Protocols = {}));
|
|
212
201
|
(function (Protocols) {
|
|
213
202
|
function isProtocol(protocol) {
|
|
214
203
|
return Object.values(Protocols).includes(protocol);
|
|
@@ -223,19 +212,19 @@ var Protocols;
|
|
|
223
212
|
}
|
|
224
213
|
Protocols.toCode = toCode;
|
|
225
214
|
function encode(protocol, length = 1) {
|
|
226
|
-
return
|
|
215
|
+
return numberToBytes(Protocols.toCode(protocol), length);
|
|
227
216
|
}
|
|
228
217
|
Protocols.encode = encode;
|
|
229
218
|
function decode(array) {
|
|
230
|
-
return Protocols.fromCode(
|
|
219
|
+
return Protocols.fromCode(bytesToNumber(array));
|
|
231
220
|
}
|
|
232
221
|
Protocols.decode = decode;
|
|
233
|
-
})(Protocols || (
|
|
222
|
+
})(Protocols || (Protocols = {}));
|
|
234
223
|
;
|
|
235
|
-
class Datagram {
|
|
224
|
+
export class Datagram {
|
|
236
225
|
constructor(protocol, payload, sessionTag) {
|
|
237
226
|
this._version = Datagram.version;
|
|
238
|
-
this.sessionTag = sessionTag instanceof Uint8Array ?
|
|
227
|
+
this.sessionTag = sessionTag instanceof Uint8Array ? decodeBase64(sessionTag) : sessionTag;
|
|
239
228
|
this.protocol = protocol;
|
|
240
229
|
this.payload = payload instanceof Uint8Array ? payload : payload === null || payload === void 0 ? void 0 : payload.toBytes();
|
|
241
230
|
}
|
|
@@ -243,50 +232,50 @@ class Datagram {
|
|
|
243
232
|
return this._version;
|
|
244
233
|
}
|
|
245
234
|
get signature() {
|
|
246
|
-
return this._signature ?
|
|
235
|
+
return this._signature ? decodeBase64(this._signature) : undefined;
|
|
247
236
|
}
|
|
248
237
|
get unsigned() {
|
|
249
238
|
const data = this.toBytes();
|
|
250
239
|
data[0] &= 127;
|
|
251
|
-
return data.subarray(0, data.length - (this._signature ?
|
|
240
|
+
return data.subarray(0, data.length - (this._signature ? crypto.EdDSA.signatureLength : 0));
|
|
252
241
|
}
|
|
253
242
|
toBytes() {
|
|
254
243
|
var _a, _b;
|
|
255
|
-
return
|
|
244
|
+
return concatBytes(new Uint8Array(1).fill(this.version | (this.signature ? 128 : 0)), //1
|
|
256
245
|
Protocols.encode(this.protocol), //1
|
|
257
|
-
this.sessionTag ?
|
|
246
|
+
this.sessionTag ? encodeBase64(this.sessionTag) : new Uint8Array(32).fill(0), //32
|
|
258
247
|
(_a = this.payload) !== null && _a !== void 0 ? _a : new Uint8Array(), (_b = this._signature) !== null && _b !== void 0 ? _b : new Uint8Array());
|
|
259
248
|
}
|
|
260
249
|
sign(secretKey) {
|
|
261
|
-
this._signature =
|
|
250
|
+
this._signature = crypto.EdDSA.sign(this.unsigned, secretKey);
|
|
262
251
|
return this;
|
|
263
252
|
}
|
|
264
253
|
verify(publicKey) {
|
|
265
254
|
if (!this._signature)
|
|
266
255
|
throw new Error("Datagram not signed");
|
|
267
|
-
return
|
|
256
|
+
return crypto.EdDSA.verify(this._signature, this.unsigned, publicKey);
|
|
268
257
|
}
|
|
269
258
|
toString() {
|
|
270
|
-
return
|
|
259
|
+
return decodeBase64(this.toBytes());
|
|
271
260
|
}
|
|
272
261
|
toJSON() {
|
|
273
262
|
return {
|
|
274
263
|
version: this.version,
|
|
275
264
|
sessionTag: this.sessionTag,
|
|
276
265
|
protocol: this.protocol,
|
|
277
|
-
payload: this.payload ?
|
|
278
|
-
signature: this._signature ?
|
|
266
|
+
payload: this.payload ? decodeBase64(this.payload) : undefined,
|
|
267
|
+
signature: this._signature ? decodeBase64(this._signature) : undefined
|
|
279
268
|
};
|
|
280
269
|
}
|
|
281
270
|
static from(data) {
|
|
282
271
|
if (typeof data === 'string')
|
|
283
|
-
data =
|
|
272
|
+
data = encodeBase64(data);
|
|
284
273
|
if (data instanceof Uint8Array) {
|
|
285
274
|
const authTag = data.subarray(2, Datagram.headerLength);
|
|
286
|
-
const datagram = new Datagram(Protocols.decode(data.subarray(1, 2)), data.subarray(Datagram.headerLength, data.length - (data[0] & 128 ?
|
|
275
|
+
const datagram = new Datagram(Protocols.decode(data.subarray(1, 2)), data.subarray(Datagram.headerLength, data.length - (data[0] & 128 ? crypto.EdDSA.signatureLength : 0)), compareBytes(authTag, new Uint8Array(32).fill(0)) ? undefined : decodeBase64(authTag));
|
|
287
276
|
datagram._version = data[0] & 127;
|
|
288
277
|
if (data[0] & 128)
|
|
289
|
-
datagram._signature = data.subarray(data.length -
|
|
278
|
+
datagram._signature = data.subarray(data.length - crypto.EdDSA.signatureLength);
|
|
290
279
|
return datagram;
|
|
291
280
|
}
|
|
292
281
|
else if (data instanceof Datagram) {
|
|
@@ -299,10 +288,9 @@ class Datagram {
|
|
|
299
288
|
throw new Error('Invalid constructor arguments for Datagram');
|
|
300
289
|
}
|
|
301
290
|
}
|
|
302
|
-
exports.Datagram = Datagram;
|
|
303
291
|
Datagram.version = 1;
|
|
304
292
|
Datagram.headerLength = 34;
|
|
305
|
-
class EncryptedDatagram extends Datagram {
|
|
293
|
+
export class EncryptedDatagram extends Datagram {
|
|
306
294
|
constructor(protocol, sessionTag, payload) {
|
|
307
295
|
super(protocol, payload, sessionTag);
|
|
308
296
|
}
|
|
@@ -313,8 +301,7 @@ class EncryptedDatagram extends Datagram {
|
|
|
313
301
|
return datagram;
|
|
314
302
|
}
|
|
315
303
|
}
|
|
316
|
-
|
|
317
|
-
class EncryptionHeader {
|
|
304
|
+
export class EncryptionHeader {
|
|
318
305
|
constructor(keys, nonce) {
|
|
319
306
|
this.nonce = nonce;
|
|
320
307
|
this.count = keys.count;
|
|
@@ -322,13 +309,13 @@ class EncryptionHeader {
|
|
|
322
309
|
this.publicKey = keys.publicKey;
|
|
323
310
|
}
|
|
324
311
|
toBytes() {
|
|
325
|
-
return
|
|
312
|
+
return concatBytes(numberToBytes(this.count, EncryptionHeader.countLength), numberToBytes(this.previous, EncryptionHeader.countLength), this.publicKey, this.nonce);
|
|
326
313
|
}
|
|
327
314
|
toJSON() {
|
|
328
315
|
return {
|
|
329
316
|
count: this.count,
|
|
330
317
|
previous: this.previous,
|
|
331
|
-
publicKey:
|
|
318
|
+
publicKey: decodeBase64(this.publicKey)
|
|
332
319
|
};
|
|
333
320
|
}
|
|
334
321
|
static from(data) {
|
|
@@ -336,17 +323,16 @@ class EncryptionHeader {
|
|
|
336
323
|
data = data.toBytes();
|
|
337
324
|
let offset = 0;
|
|
338
325
|
return new EncryptionHeader({
|
|
339
|
-
count:
|
|
340
|
-
previous:
|
|
326
|
+
count: bytesToNumber(data.subarray(offset, offset += EncryptionHeader.countLength)),
|
|
327
|
+
previous: bytesToNumber(data.subarray(offset, offset += EncryptionHeader.countLength)),
|
|
341
328
|
publicKey: data.subarray(offset, offset += EncryptionHeader.keyLength)
|
|
342
329
|
}, data.subarray(offset, offset += EncryptedData.nonceLength));
|
|
343
330
|
}
|
|
344
331
|
}
|
|
345
|
-
|
|
346
|
-
EncryptionHeader.
|
|
347
|
-
EncryptionHeader.nonceLength = crypto_1.default.box.nonceLength;
|
|
332
|
+
EncryptionHeader.keyLength = crypto.box.keyLength;
|
|
333
|
+
EncryptionHeader.nonceLength = crypto.box.nonceLength;
|
|
348
334
|
EncryptionHeader.countLength = 2;
|
|
349
|
-
class EncryptedData {
|
|
335
|
+
export class EncryptedData {
|
|
350
336
|
constructor({ hashkey, header, nonce, payload }) {
|
|
351
337
|
this._version = EncryptedData.version;
|
|
352
338
|
this.header = header;
|
|
@@ -362,22 +348,22 @@ class EncryptedData {
|
|
|
362
348
|
}
|
|
363
349
|
toBytes() {
|
|
364
350
|
var _a, _b;
|
|
365
|
-
return
|
|
351
|
+
return concatBytes(numberToBytes(this._version | (this.hashkey && this.nonce ? 128 : 0), 1), numberToBytes(this.header.length, 3), this.header, (_a = this.hashkey) !== null && _a !== void 0 ? _a : new Uint8Array(), (_b = this.nonce) !== null && _b !== void 0 ? _b : new Uint8Array, this.payload);
|
|
366
352
|
}
|
|
367
353
|
toJSON() {
|
|
368
354
|
return {
|
|
369
355
|
version: this._version,
|
|
370
|
-
header:
|
|
371
|
-
hashkey: this.hashkey ?
|
|
372
|
-
nonce: this.nonce ?
|
|
373
|
-
payload:
|
|
356
|
+
header: decodeBase64(this.header),
|
|
357
|
+
hashkey: this.hashkey ? decodeBase64(this.hashkey) : undefined,
|
|
358
|
+
nonce: this.nonce ? decodeBase64(this.nonce) : undefined,
|
|
359
|
+
payload: decodeBase64(this.payload)
|
|
374
360
|
};
|
|
375
361
|
}
|
|
376
362
|
static from(data) {
|
|
377
363
|
if (data instanceof EncryptedData)
|
|
378
364
|
data = data.toBytes();
|
|
379
|
-
const versionByte =
|
|
380
|
-
const headerLength =
|
|
365
|
+
const versionByte = bytesToNumber(data.subarray(0, 1));
|
|
366
|
+
const headerLength = bytesToNumber(data.subarray(1, 4));
|
|
381
367
|
let offset = 4;
|
|
382
368
|
const header = data.subarray(offset, offset += headerLength);
|
|
383
369
|
let hashkey, nonce;
|
|
@@ -394,10 +380,9 @@ class EncryptedData {
|
|
|
394
380
|
return obj;
|
|
395
381
|
}
|
|
396
382
|
}
|
|
397
|
-
exports.EncryptedData = EncryptedData;
|
|
398
383
|
EncryptedData.version = 1;
|
|
399
|
-
EncryptedData.nonceLength =
|
|
400
|
-
class AsyncMap {
|
|
384
|
+
EncryptedData.nonceLength = crypto.box.nonceLength;
|
|
385
|
+
export class AsyncMap {
|
|
401
386
|
constructor(iterable) {
|
|
402
387
|
this.map = new Map(iterable);
|
|
403
388
|
}
|
|
@@ -433,4 +418,3 @@ class AsyncMap {
|
|
|
433
418
|
});
|
|
434
419
|
}
|
|
435
420
|
}
|
|
436
|
-
exports.AsyncMap = AsyncMap;
|
package/dist/x3dh.d.ts
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
19
|
import { KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage, Crypto } from "@freesignal/interfaces";
|
|
20
|
-
import { KeySession } from "./double-ratchet";
|
|
21
|
-
import { IdentityKey, PrivateIdentityKey } from "./types";
|
|
20
|
+
import { KeySession } from "./double-ratchet.js";
|
|
21
|
+
import { IdentityKey, PrivateIdentityKey } from "./types.js";
|
|
22
22
|
export interface ExportedKeyExchange {
|
|
23
23
|
privateIdentityKey: PrivateIdentityKey;
|
|
24
24
|
storage: Array<[string, Crypto.KeyPair]>;
|
package/dist/x3dh.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* FreeSignal Protocol
|
|
4
3
|
*
|
|
@@ -26,34 +25,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
26
25
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
27
26
|
});
|
|
28
27
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const double_ratchet_1 = require("./double-ratchet");
|
|
36
|
-
const utils_1 = require("@freesignal/utils");
|
|
37
|
-
const types_1 = require("./types");
|
|
38
|
-
const _1 = require(".");
|
|
39
|
-
class KeyExchange {
|
|
28
|
+
import crypto from "@freesignal/crypto";
|
|
29
|
+
import { KeySession } from "./double-ratchet.js";
|
|
30
|
+
import { concatBytes, decodeBase64, encodeBase64, compareBytes } from "@freesignal/utils";
|
|
31
|
+
import { decryptData, encryptData, IdentityKey } from "./types.js";
|
|
32
|
+
import { createIdentity } from "./index.js";
|
|
33
|
+
export class KeyExchange {
|
|
40
34
|
constructor(storage, privateIdentityKey) {
|
|
41
35
|
this.storage = storage;
|
|
42
|
-
this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey :
|
|
36
|
+
this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : createIdentity();
|
|
43
37
|
}
|
|
44
38
|
get identityKey() {
|
|
45
39
|
return this.privateIdentityKey.identityKey;
|
|
46
40
|
}
|
|
47
41
|
generateSPK() {
|
|
48
|
-
const signedPreKey =
|
|
49
|
-
const signedPreKeyHash =
|
|
50
|
-
this.storage.set(
|
|
42
|
+
const signedPreKey = crypto.ECDH.keyPair();
|
|
43
|
+
const signedPreKeyHash = crypto.hash(signedPreKey.publicKey);
|
|
44
|
+
this.storage.set(decodeBase64(signedPreKeyHash), signedPreKey);
|
|
51
45
|
return { signedPreKey, signedPreKeyHash };
|
|
52
46
|
}
|
|
53
47
|
generateOPK(spkHash) {
|
|
54
|
-
const onetimePreKey =
|
|
55
|
-
const onetimePreKeyHash =
|
|
56
|
-
this.storage.set(
|
|
48
|
+
const onetimePreKey = crypto.ECDH.keyPair();
|
|
49
|
+
const onetimePreKeyHash = crypto.hash(onetimePreKey.publicKey);
|
|
50
|
+
this.storage.set(decodeBase64(spkHash).concat(decodeBase64(onetimePreKeyHash)), onetimePreKey);
|
|
57
51
|
return { onetimePreKey, onetimePreKeyHash };
|
|
58
52
|
}
|
|
59
53
|
generateBundle(length) {
|
|
@@ -63,9 +57,9 @@ class KeyExchange {
|
|
|
63
57
|
return {
|
|
64
58
|
version: KeyExchange.version,
|
|
65
59
|
identityKey: this.identityKey.toString(),
|
|
66
|
-
signedPreKey:
|
|
67
|
-
signature:
|
|
68
|
-
onetimePreKeys: onetimePreKey.map(opk =>
|
|
60
|
+
signedPreKey: decodeBase64(signedPreKey.publicKey),
|
|
61
|
+
signature: decodeBase64(crypto.EdDSA.sign(signedPreKeyHash, this.privateIdentityKey.signatureKey)),
|
|
62
|
+
onetimePreKeys: onetimePreKey.map(opk => decodeBase64(opk.publicKey))
|
|
69
63
|
};
|
|
70
64
|
});
|
|
71
65
|
}
|
|
@@ -76,31 +70,31 @@ class KeyExchange {
|
|
|
76
70
|
return {
|
|
77
71
|
version: KeyExchange.version,
|
|
78
72
|
identityKey: this.identityKey.toString(),
|
|
79
|
-
signedPreKey:
|
|
80
|
-
signature:
|
|
81
|
-
onetimePreKey:
|
|
73
|
+
signedPreKey: decodeBase64(signedPreKey.publicKey),
|
|
74
|
+
signature: decodeBase64(crypto.EdDSA.sign(signedPreKeyHash, this.privateIdentityKey.signatureKey)),
|
|
75
|
+
onetimePreKey: decodeBase64(onetimePreKey.publicKey)
|
|
82
76
|
};
|
|
83
77
|
});
|
|
84
78
|
}
|
|
85
79
|
digestData(message, associatedData) {
|
|
86
80
|
return __awaiter(this, void 0, void 0, function* () {
|
|
87
81
|
//console.debug("Digest Data")
|
|
88
|
-
const ephemeralKey =
|
|
89
|
-
const signedPreKey =
|
|
90
|
-
const identityKey =
|
|
91
|
-
if (!
|
|
82
|
+
const ephemeralKey = crypto.ECDH.keyPair();
|
|
83
|
+
const signedPreKey = encodeBase64(message.signedPreKey);
|
|
84
|
+
const identityKey = IdentityKey.from(message.identityKey);
|
|
85
|
+
if (!crypto.EdDSA.verify(encodeBase64(message.signature), crypto.hash(signedPreKey), identityKey.signatureKey))
|
|
92
86
|
throw new Error("Signature verification failed");
|
|
93
|
-
const onetimePreKey = message.onetimePreKey ?
|
|
94
|
-
const signedPreKeyHash =
|
|
95
|
-
const onetimePreKeyHash = onetimePreKey ?
|
|
96
|
-
const derivedKey =
|
|
97
|
-
...
|
|
98
|
-
...
|
|
99
|
-
...
|
|
100
|
-
...onetimePreKey ?
|
|
101
|
-
]), new Uint8Array(
|
|
102
|
-
const session = new
|
|
103
|
-
const encrypted =
|
|
87
|
+
const onetimePreKey = message.onetimePreKey ? encodeBase64(message.onetimePreKey) : undefined;
|
|
88
|
+
const signedPreKeyHash = crypto.hash(signedPreKey);
|
|
89
|
+
const onetimePreKeyHash = onetimePreKey ? crypto.hash(onetimePreKey) : new Uint8Array();
|
|
90
|
+
const derivedKey = crypto.hkdf(new Uint8Array([
|
|
91
|
+
...crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, signedPreKey),
|
|
92
|
+
...crypto.ECDH.scalarMult(ephemeralKey.secretKey, identityKey.exchangeKey),
|
|
93
|
+
...crypto.ECDH.scalarMult(ephemeralKey.secretKey, signedPreKey),
|
|
94
|
+
...onetimePreKey ? crypto.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
|
|
95
|
+
]), new Uint8Array(KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, KeySession.keyLength * 3);
|
|
96
|
+
const session = new KeySession({ identityKey, remoteKey: identityKey.exchangeKey, rootKey: derivedKey.subarray(0, KeySession.keyLength), headerKey: derivedKey.subarray(KeySession.keyLength, KeySession.keyLength * 2), nextHeaderKey: derivedKey.subarray(KeySession.keyLength * 2) });
|
|
97
|
+
const encrypted = encryptData(session, concatBytes(crypto.hash(this.identityKey.toBytes()), crypto.hash(identityKey.toBytes()), associatedData !== null && associatedData !== void 0 ? associatedData : new Uint8Array()));
|
|
104
98
|
if (!encrypted)
|
|
105
99
|
throw new Error("Decryption error");
|
|
106
100
|
return {
|
|
@@ -108,10 +102,10 @@ class KeyExchange {
|
|
|
108
102
|
message: {
|
|
109
103
|
version: KeyExchange.version,
|
|
110
104
|
identityKey: this.identityKey.toString(),
|
|
111
|
-
ephemeralKey:
|
|
112
|
-
signedPreKeyHash:
|
|
113
|
-
onetimePreKeyHash:
|
|
114
|
-
associatedData:
|
|
105
|
+
ephemeralKey: decodeBase64(ephemeralKey.publicKey),
|
|
106
|
+
signedPreKeyHash: decodeBase64(signedPreKeyHash),
|
|
107
|
+
onetimePreKeyHash: decodeBase64(onetimePreKeyHash),
|
|
108
|
+
associatedData: decodeBase64(encrypted.toBytes())
|
|
115
109
|
}
|
|
116
110
|
};
|
|
117
111
|
});
|
|
@@ -122,23 +116,23 @@ class KeyExchange {
|
|
|
122
116
|
const signedPreKey = yield this.storage.get(message.signedPreKeyHash);
|
|
123
117
|
const hash = message.signedPreKeyHash.concat(message.onetimePreKeyHash);
|
|
124
118
|
const onetimePreKey = yield this.storage.get(hash);
|
|
125
|
-
const identityKey =
|
|
119
|
+
const identityKey = IdentityKey.from(message.identityKey);
|
|
126
120
|
if (!signedPreKey || !onetimePreKey || !message.identityKey || !message.ephemeralKey)
|
|
127
121
|
throw new Error("ACK message malformed");
|
|
128
122
|
if (!this.storage.delete(hash))
|
|
129
123
|
throw new Error("Bundle store deleting error");
|
|
130
|
-
const ephemeralKey =
|
|
131
|
-
const derivedKey =
|
|
132
|
-
...
|
|
133
|
-
...
|
|
134
|
-
...
|
|
135
|
-
...onetimePreKey ?
|
|
136
|
-
]), new Uint8Array(
|
|
137
|
-
const session = new
|
|
138
|
-
const data =
|
|
124
|
+
const ephemeralKey = encodeBase64(message.ephemeralKey);
|
|
125
|
+
const derivedKey = crypto.hkdf(new Uint8Array([
|
|
126
|
+
...crypto.ECDH.scalarMult(signedPreKey.secretKey, identityKey.exchangeKey),
|
|
127
|
+
...crypto.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, ephemeralKey),
|
|
128
|
+
...crypto.ECDH.scalarMult(signedPreKey.secretKey, ephemeralKey),
|
|
129
|
+
...onetimePreKey ? crypto.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
|
|
130
|
+
]), new Uint8Array(KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, KeySession.keyLength * 3);
|
|
131
|
+
const session = new KeySession({ identityKey, secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, KeySession.keyLength), nextHeaderKey: derivedKey.subarray(KeySession.keyLength, KeySession.keyLength * 2), headerKey: derivedKey.subarray(KeySession.keyLength * 2) });
|
|
132
|
+
const data = decryptData(session, encodeBase64(message.associatedData));
|
|
139
133
|
if (!data)
|
|
140
134
|
throw new Error("Error decrypting ACK message");
|
|
141
|
-
if (!
|
|
135
|
+
if (!compareBytes(data.subarray(0, 64), concatBytes(crypto.hash(identityKey.toBytes()), crypto.hash(this.identityKey.toBytes()))))
|
|
142
136
|
throw new Error("Error verifing Associated Data");
|
|
143
137
|
return {
|
|
144
138
|
session,
|
|
@@ -147,7 +141,6 @@ class KeyExchange {
|
|
|
147
141
|
});
|
|
148
142
|
}
|
|
149
143
|
}
|
|
150
|
-
exports.KeyExchange = KeyExchange;
|
|
151
144
|
KeyExchange.version = 1;
|
|
152
145
|
KeyExchange.hkdfInfo = "freesignal/x3dh/v." + KeyExchange.version;
|
|
153
146
|
KeyExchange.maxOPK = 10;
|