@freesignal/protocol 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/double-ratchet.d.ts +0 -2
- package/dist/double-ratchet.js +6 -5
- package/dist/node.d.ts +2 -2
- package/dist/node.js +7 -12
- package/dist/test.js +11 -5
- package/dist/types.d.ts +26 -52
- package/dist/types.js +81 -84
- package/dist/x3dh.js +8 -6
- package/package.json +1 -1
package/dist/double-ratchet.d.ts
CHANGED
package/dist/double-ratchet.js
CHANGED
|
@@ -29,6 +29,7 @@ const utils_1 = require("@freesignal/utils");
|
|
|
29
29
|
* Used for forward-secure encryption and decryption of messages.
|
|
30
30
|
*/
|
|
31
31
|
class KeySession {
|
|
32
|
+
//headerKey?: Uint8Array, nextHeaderKey?: Uint8Array,
|
|
32
33
|
constructor(opts = {}) {
|
|
33
34
|
var _a;
|
|
34
35
|
this.previousKeys = new KeyMap();
|
|
@@ -36,10 +37,10 @@ class KeySession {
|
|
|
36
37
|
this.keyPair = crypto_1.default.ECDH.keyPair(opts.secretKey);
|
|
37
38
|
if (opts.rootKey)
|
|
38
39
|
this.rootKey = opts.rootKey;
|
|
39
|
-
if (opts.nextHeaderKey)
|
|
40
|
-
|
|
40
|
+
//if (opts.nextHeaderKey)
|
|
41
|
+
// this.nextHeaderKey = opts.nextHeaderKey;
|
|
41
42
|
if (opts.remoteKey) {
|
|
42
|
-
this.sendingChain = this.getChain(opts.remoteKey
|
|
43
|
+
this.sendingChain = this.getChain(opts.remoteKey); //, opts.headerKey);
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
getChain(remoteKey, headerKey, previousCount) {
|
|
@@ -51,10 +52,10 @@ class KeySession {
|
|
|
51
52
|
return new KeyChain(this.publicKey, remoteKey, hashkey.subarray(KeySession.keyLength, KeySession.keyLength * 2), hashkey.subarray(KeySession.keyLength * 2), headerKey, previousCount);
|
|
52
53
|
}
|
|
53
54
|
getHeaderKeys() {
|
|
54
|
-
var _a, _b, _c;
|
|
55
|
+
var _a, _b, _c, _d, _e;
|
|
55
56
|
return {
|
|
56
57
|
sending: (_a = this.sendingChain) === null || _a === void 0 ? void 0 : _a.headerKey,
|
|
57
|
-
receiving: (_b = this.
|
|
58
|
+
receiving: (_e = ((_c = (_b = this.receivingChain) === null || _b === void 0 ? void 0 : _b.headerKey) !== null && _c !== void 0 ? _c : (_d = this.receivingChain) === null || _d === void 0 ? void 0 : _d.nextHeaderKey)) !== null && _e !== void 0 ? _e : this.nextHeaderKey
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
getSendingKey() {
|
package/dist/node.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
19
|
import { Database, LocalStorage, Crypto, KeyExchangeDataBundle, KeyExchangeData } from "@freesignal/interfaces";
|
|
20
|
-
import { Datagram, DatagramHeader,
|
|
20
|
+
import { Datagram, DatagramHeader, IdentityKey, PrivateIdentityKey, Protocols, UserId } from "./types";
|
|
21
21
|
import { KeyExchange } from "./x3dh";
|
|
22
22
|
import { ExportedKeySession, KeySession } from "./double-ratchet";
|
|
23
23
|
export declare class BootstrapRequest {
|
|
@@ -65,7 +65,7 @@ export declare class FreeSignalNode {
|
|
|
65
65
|
open(datagram: Datagram | Uint8Array): Promise<{
|
|
66
66
|
header: DatagramHeader;
|
|
67
67
|
payload?: Uint8Array;
|
|
68
|
-
|
|
68
|
+
datagram?: Datagram;
|
|
69
69
|
}>;
|
|
70
70
|
}
|
|
71
71
|
declare class SessionMap implements LocalStorage<string, KeySession> {
|
package/dist/node.js
CHANGED
|
@@ -168,6 +168,7 @@ class FreeSignalNode {
|
|
|
168
168
|
*/
|
|
169
169
|
open(datagram) {
|
|
170
170
|
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
var _a;
|
|
171
172
|
if (datagram instanceof Uint8Array)
|
|
172
173
|
datagram = types_1.Datagram.from(datagram);
|
|
173
174
|
let out = {
|
|
@@ -218,15 +219,12 @@ class FreeSignalNode {
|
|
|
218
219
|
};
|
|
219
220
|
}
|
|
220
221
|
const response = { type: types_1.DiscoverType.RESPONSE, discoverId: message.discoverId, data };
|
|
221
|
-
out.
|
|
222
|
-
out.discoverType = types_1.DiscoverType.REQUEST;
|
|
222
|
+
out.datagram = yield this.encrypt(datagram.sender, types_1.Protocols.DISCOVER, (0, utils_1.encodeData)(response));
|
|
223
223
|
}
|
|
224
224
|
else if (message.type === types_1.DiscoverType.RESPONSE && this.discovers.has(message.discoverId)) {
|
|
225
225
|
this.discovers.delete(message.discoverId);
|
|
226
|
-
if (message.data)
|
|
227
|
-
out.
|
|
228
|
-
out.discoverType = types_1.DiscoverType.RESPONSE;
|
|
229
|
-
}
|
|
226
|
+
if (message.data)
|
|
227
|
+
out.datagram = yield this.packHandshake(message.data);
|
|
230
228
|
}
|
|
231
229
|
return out;
|
|
232
230
|
case types_1.Protocols.BOOTSTRAP:
|
|
@@ -239,12 +237,9 @@ class FreeSignalNode {
|
|
|
239
237
|
this.onRequest(request);
|
|
240
238
|
}
|
|
241
239
|
;
|
|
242
|
-
const
|
|
243
|
-
if (
|
|
244
|
-
|
|
245
|
-
if (data)
|
|
246
|
-
out.payload = (0, utils_1.encodeData)(data);
|
|
247
|
-
}
|
|
240
|
+
const bootstrap = yield ((_a = (yield this.bootstraps.get(datagram.sender))) === null || _a === void 0 ? void 0 : _a.get());
|
|
241
|
+
if (bootstrap)
|
|
242
|
+
out.datagram = yield this.packHandshake(bootstrap);
|
|
248
243
|
return out;
|
|
249
244
|
default:
|
|
250
245
|
throw new Error("Invalid protocol");
|
package/dist/test.js
CHANGED
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const utils_1 = require("@freesignal/utils");
|
|
12
13
|
const _1 = require(".");
|
|
13
14
|
console.log("FreeSignal protocol test");
|
|
14
15
|
const bob = (0, _1.createNode)({ keyExchange: new _1.AsyncMap(), sessions: new _1.AsyncMap(), users: new _1.AsyncMap(), bundles: new _1.AsyncMap(), bootstraps: new _1.AsyncMap() });
|
|
@@ -16,22 +17,27 @@ const alice = (0, _1.createNode)({ keyExchange: new _1.AsyncMap(), sessions: new
|
|
|
16
17
|
setImmediate(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
18
|
const bobBootstrap = yield bob.packBootstrap(alice.userId);
|
|
18
19
|
alice.onRequest = (request) => { request.accept(); };
|
|
19
|
-
yield alice.open(bobBootstrap);
|
|
20
|
+
const test = (yield alice.open(bobBootstrap)).datagram;
|
|
21
|
+
console.log("Valid bootstrap: ", !!test);
|
|
20
22
|
const bobRequest = yield alice.getRequest(bob.userId.toString());
|
|
21
23
|
if (!bobRequest)
|
|
22
24
|
throw new Error("Bootstrap Failed");
|
|
23
25
|
const aliceHandshake = yield alice.packHandshake(bobRequest);
|
|
24
26
|
yield bob.open(aliceHandshake);
|
|
25
27
|
const first = (yield bob.packData(alice.userId, "Hi Alice!")).toBytes();
|
|
26
|
-
console.log("Bob: ", (yield alice.open(first)).payload);
|
|
28
|
+
console.log("Bob: ", (0, utils_1.decodeData)((yield alice.open(first)).payload));
|
|
27
29
|
const second = yield alice.packData(bob.userId, "Hi Bob!");
|
|
28
|
-
console.log("Alice: ", (yield bob.open(second)).payload);
|
|
30
|
+
console.log("Alice: ", (0, utils_1.decodeData)((yield bob.open(second)).payload));
|
|
29
31
|
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)));
|
|
30
32
|
third.forEach((data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
-
console.log("Bob: ", (yield alice.open(data)).payload);
|
|
33
|
+
console.log("Bob: ", (0, utils_1.decodeData)((yield alice.open(data)).payload));
|
|
32
34
|
}));
|
|
33
35
|
const fourth = yield alice.packData(bob.userId, "Not so bad my man");
|
|
34
|
-
console.log("Alice: ", (yield bob.open(fourth)).payload);
|
|
36
|
+
console.log("Alice: ", (0, utils_1.decodeData)((yield bob.open(fourth)).payload));
|
|
37
|
+
const fifth = yield Promise.all(["I'm thinking...", "His this secure?"].map(msg => bob.packData(alice.userId, msg)));
|
|
38
|
+
fifth.forEach((data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
console.log("Bob: ", (0, utils_1.decodeData)((yield alice.open(data)).payload));
|
|
40
|
+
}));
|
|
35
41
|
//const testone = await Promise.all(Array(400).fill(0).map(() => alice.packData(bob.userId, decodeBase64(crypto.randomBytes(64)))));
|
|
36
42
|
//console.log(((await bob.open(testone[350])).payload));
|
|
37
43
|
}));
|
package/dist/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
19
|
import { LocalStorage, Encodable, KeyExchangeData } from "@freesignal/interfaces";
|
|
20
|
-
import { KeySession } from "./double-ratchet";
|
|
20
|
+
import { EncryptionKeys, KeySession } from "./double-ratchet";
|
|
21
21
|
export declare function encryptData(session: KeySession, data: Uint8Array): EncryptedData;
|
|
22
22
|
export declare function decryptData(session: KeySession, encryptedData: Uint8Array): Uint8Array;
|
|
23
23
|
export declare class UserId implements Encodable {
|
|
@@ -130,67 +130,41 @@ export declare class Datagram implements Encodable, DatagramHeader {
|
|
|
130
130
|
static verify(datagram: Datagram, publicKey: Uint8Array): boolean;
|
|
131
131
|
static from(data: Uint8Array | Datagram | string): Datagram;
|
|
132
132
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* The length of the payload.
|
|
140
|
-
*/
|
|
141
|
-
readonly length: number;
|
|
142
|
-
/**
|
|
143
|
-
* Version of the payload.
|
|
144
|
-
*/
|
|
145
|
-
readonly version: number;
|
|
146
|
-
/**
|
|
147
|
-
* The current message count of the sending chain.
|
|
148
|
-
*/
|
|
133
|
+
export declare class EncryptionHeader implements EncryptionKeys, Encodable {
|
|
134
|
+
readonly nonce: Uint8Array;
|
|
135
|
+
static readonly keyLength: number;
|
|
136
|
+
static readonly nonceLength: number;
|
|
137
|
+
static readonly countLength = 2;
|
|
149
138
|
readonly count: number;
|
|
150
|
-
/**
|
|
151
|
-
* The count of the previous sending chain.
|
|
152
|
-
*/
|
|
153
139
|
readonly previous: number;
|
|
154
|
-
/**
|
|
155
|
-
* The sender's public key used for this message.
|
|
156
|
-
*/
|
|
157
140
|
readonly publicKey: Uint8Array;
|
|
158
|
-
|
|
159
|
-
* The nonce used during encryption.
|
|
160
|
-
*/
|
|
161
|
-
readonly nonce: Uint8Array;
|
|
162
|
-
/**
|
|
163
|
-
* The encrypted message content.
|
|
164
|
-
*/
|
|
165
|
-
readonly ciphertext: Uint8Array;
|
|
166
|
-
/**
|
|
167
|
-
* Serializes the payload into a Uint8Array for transport.
|
|
168
|
-
*/
|
|
141
|
+
constructor(keys: EncryptionKeys, nonce: Uint8Array);
|
|
169
142
|
toBytes(): Uint8Array;
|
|
170
|
-
/**
|
|
171
|
-
* Returns the payload as a Base64 string.
|
|
172
|
-
*/
|
|
173
|
-
toString(): string;
|
|
174
|
-
/**
|
|
175
|
-
* Returns the decoded object as a JSON string.
|
|
176
|
-
*/
|
|
177
143
|
toJSON(): {
|
|
178
|
-
version: number;
|
|
179
144
|
count: number;
|
|
180
145
|
previous: number;
|
|
181
146
|
publicKey: string;
|
|
182
|
-
nonce: string;
|
|
183
|
-
ciphertext: string;
|
|
184
147
|
};
|
|
148
|
+
static from(data: Uint8Array | EncryptionHeader): EncryptionHeader;
|
|
185
149
|
}
|
|
186
|
-
export declare
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
150
|
+
export declare class EncryptedData implements Encodable {
|
|
151
|
+
readonly header: Uint8Array;
|
|
152
|
+
readonly nonce: Uint8Array;
|
|
153
|
+
readonly payload: Uint8Array;
|
|
154
|
+
static readonly version = 1;
|
|
155
|
+
static readonly nonceLength: number;
|
|
156
|
+
private _version;
|
|
157
|
+
constructor(header: Uint8Array, nonce: Uint8Array, payload: Uint8Array);
|
|
158
|
+
get version(): number;
|
|
159
|
+
get length(): number;
|
|
160
|
+
toBytes(): Uint8Array;
|
|
161
|
+
toJSON(): {
|
|
162
|
+
version: number;
|
|
163
|
+
header: string;
|
|
164
|
+
nonce: string;
|
|
165
|
+
payload: string;
|
|
166
|
+
};
|
|
167
|
+
static from(data: Uint8Array | EncryptedData): EncryptedData;
|
|
194
168
|
}
|
|
195
169
|
export declare class AsyncMap<K, V> implements LocalStorage<K, V> {
|
|
196
170
|
private readonly map;
|
package/dist/types.js
CHANGED
|
@@ -30,26 +30,41 @@ 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.DatagramHeader = exports.Protocols = exports.DiscoverType = exports.PrivateIdentityKey = exports.IdentityKey = exports.UserId = void 0;
|
|
33
|
+
exports.AsyncMap = exports.EncryptedData = exports.EncryptionHeader = exports.Datagram = exports.DatagramHeader = exports.Protocols = exports.DiscoverType = exports.PrivateIdentityKey = exports.IdentityKey = exports.UserId = void 0;
|
|
34
34
|
exports.encryptData = encryptData;
|
|
35
35
|
exports.decryptData = decryptData;
|
|
36
36
|
const utils_1 = require("@freesignal/utils");
|
|
37
37
|
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
38
|
-
const double_ratchet_1 = require("./double-ratchet");
|
|
39
38
|
function encryptData(session, data) {
|
|
39
|
+
//console.log(session.id, ' Sending: ', decodeBase64(session.getHeaderKeys().sending ?? new Uint8Array()));
|
|
40
40
|
const key = session.getSendingKey();
|
|
41
41
|
if (!key)
|
|
42
42
|
throw new Error("Error generating key");
|
|
43
|
-
const nonce = crypto_1.default.randomBytes(
|
|
43
|
+
const nonce = crypto_1.default.randomBytes(EncryptionHeader.nonceLength);
|
|
44
44
|
const ciphertext = crypto_1.default.box.encrypt(data, nonce, key.secretKey);
|
|
45
|
-
|
|
45
|
+
const headerKey = session.getHeaderKeys().sending;
|
|
46
|
+
let header = new EncryptionHeader(key, nonce).toBytes();
|
|
47
|
+
const headerNonce = crypto_1.default.randomBytes(EncryptionHeader.nonceLength);
|
|
48
|
+
if (headerKey)
|
|
49
|
+
header = crypto_1.default.box.encrypt(header, headerNonce, headerKey);
|
|
50
|
+
const test = new EncryptedData(header, headerNonce, ciphertext);
|
|
51
|
+
return test;
|
|
46
52
|
}
|
|
47
53
|
function decryptData(session, encryptedData) {
|
|
54
|
+
//console.log(session.id, ' Receiving: ', decodeBase64(session.getHeaderKeys().receiving ?? new Uint8Array()));
|
|
48
55
|
const encrypted = EncryptedData.from(encryptedData);
|
|
49
|
-
const
|
|
56
|
+
const headerKey = session.getHeaderKeys().receiving;
|
|
57
|
+
let headerData = encrypted.header;
|
|
58
|
+
if (headerKey) {
|
|
59
|
+
headerData = crypto_1.default.box.decrypt(headerData, encrypted.nonce, headerKey);
|
|
60
|
+
if (!headerData)
|
|
61
|
+
throw new Error("Error calculating header");
|
|
62
|
+
}
|
|
63
|
+
const header = EncryptionHeader.from(headerData);
|
|
64
|
+
const key = session.getReceivingKey(header);
|
|
50
65
|
if (!key)
|
|
51
66
|
throw new Error("Error calculating key");
|
|
52
|
-
const decrypted = crypto_1.default.box.decrypt(encrypted.
|
|
67
|
+
const decrypted = crypto_1.default.box.decrypt(encrypted.payload, header.nonce, key);
|
|
53
68
|
if (!decrypted)
|
|
54
69
|
throw new Error("Error decrypting data");
|
|
55
70
|
return decrypted;
|
|
@@ -330,10 +345,10 @@ class Datagram {
|
|
|
330
345
|
}
|
|
331
346
|
else if (data instanceof Datagram) {
|
|
332
347
|
const datagram = new Datagram(data.sender, data.receiver, data.protocol, data.payload);
|
|
333
|
-
datagram._id =
|
|
334
|
-
datagram._version =
|
|
335
|
-
datagram._createdAt =
|
|
336
|
-
datagram._signature =
|
|
348
|
+
datagram._id = data.id;
|
|
349
|
+
datagram._version = data.version;
|
|
350
|
+
datagram._createdAt = data._createdAt;
|
|
351
|
+
datagram._signature = data._signature;
|
|
337
352
|
return datagram;
|
|
338
353
|
}
|
|
339
354
|
else
|
|
@@ -343,91 +358,73 @@ class Datagram {
|
|
|
343
358
|
exports.Datagram = Datagram;
|
|
344
359
|
Datagram.version = 1;
|
|
345
360
|
Datagram.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
|
|
346
|
-
|
|
347
|
-
(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
* @returns An instance of `EncryptedPayload`.
|
|
353
|
-
*/
|
|
354
|
-
function from(array) {
|
|
355
|
-
return new EncryptedDataConstructor(array);
|
|
356
|
-
}
|
|
357
|
-
EncryptedData.from = from;
|
|
358
|
-
})(EncryptedData || (exports.EncryptedData = EncryptedData = {}));
|
|
359
|
-
class EncryptedDataConstructor {
|
|
360
|
-
constructor(...arrays) {
|
|
361
|
-
arrays = arrays.filter(value => value !== undefined);
|
|
362
|
-
if (arrays[0] instanceof EncryptedDataConstructor) {
|
|
363
|
-
this.raw = arrays[0].raw;
|
|
364
|
-
return this;
|
|
365
|
-
}
|
|
366
|
-
if (typeof arrays[0] === 'number')
|
|
367
|
-
arrays[0] = (0, utils_1.numberToBytes)(arrays[0], EncryptedDataConstructor.countLength);
|
|
368
|
-
if (typeof arrays[1] === 'number')
|
|
369
|
-
arrays[1] = (0, utils_1.numberToBytes)(arrays[1], EncryptedDataConstructor.countLength);
|
|
370
|
-
if (arrays.length === 6) {
|
|
371
|
-
arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToBytes)(arrays[5], 1) : arrays[5]);
|
|
372
|
-
arrays.pop();
|
|
373
|
-
}
|
|
374
|
-
else if (arrays.length > 1) {
|
|
375
|
-
arrays.unshift((0, utils_1.numberToBytes)(double_ratchet_1.KeySession.version, 1));
|
|
376
|
-
}
|
|
377
|
-
this.raw = (0, utils_1.concatBytes)(...arrays);
|
|
378
|
-
}
|
|
379
|
-
get length() { return this.raw.length; }
|
|
380
|
-
get version() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
|
|
381
|
-
get count() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
|
|
382
|
-
get previous() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
|
|
383
|
-
get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
|
|
384
|
-
get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
|
|
385
|
-
get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
|
|
386
|
-
toBytes() {
|
|
387
|
-
return this.raw;
|
|
361
|
+
class EncryptionHeader {
|
|
362
|
+
constructor(keys, nonce) {
|
|
363
|
+
this.nonce = nonce;
|
|
364
|
+
this.count = keys.count;
|
|
365
|
+
this.previous = keys.previous;
|
|
366
|
+
this.publicKey = keys.publicKey;
|
|
388
367
|
}
|
|
389
|
-
|
|
390
|
-
return (0, utils_1.
|
|
368
|
+
toBytes() {
|
|
369
|
+
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.count, EncryptionHeader.countLength), (0, utils_1.numberToBytes)(this.previous, EncryptionHeader.countLength), this.publicKey, this.nonce);
|
|
391
370
|
}
|
|
392
371
|
toJSON() {
|
|
393
372
|
return {
|
|
394
|
-
version: this.version,
|
|
395
373
|
count: this.count,
|
|
396
374
|
previous: this.previous,
|
|
397
|
-
publicKey: (0, utils_1.decodeBase64)(this.publicKey)
|
|
398
|
-
nonce: (0, utils_1.decodeBase64)(this.nonce),
|
|
399
|
-
ciphertext: (0, utils_1.decodeBase64)(this.ciphertext)
|
|
375
|
+
publicKey: (0, utils_1.decodeBase64)(this.publicKey)
|
|
400
376
|
};
|
|
401
377
|
}
|
|
378
|
+
static from(data) {
|
|
379
|
+
if (data instanceof EncryptionHeader)
|
|
380
|
+
data = data.toBytes();
|
|
381
|
+
return new EncryptionHeader({
|
|
382
|
+
count: (0, utils_1.bytesToNumber)(data.subarray(0, EncryptionHeader.countLength)),
|
|
383
|
+
previous: (0, utils_1.bytesToNumber)(data.subarray(EncryptionHeader.countLength, EncryptionHeader.countLength * 2)),
|
|
384
|
+
publicKey: data.subarray(EncryptionHeader.countLength * 2, EncryptionHeader.countLength * 2 + EncryptionHeader.keyLength)
|
|
385
|
+
}, data.subarray(EncryptionHeader.countLength * 2 + EncryptionHeader.keyLength, EncryptionHeader.countLength * 2 + EncryptionHeader.keyLength + EncryptedData.nonceLength));
|
|
386
|
+
}
|
|
402
387
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return
|
|
388
|
+
exports.EncryptionHeader = EncryptionHeader;
|
|
389
|
+
EncryptionHeader.keyLength = crypto_1.default.box.keyLength;
|
|
390
|
+
EncryptionHeader.nonceLength = crypto_1.default.box.nonceLength;
|
|
391
|
+
EncryptionHeader.countLength = 2;
|
|
392
|
+
class EncryptedData {
|
|
393
|
+
constructor(header, nonce, payload) {
|
|
394
|
+
this.header = header;
|
|
395
|
+
this.nonce = nonce;
|
|
396
|
+
this.payload = payload;
|
|
397
|
+
this._version = EncryptedData.version;
|
|
398
|
+
}
|
|
399
|
+
get version() {
|
|
400
|
+
return this._version;
|
|
401
|
+
}
|
|
402
|
+
get length() {
|
|
403
|
+
return this.toBytes().length;
|
|
404
|
+
}
|
|
405
|
+
toBytes() {
|
|
406
|
+
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this._version, 1), (0, utils_1.numberToBytes)(this.header.length, 3), this.header, this.nonce, this.payload);
|
|
407
|
+
}
|
|
408
|
+
toJSON() {
|
|
409
|
+
return {
|
|
410
|
+
version: this._version,
|
|
411
|
+
header: (0, utils_1.decodeBase64)(this.header),
|
|
412
|
+
nonce: (0, utils_1.decodeBase64)(this.nonce),
|
|
413
|
+
payload: (0, utils_1.decodeBase64)(this.payload)
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
static from(data) {
|
|
417
|
+
if (data instanceof EncryptedData)
|
|
418
|
+
data = data.toBytes();
|
|
419
|
+
const headerLength = (0, utils_1.bytesToNumber)(data.subarray(1, 4));
|
|
420
|
+
const obj = new EncryptedData(data.subarray(4, 4 + headerLength), data.subarray(4 + headerLength, 4 + headerLength + this.nonceLength), data.subarray(4 + headerLength + this.nonceLength));
|
|
421
|
+
obj._version = (0, utils_1.bytesToNumber)(data.subarray(0, 1));
|
|
422
|
+
return obj;
|
|
422
423
|
}
|
|
423
424
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.countLength);
|
|
428
|
-
Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
|
|
429
|
-
Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
|
|
430
|
-
Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
|
|
425
|
+
exports.EncryptedData = EncryptedData;
|
|
426
|
+
EncryptedData.version = 1;
|
|
427
|
+
EncryptedData.nonceLength = crypto_1.default.box.nonceLength;
|
|
431
428
|
class AsyncMap {
|
|
432
429
|
constructor(iterable) {
|
|
433
430
|
this.map = new Map(iterable);
|
package/dist/x3dh.js
CHANGED
|
@@ -92,13 +92,14 @@ class KeyExchange {
|
|
|
92
92
|
const onetimePreKey = message.onetimePreKey ? (0, utils_1.encodeBase64)(message.onetimePreKey) : undefined;
|
|
93
93
|
const signedPreKeyHash = crypto_1.default.hash(signedPreKey);
|
|
94
94
|
const onetimePreKeyHash = onetimePreKey ? crypto_1.default.hash(onetimePreKey) : new Uint8Array();
|
|
95
|
-
const
|
|
95
|
+
const derivedKey = crypto_1.default.hkdf(new Uint8Array([
|
|
96
96
|
...crypto_1.default.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, signedPreKey),
|
|
97
97
|
...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, identityKey.exchangeKey),
|
|
98
98
|
...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, signedPreKey),
|
|
99
99
|
...onetimePreKey ? crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
|
|
100
|
-
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength);
|
|
101
|
-
|
|
100
|
+
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 2);
|
|
101
|
+
//, headerKey: derivedKey.subarray(KeySession.keyLength)
|
|
102
|
+
const session = new double_ratchet_1.KeySession({ remoteKey: identityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
|
|
102
103
|
const encrypted = (0, types_1.encryptData)(session, (0, utils_1.concatBytes)(crypto_1.default.hash(this.identityKey.toBytes()), crypto_1.default.hash(identityKey.toBytes()), associatedData !== null && associatedData !== void 0 ? associatedData : new Uint8Array()));
|
|
103
104
|
if (!encrypted)
|
|
104
105
|
throw new Error("Decryption error");
|
|
@@ -127,13 +128,14 @@ class KeyExchange {
|
|
|
127
128
|
if (!this.storage.delete(hash))
|
|
128
129
|
throw new Error("Bundle store deleting error");
|
|
129
130
|
const ephemeralKey = (0, utils_1.encodeBase64)(message.ephemeralKey);
|
|
130
|
-
const
|
|
131
|
+
const derivedKey = crypto_1.default.hkdf(new Uint8Array([
|
|
131
132
|
...crypto_1.default.ECDH.scalarMult(signedPreKey.secretKey, identityKey.exchangeKey),
|
|
132
133
|
...crypto_1.default.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, ephemeralKey),
|
|
133
134
|
...crypto_1.default.ECDH.scalarMult(signedPreKey.secretKey, ephemeralKey),
|
|
134
135
|
...onetimePreKey ? crypto_1.default.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
|
|
135
|
-
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength);
|
|
136
|
-
|
|
136
|
+
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 2);
|
|
137
|
+
//, nextHeaderKey: derivedKey.subarray(KeySession.keyLength)
|
|
138
|
+
const session = new double_ratchet_1.KeySession({ secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
|
|
137
139
|
const data = (0, types_1.decryptData)(session, (0, utils_1.encodeBase64)(message.associatedData));
|
|
138
140
|
if (!data)
|
|
139
141
|
throw new Error("Error decrypting ACK message");
|