@freesignal/protocol 0.2.5 → 0.2.8
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 +21 -8
- package/api.js +54 -65
- package/double-ratchet.d.ts +3 -4
- package/double-ratchet.js +5 -4
- package/index.js +0 -9
- package/package.json +2 -2
- package/types.d.ts +59 -2
- package/types.js +175 -21
- package/x3dh.d.ts +7 -0
- package/x3dh.js +16 -8
package/api.d.ts
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FreeSignal Protocol
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2025 Christian Braghette
|
|
5
|
+
*
|
|
6
|
+
* This program is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU General Public License
|
|
17
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
|
+
*/
|
|
1
19
|
import { Crypto, KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage } from "@freesignal/interfaces";
|
|
2
|
-
import {
|
|
20
|
+
import { ExportedKeySession } 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;
|
|
9
26
|
protected readonly boxKey: Crypto.KeyPair;
|
|
10
|
-
protected readonly sessions: LocalStorage<UserId,
|
|
27
|
+
protected readonly sessions: LocalStorage<UserId, ExportedKeySession>;
|
|
11
28
|
protected readonly keyExchange: KeyExchange;
|
|
12
29
|
protected readonly users: LocalStorage<UserId, IdentityKeys>;
|
|
13
30
|
readonly userId: UserId;
|
|
14
31
|
constructor(opts: {
|
|
15
32
|
secretSignKey: Uint8Array;
|
|
16
33
|
secretBoxKey: Uint8Array;
|
|
17
|
-
sessions: LocalStorage<UserId,
|
|
34
|
+
sessions: LocalStorage<UserId, ExportedKeySession>;
|
|
18
35
|
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
19
36
|
users: LocalStorage<UserId, IdentityKeys>;
|
|
20
37
|
});
|
|
@@ -33,9 +50,5 @@ export declare class FreeSignalAPI {
|
|
|
33
50
|
identityKeys: IdentityKeys;
|
|
34
51
|
userId: UserId;
|
|
35
52
|
}>;
|
|
36
|
-
protected packIdList(datagramIds: DatagramId[]): Uint8Array;
|
|
37
|
-
protected unpackIdList(data: Uint8Array): DatagramId[];
|
|
38
|
-
protected packDatagrams(messages: Datagram[]): Uint8Array;
|
|
39
|
-
protected unpackDatagrams(data: Uint8Array): Datagram[];
|
|
40
53
|
}
|
|
41
54
|
export {};
|
package/api.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* FreeSignal Protocol
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2025 Christian Braghette
|
|
6
|
+
*
|
|
7
|
+
* This program is free software: you can redistribute it and/or modify
|
|
8
|
+
* it under the terms of the GNU General Public License as published by
|
|
9
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
* (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* This program is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
* GNU General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* You should have received a copy of the GNU General Public License
|
|
18
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
19
|
+
*/
|
|
2
20
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
21
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
22
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -12,13 +30,12 @@ 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 =
|
|
33
|
+
exports.FreeSignalAPI = void 0;
|
|
16
34
|
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
35
|
+
const double_ratchet_1 = require("./double-ratchet");
|
|
17
36
|
const x3dh_1 = require("./x3dh");
|
|
18
37
|
const utils_1 = require("@freesignal/utils");
|
|
19
38
|
const types_1 = require("./types");
|
|
20
|
-
const fflate_1 = __importDefault(require("fflate"));
|
|
21
|
-
exports.FREESIGNAL_MIME = "application/x-freesignal";
|
|
22
39
|
class FreeSignalAPI {
|
|
23
40
|
constructor(opts) {
|
|
24
41
|
const { secretSignKey, secretBoxKey, sessions, keyExchange, users } = opts;
|
|
@@ -37,23 +54,25 @@ class FreeSignalAPI {
|
|
|
37
54
|
}
|
|
38
55
|
encryptData(data, userId) {
|
|
39
56
|
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
-
const
|
|
41
|
-
if (!
|
|
57
|
+
const sessionJson = yield this.sessions.get(userId);
|
|
58
|
+
if (!sessionJson)
|
|
42
59
|
throw new Error('Session not found for user: ' + userId);
|
|
60
|
+
const session = double_ratchet_1.KeySession.from(sessionJson);
|
|
43
61
|
const encrypted = session.encrypt(data);
|
|
44
|
-
this.sessions.set(userId, session); // Ensure session is updated
|
|
62
|
+
this.sessions.set(userId, session.toJSON()); // Ensure session is updated
|
|
45
63
|
return encrypted;
|
|
46
64
|
});
|
|
47
65
|
}
|
|
48
66
|
decryptData(data, userId) {
|
|
49
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
-
const
|
|
51
|
-
if (!
|
|
68
|
+
const sessionJson = yield this.sessions.get(userId);
|
|
69
|
+
if (!sessionJson)
|
|
52
70
|
throw new Error('Session not found for user: ' + userId);
|
|
71
|
+
const session = double_ratchet_1.KeySession.from(sessionJson);
|
|
53
72
|
const decrypted = session.decrypt(data);
|
|
54
73
|
if (!decrypted)
|
|
55
74
|
throw new Error('Decryption failed for user: ' + userId);
|
|
56
|
-
this.sessions.set(userId, session); // Ensure session is updated
|
|
75
|
+
this.sessions.set(userId, session.toJSON()); // Ensure session is updated
|
|
57
76
|
return decrypted;
|
|
58
77
|
});
|
|
59
78
|
}
|
|
@@ -62,7 +81,10 @@ class FreeSignalAPI {
|
|
|
62
81
|
const res = yield fetch(`${url}/${userId !== null && userId !== void 0 ? userId : ''}`, {
|
|
63
82
|
method: 'GET'
|
|
64
83
|
});
|
|
65
|
-
|
|
84
|
+
const body = types_1.XFreeSignal.decodeBody(new Uint8Array(yield res.arrayBuffer()));
|
|
85
|
+
if (body.type === 'error')
|
|
86
|
+
throw new Error(body.data);
|
|
87
|
+
return body.data;
|
|
66
88
|
});
|
|
67
89
|
}
|
|
68
90
|
postHandshake(url, message) {
|
|
@@ -70,9 +92,9 @@ class FreeSignalAPI {
|
|
|
70
92
|
const res = yield fetch(url, {
|
|
71
93
|
method: 'POST',
|
|
72
94
|
headers: {
|
|
73
|
-
'Content-Type':
|
|
95
|
+
'Content-Type': types_1.XFreeSignal.MIME
|
|
74
96
|
},
|
|
75
|
-
body: (
|
|
97
|
+
body: types_1.XFreeSignal.encodeBody('data', message)
|
|
76
98
|
});
|
|
77
99
|
return res.status === 200;
|
|
78
100
|
});
|
|
@@ -82,10 +104,10 @@ class FreeSignalAPI {
|
|
|
82
104
|
const res = yield fetch(url, {
|
|
83
105
|
method: 'PUT',
|
|
84
106
|
headers: {
|
|
85
|
-
'Content-Type':
|
|
107
|
+
'Content-Type': types_1.XFreeSignal.MIME,
|
|
86
108
|
authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
|
|
87
109
|
},
|
|
88
|
-
body: (
|
|
110
|
+
body: types_1.XFreeSignal.encodeBody('data', bundle)
|
|
89
111
|
});
|
|
90
112
|
return res.status === 201;
|
|
91
113
|
});
|
|
@@ -109,35 +131,44 @@ class FreeSignalAPI {
|
|
|
109
131
|
authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
|
|
110
132
|
}
|
|
111
133
|
});
|
|
112
|
-
|
|
134
|
+
const body = types_1.XFreeSignal.decodeBody(new Uint8Array(yield res.arrayBuffer()));
|
|
135
|
+
if (body.type === 'error')
|
|
136
|
+
throw new Error(body.data);
|
|
137
|
+
return types_1.DataEncoder.from(yield this.decryptData(body.data, types_1.UserId.getUserId(publicKey).toString())).data.map(array => types_1.Datagram.from(array));
|
|
113
138
|
});
|
|
114
139
|
}
|
|
115
140
|
postDatagrams(datagrams, publicKey, url) {
|
|
116
141
|
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
-
const data = yield this.encryptData(
|
|
142
|
+
const data = yield this.encryptData(new types_1.DataEncoder(datagrams.map(datagram => types_1.Datagram.from(datagram).encode())).encode(), types_1.UserId.getUserId(publicKey).toString());
|
|
118
143
|
const res = yield fetch(url, {
|
|
119
144
|
method: 'POST',
|
|
120
145
|
headers: {
|
|
121
|
-
'Content-Type':
|
|
146
|
+
'Content-Type': types_1.XFreeSignal.MIME,
|
|
122
147
|
authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
|
|
123
148
|
},
|
|
124
|
-
body: data.encode()
|
|
149
|
+
body: types_1.XFreeSignal.encodeBody('data', data.encode())
|
|
125
150
|
});
|
|
126
|
-
|
|
151
|
+
const body = types_1.XFreeSignal.decodeBody(new Uint8Array(yield res.arrayBuffer()));
|
|
152
|
+
if (body.type === 'error')
|
|
153
|
+
throw new Error(body.data);
|
|
154
|
+
return types_1.DataEncoder.from(yield this.decryptData(body.data, types_1.UserId.getUserId(publicKey).toString())).data;
|
|
127
155
|
});
|
|
128
156
|
}
|
|
129
157
|
deleteDatagrams(datagramIds, publicKey, url) {
|
|
130
158
|
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
-
const data = yield this.encryptData(
|
|
159
|
+
const data = yield this.encryptData(new types_1.DataEncoder(datagramIds.map(datagramId => crypto_1.default.UUID.parse(datagramId))).encode(), types_1.UserId.getUserId(publicKey).toString());
|
|
132
160
|
const res = yield fetch(url, {
|
|
133
161
|
method: 'DELETE',
|
|
134
162
|
headers: {
|
|
135
|
-
'Content-Type':
|
|
163
|
+
'Content-Type': types_1.XFreeSignal.MIME,
|
|
136
164
|
authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
|
|
137
165
|
},
|
|
138
|
-
body: data.encode()
|
|
166
|
+
body: types_1.XFreeSignal.encodeBody('data', data.encode())
|
|
139
167
|
});
|
|
140
|
-
|
|
168
|
+
const body = types_1.XFreeSignal.decodeBody(new Uint8Array(yield res.arrayBuffer()));
|
|
169
|
+
if (body.type === 'error')
|
|
170
|
+
throw new Error(body.data);
|
|
171
|
+
return types_1.DataEncoder.from(yield this.decryptData(body.data, types_1.UserId.getUserId(publicKey).toString())).data;
|
|
141
172
|
});
|
|
142
173
|
}
|
|
143
174
|
createToken(publicKey) {
|
|
@@ -160,47 +191,5 @@ class FreeSignalAPI {
|
|
|
160
191
|
throw new Error('Authorization header is required');
|
|
161
192
|
});
|
|
162
193
|
}
|
|
163
|
-
packIdList(datagramIds) {
|
|
164
|
-
return datagramIds.map(datagramId => crypto_1.default.UUID.parse(datagramId)).reduce((prev, curr) => new Uint8Array([...prev, ...curr]), new Uint8Array());
|
|
165
|
-
}
|
|
166
|
-
unpackIdList(data) {
|
|
167
|
-
const ids = [];
|
|
168
|
-
for (let i = 0; i < data.length; i += 16) {
|
|
169
|
-
ids.push(crypto_1.default.UUID.stringify(data.subarray(i, i + 16)));
|
|
170
|
-
}
|
|
171
|
-
return ids;
|
|
172
|
-
}
|
|
173
|
-
packDatagrams(messages) {
|
|
174
|
-
return fflate_1.default.deflateSync((0, utils_1.concatUint8Array)(...messages.flatMap(datagram => {
|
|
175
|
-
const encoded = types_1.Datagram.from(datagram).encode();
|
|
176
|
-
return [(0, utils_1.numberToUint8Array)(encoded.length, 8), encoded];
|
|
177
|
-
})));
|
|
178
|
-
}
|
|
179
|
-
unpackDatagrams(data) {
|
|
180
|
-
const messages = [];
|
|
181
|
-
let offset = 0;
|
|
182
|
-
data = fflate_1.default.inflateSync(data);
|
|
183
|
-
while (offset < data.length) {
|
|
184
|
-
const length = data.subarray(offset, offset + 8);
|
|
185
|
-
if (length.length < 8) {
|
|
186
|
-
throw new Error('Invalid message length');
|
|
187
|
-
}
|
|
188
|
-
const messageLength = (0, utils_1.numberFromUint8Array)(length);
|
|
189
|
-
offset += 8;
|
|
190
|
-
if (offset + messageLength > data.length) {
|
|
191
|
-
throw new Error('Invalid message length');
|
|
192
|
-
}
|
|
193
|
-
const messageData = data.subarray(offset, offset + messageLength);
|
|
194
|
-
offset += messageLength;
|
|
195
|
-
try {
|
|
196
|
-
const datagram = types_1.Datagram.from(messageData);
|
|
197
|
-
messages.push(datagram);
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
throw new Error('Invalid datagram format');
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return messages;
|
|
204
|
-
}
|
|
205
194
|
}
|
|
206
195
|
exports.FreeSignalAPI = FreeSignalAPI;
|
package/double-ratchet.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 { EncryptedData } from "./types";
|
|
20
|
-
|
|
20
|
+
export interface ExportedKeySession {
|
|
21
21
|
secretKey: string;
|
|
22
22
|
remoteKey: string;
|
|
23
23
|
rootKey: string;
|
|
@@ -27,7 +27,7 @@ type ExportedKeySession = {
|
|
|
27
27
|
receivingCount: number;
|
|
28
28
|
previousCount: number;
|
|
29
29
|
previousKeys: [number, Uint8Array][];
|
|
30
|
-
}
|
|
30
|
+
}
|
|
31
31
|
/**
|
|
32
32
|
* Represents a secure Double Ratchet session.
|
|
33
33
|
* Used for forward-secure encryption and decryption of messages.
|
|
@@ -90,7 +90,7 @@ export declare class KeySession {
|
|
|
90
90
|
* @param json string returned by `export()` method.
|
|
91
91
|
* @returns session with the state parsed.
|
|
92
92
|
*/
|
|
93
|
-
static
|
|
93
|
+
static from(data: ExportedKeySession): 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.
|
|
@@ -98,4 +98,3 @@ export declare class KeySession {
|
|
|
98
98
|
static readonly keyLength = 32;
|
|
99
99
|
private static symmetricRatchet;
|
|
100
100
|
}
|
|
101
|
-
export {};
|
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 (!
|
|
120
|
+
if (!this._remoteKey)
|
|
121
|
+
throw new Error("Missing remoteKey");
|
|
122
|
+
if (!(0, utils_1.verifyArrays)(publicKey, this._remoteKey)) {
|
|
121
123
|
while (this.receivingCount < encrypted.previous)
|
|
122
124
|
this.previousKeys.set(this.receivingCount, this.getReceivingKey());
|
|
123
125
|
this.setRemoteKey(publicKey);
|
|
@@ -143,7 +145,7 @@ class KeySession {
|
|
|
143
145
|
*/
|
|
144
146
|
toJSON() {
|
|
145
147
|
return {
|
|
146
|
-
secretKey: (0, utils_1.decodeBase64)((0, utils_1.
|
|
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,8 +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
|
|
164
|
-
const data = JSON.parse(json);
|
|
165
|
+
static from(data) {
|
|
165
166
|
const session = new KeySession({ secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: (0, utils_1.encodeBase64)(data.rootKey) });
|
|
166
167
|
session._remoteKey = (0, utils_1.encodeBase64)(data.remoteKey);
|
|
167
168
|
session.sendingChain = (0, utils_1.encodeBase64)(data.sendingChain);
|
package/index.js
CHANGED
|
@@ -61,15 +61,6 @@ function createKeyExchange(signSecretKey, boxSecretKey, bundleStore) {
|
|
|
61
61
|
function createIdentityKeys(signSecretKey, boxSecretKey) {
|
|
62
62
|
return { sign: crypto_1.default.EdDSA.keyPair(signSecretKey), box: crypto_1.default.ECDH.keyPair(boxSecretKey) };
|
|
63
63
|
}
|
|
64
|
-
/*export function createAPI(opts: {
|
|
65
|
-
secretSignKey: Uint8Array;
|
|
66
|
-
secretBoxKey: Uint8Array;
|
|
67
|
-
sessions: LocalStorage<UserId, KeySession>;
|
|
68
|
-
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
69
|
-
users: LocalStorage<UserId, IdentityKeys>;
|
|
70
|
-
}): FreeSignalAPI {
|
|
71
|
-
return new FreeSignalAPI(opts);
|
|
72
|
-
}*/
|
|
73
64
|
var types_1 = require("./types");
|
|
74
65
|
Object.defineProperty(exports, "UserId", { enumerable: true, get: function () { return types_1.UserId; } });
|
|
75
66
|
Object.defineProperty(exports, "IdentityKeys", { enumerable: true, get: function () { return types_1.IdentityKeys; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freesignal/protocol",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
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.
|
|
16
|
+
"@freesignal/utils": "^1.2.0",
|
|
17
17
|
"base64-js": "^1.5.1",
|
|
18
18
|
"fflate": "^0.8.2",
|
|
19
19
|
"js-sha3": "^0.9.3",
|
package/types.d.ts
CHANGED
|
@@ -144,7 +144,14 @@ export interface EncryptedData extends Encodable {
|
|
|
144
144
|
/**
|
|
145
145
|
* Returns the decoded object as a JSON string.
|
|
146
146
|
*/
|
|
147
|
-
toJSON():
|
|
147
|
+
toJSON(): {
|
|
148
|
+
version: number;
|
|
149
|
+
count: number;
|
|
150
|
+
previous: number;
|
|
151
|
+
publicKey: string;
|
|
152
|
+
nonce: string;
|
|
153
|
+
ciphertext: string;
|
|
154
|
+
};
|
|
148
155
|
}
|
|
149
156
|
export declare class EncryptedData {
|
|
150
157
|
/**
|
|
@@ -174,5 +181,55 @@ export declare class EncryptedDataConstructor implements EncryptedData {
|
|
|
174
181
|
get ciphertext(): Uint8Array;
|
|
175
182
|
encode(): Uint8Array;
|
|
176
183
|
toString(): string;
|
|
177
|
-
toJSON():
|
|
184
|
+
toJSON(): {
|
|
185
|
+
version: number;
|
|
186
|
+
count: number;
|
|
187
|
+
previous: number;
|
|
188
|
+
publicKey: string;
|
|
189
|
+
nonce: string;
|
|
190
|
+
ciphertext: string;
|
|
191
|
+
};
|
|
178
192
|
}
|
|
193
|
+
declare enum DataType {
|
|
194
|
+
UKNOWN = -1,
|
|
195
|
+
RAW = 0,
|
|
196
|
+
NUMBER = 1,
|
|
197
|
+
STRING = 2,
|
|
198
|
+
ARRAY = 3,
|
|
199
|
+
OBJECT = 4
|
|
200
|
+
}
|
|
201
|
+
declare namespace DataType {
|
|
202
|
+
function getType(type: string): DataType;
|
|
203
|
+
function getName(type: DataType): string;
|
|
204
|
+
function from(data: any): DataType;
|
|
205
|
+
}
|
|
206
|
+
export declare class DataEncoder<T> implements Encodable {
|
|
207
|
+
readonly data: T;
|
|
208
|
+
readonly type: string;
|
|
209
|
+
constructor(data: T);
|
|
210
|
+
protected get _type(): DataType;
|
|
211
|
+
encode(): Uint8Array;
|
|
212
|
+
toString(): string;
|
|
213
|
+
toJSON(): T;
|
|
214
|
+
static from<T = any>(array: Uint8Array): DataEncoder<T>;
|
|
215
|
+
}
|
|
216
|
+
export declare namespace XFreeSignal {
|
|
217
|
+
export const MIME = "application/x-freesignal";
|
|
218
|
+
export const version = 1;
|
|
219
|
+
export function encodeBody(type: 'data' | 'error', data: any, compressed?: boolean): BodyInit;
|
|
220
|
+
export function decodeBody<T = any>(body: Uint8Array): Body<T>;
|
|
221
|
+
class Body<T> implements Encodable {
|
|
222
|
+
readonly type: 'data' | 'error';
|
|
223
|
+
readonly data: T;
|
|
224
|
+
constructor(type: 'data' | 'error', data: T);
|
|
225
|
+
encode(compressed?: boolean): Uint8Array;
|
|
226
|
+
toString(): string;
|
|
227
|
+
toJSON(): {
|
|
228
|
+
type: "data" | "error";
|
|
229
|
+
data: T;
|
|
230
|
+
};
|
|
231
|
+
static from<T = any>(array: Uint8Array): Body<T>;
|
|
232
|
+
}
|
|
233
|
+
export {};
|
|
234
|
+
}
|
|
235
|
+
export {};
|
package/types.js
CHANGED
|
@@ -21,10 +21,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
22
|
};
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.EncryptedDataConstructor = exports.EncryptedData = exports.Datagram = exports.Protocols = exports.IdentityKeys = exports.UserId = void 0;
|
|
24
|
+
exports.XFreeSignal = exports.DataEncoder = exports.EncryptedDataConstructor = exports.EncryptedData = exports.Datagram = exports.Protocols = exports.IdentityKeys = exports.UserId = void 0;
|
|
25
25
|
const utils_1 = require("@freesignal/utils");
|
|
26
26
|
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
27
27
|
const double_ratchet_1 = require("./double-ratchet");
|
|
28
|
+
const fflate_1 = __importDefault(require("fflate"));
|
|
28
29
|
var UserId;
|
|
29
30
|
(function (UserId) {
|
|
30
31
|
class UserIdConstructor {
|
|
@@ -36,7 +37,7 @@ var UserId;
|
|
|
36
37
|
return (0, utils_1.decodeBase64)(this.array);
|
|
37
38
|
}
|
|
38
39
|
toJSON() {
|
|
39
|
-
return
|
|
40
|
+
return this.toString();
|
|
40
41
|
}
|
|
41
42
|
toUint8Array() {
|
|
42
43
|
return this.array;
|
|
@@ -68,13 +69,13 @@ var IdentityKeys;
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
encode() {
|
|
71
|
-
return (0, utils_1.
|
|
72
|
+
return (0, utils_1.concatArrays)((0, utils_1.encodeBase64)(this.publicKey), (0, utils_1.encodeBase64)(this.identityKey));
|
|
72
73
|
}
|
|
73
74
|
toString() {
|
|
74
75
|
throw (0, utils_1.decodeBase64)(this.encode());
|
|
75
76
|
}
|
|
76
77
|
toJSON() {
|
|
77
|
-
throw
|
|
78
|
+
throw this.toString();
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
function isIdentityKeys(obj) {
|
|
@@ -107,11 +108,11 @@ var Protocols;
|
|
|
107
108
|
}
|
|
108
109
|
Protocols.toCode = toCode;
|
|
109
110
|
function encode(protocol, length) {
|
|
110
|
-
return (0, utils_1.
|
|
111
|
+
return (0, utils_1.numberToArray)(Protocols.toCode(protocol), length);
|
|
111
112
|
}
|
|
112
113
|
Protocols.encode = encode;
|
|
113
114
|
function decode(array) {
|
|
114
|
-
return Protocols.fromCode((0, utils_1.
|
|
115
|
+
return Protocols.fromCode((0, utils_1.numberFromArray)(array));
|
|
115
116
|
}
|
|
116
117
|
Protocols.decode = decode;
|
|
117
118
|
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
@@ -125,7 +126,7 @@ var Datagram;
|
|
|
125
126
|
this.version = data[0] & 127;
|
|
126
127
|
this.protocol = Protocols.decode(data.subarray(1, 2));
|
|
127
128
|
this.id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
|
|
128
|
-
this.createdAt = (0, utils_1.
|
|
129
|
+
this.createdAt = (0, utils_1.numberFromArray)(data.subarray(18, 26));
|
|
129
130
|
this.sender = (0, utils_1.decodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength));
|
|
130
131
|
this.receiver = (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, DatagramConstructor.headerOffset));
|
|
131
132
|
if (data[0] & 128)
|
|
@@ -177,16 +178,16 @@ var Datagram;
|
|
|
177
178
|
}
|
|
178
179
|
encode() {
|
|
179
180
|
var _a, _b, _c;
|
|
180
|
-
const data = (0, utils_1.
|
|
181
|
+
const data = (0, utils_1.concatArrays)(new Uint8Array(1).fill(this.version | (this.secretKey ? 128 : 0)), //1
|
|
181
182
|
Protocols.encode(this.protocol), //1
|
|
182
183
|
(_a = crypto_1.default.UUID.parse(this.id)) !== null && _a !== void 0 ? _a : [], //16
|
|
183
|
-
(0, utils_1.
|
|
184
|
+
(0, utils_1.numberToArray)(this.createdAt, 8), //8
|
|
184
185
|
(0, utils_1.encodeBase64)(this.sender), //32
|
|
185
186
|
(0, utils_1.encodeBase64)(this.receiver), //32
|
|
186
187
|
(_b = this._payload) !== null && _b !== void 0 ? _b : new Uint8Array());
|
|
187
188
|
if (this.secretKey)
|
|
188
189
|
this._signature = crypto_1.default.EdDSA.sign(data, this.secretKey);
|
|
189
|
-
return (0, utils_1.
|
|
190
|
+
return (0, utils_1.concatArrays)(data, (_c = this._signature) !== null && _c !== void 0 ? _c : new Uint8Array());
|
|
190
191
|
}
|
|
191
192
|
sign(secretKey) {
|
|
192
193
|
this.secretKey = secretKey;
|
|
@@ -196,7 +197,7 @@ var Datagram;
|
|
|
196
197
|
return (0, utils_1.decodeBase64)(this.encode());
|
|
197
198
|
}
|
|
198
199
|
toJSON() {
|
|
199
|
-
return
|
|
200
|
+
return this.toString();
|
|
200
201
|
}
|
|
201
202
|
}
|
|
202
203
|
DatagramConstructor.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
|
|
@@ -238,22 +239,22 @@ class EncryptedDataConstructor {
|
|
|
238
239
|
return this;
|
|
239
240
|
}
|
|
240
241
|
if (typeof arrays[0] === 'number')
|
|
241
|
-
arrays[0] = (0, utils_1.
|
|
242
|
+
arrays[0] = (0, utils_1.numberToArray)(arrays[0], EncryptedDataConstructor.countLength);
|
|
242
243
|
if (typeof arrays[1] === 'number')
|
|
243
|
-
arrays[1] = (0, utils_1.
|
|
244
|
+
arrays[1] = (0, utils_1.numberToArray)(arrays[1], EncryptedDataConstructor.countLength);
|
|
244
245
|
if (arrays.length === 6) {
|
|
245
|
-
arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.
|
|
246
|
+
arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToArray)(arrays[5]) : arrays[5]);
|
|
246
247
|
arrays.pop();
|
|
247
248
|
}
|
|
248
249
|
else if (arrays.length > 1) {
|
|
249
|
-
arrays.unshift((0, utils_1.
|
|
250
|
+
arrays.unshift((0, utils_1.numberToArray)(double_ratchet_1.KeySession.version));
|
|
250
251
|
}
|
|
251
|
-
this.raw = (0, utils_1.
|
|
252
|
+
this.raw = (0, utils_1.concatArrays)(...arrays);
|
|
252
253
|
}
|
|
253
254
|
get length() { return this.raw.length; }
|
|
254
|
-
get version() { return (0, utils_1.
|
|
255
|
-
get count() { return (0, utils_1.
|
|
256
|
-
get previous() { return (0, utils_1.
|
|
255
|
+
get version() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
|
|
256
|
+
get count() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
|
|
257
|
+
get previous() { return (0, utils_1.numberFromArray)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
|
|
257
258
|
get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
|
|
258
259
|
get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
|
|
259
260
|
get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
|
|
@@ -264,14 +265,14 @@ class EncryptedDataConstructor {
|
|
|
264
265
|
return (0, utils_1.decodeBase64)(this.raw);
|
|
265
266
|
}
|
|
266
267
|
toJSON() {
|
|
267
|
-
return
|
|
268
|
+
return {
|
|
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
|
+
};
|
|
275
276
|
}
|
|
276
277
|
}
|
|
277
278
|
exports.EncryptedDataConstructor = EncryptedDataConstructor;
|
|
@@ -304,3 +305,156 @@ Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.count
|
|
|
304
305
|
Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
|
|
305
306
|
Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
|
|
306
307
|
Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
|
|
308
|
+
var DataType;
|
|
309
|
+
(function (DataType) {
|
|
310
|
+
DataType[DataType["UKNOWN"] = -1] = "UKNOWN";
|
|
311
|
+
DataType[DataType["RAW"] = 0] = "RAW";
|
|
312
|
+
DataType[DataType["NUMBER"] = 1] = "NUMBER";
|
|
313
|
+
DataType[DataType["STRING"] = 2] = "STRING";
|
|
314
|
+
DataType[DataType["ARRAY"] = 3] = "ARRAY";
|
|
315
|
+
DataType[DataType["OBJECT"] = 4] = "OBJECT";
|
|
316
|
+
})(DataType || (DataType = {}));
|
|
317
|
+
(function (DataType) {
|
|
318
|
+
function getType(type) {
|
|
319
|
+
return Object.values(DataType).indexOf(type.toLocaleUpperCase());
|
|
320
|
+
}
|
|
321
|
+
DataType.getType = getType;
|
|
322
|
+
function getName(type) {
|
|
323
|
+
return DataType[type].toLowerCase();
|
|
324
|
+
}
|
|
325
|
+
DataType.getName = getName;
|
|
326
|
+
function from(data) {
|
|
327
|
+
if (data instanceof Uint8Array)
|
|
328
|
+
return DataType.RAW;
|
|
329
|
+
return getType(typeof data);
|
|
330
|
+
}
|
|
331
|
+
DataType.from = from;
|
|
332
|
+
})(DataType || (DataType = {}));
|
|
333
|
+
class DataEncoder {
|
|
334
|
+
constructor(data) {
|
|
335
|
+
this.data = data;
|
|
336
|
+
this.type = DataType.getName(DataType.from(this.data));
|
|
337
|
+
}
|
|
338
|
+
get _type() {
|
|
339
|
+
return DataType.getType(this.type);
|
|
340
|
+
}
|
|
341
|
+
encode() {
|
|
342
|
+
let data;
|
|
343
|
+
switch (this._type) {
|
|
344
|
+
case DataType.RAW:
|
|
345
|
+
data = this.data;
|
|
346
|
+
break;
|
|
347
|
+
case DataType.NUMBER:
|
|
348
|
+
data = (0, utils_1.numberToArray)(this._type);
|
|
349
|
+
break;
|
|
350
|
+
case DataType.STRING:
|
|
351
|
+
data = (0, utils_1.encodeUTF8)(this.data);
|
|
352
|
+
break;
|
|
353
|
+
case DataType.ARRAY:
|
|
354
|
+
data = (0, utils_1.concatArrays)(...Array.from(this.data).flatMap(value => {
|
|
355
|
+
const data = new DataEncoder(value).encode();
|
|
356
|
+
return [(0, utils_1.numberToArray)(data.length, 8), data];
|
|
357
|
+
}));
|
|
358
|
+
break;
|
|
359
|
+
case DataType.OBJECT:
|
|
360
|
+
data = (0, utils_1.encodeJSON)(this.data);
|
|
361
|
+
break;
|
|
362
|
+
default:
|
|
363
|
+
throw new Error("Uknown type");
|
|
364
|
+
}
|
|
365
|
+
return (0, utils_1.concatArrays)((0, utils_1.numberToArray)(this._type), data);
|
|
366
|
+
}
|
|
367
|
+
toString() {
|
|
368
|
+
return "[Object XFreeSignalData]";
|
|
369
|
+
}
|
|
370
|
+
toJSON() {
|
|
371
|
+
return 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 {
|
|
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.d.ts
CHANGED
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
import { KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage, Crypto } from "@freesignal/interfaces";
|
|
20
20
|
import { KeySession } from "./double-ratchet";
|
|
21
21
|
import { IdentityKeys } from "./types";
|
|
22
|
+
export interface ExportedKeyExchange {
|
|
23
|
+
signatureKey: Crypto.KeyPair;
|
|
24
|
+
identityKey: Crypto.KeyPair;
|
|
25
|
+
bundleStore: Array<[string, Crypto.KeyPair]>;
|
|
26
|
+
}
|
|
22
27
|
export declare class KeyExchange {
|
|
23
28
|
static readonly version = 1;
|
|
24
29
|
private static readonly hkdfInfo;
|
|
@@ -42,4 +47,6 @@ export declare class KeyExchange {
|
|
|
42
47
|
session: KeySession;
|
|
43
48
|
identityKeys: IdentityKeys;
|
|
44
49
|
}>;
|
|
50
|
+
toJSON(): ExportedKeyExchange;
|
|
51
|
+
static from(data: ExportedKeyExchange): KeyExchange;
|
|
45
52
|
}
|
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.
|
|
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.
|
|
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,
|
|
@@ -146,19 +146,29 @@ class KeyExchange {
|
|
|
146
146
|
};
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
|
+
toJSON() {
|
|
150
|
+
return {
|
|
151
|
+
identityKey: this._identityKey,
|
|
152
|
+
signatureKey: this._signatureKey,
|
|
153
|
+
bundleStore: Array.from(this.bundleStore.entries())
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
static from(data) {
|
|
157
|
+
return new KeyExchange(data.signatureKey.secretKey, data.identityKey.secretKey, new AsyncMap(data.bundleStore));
|
|
158
|
+
}
|
|
149
159
|
}
|
|
150
160
|
exports.KeyExchange = KeyExchange;
|
|
151
161
|
KeyExchange.version = 1;
|
|
152
162
|
KeyExchange.hkdfInfo = (0, utils_1.encodeUTF8)("freesignal/x3dh/" + KeyExchange.version);
|
|
153
163
|
KeyExchange.maxOPK = 10;
|
|
154
164
|
class AsyncMap {
|
|
155
|
-
constructor() {
|
|
156
|
-
this.map = new Map();
|
|
165
|
+
constructor(iterable) {
|
|
166
|
+
this.map = new Map(iterable);
|
|
157
167
|
}
|
|
158
168
|
set(key, value) {
|
|
159
169
|
return __awaiter(this, void 0, void 0, function* () {
|
|
160
170
|
this.map.set(key, value);
|
|
161
|
-
return
|
|
171
|
+
return;
|
|
162
172
|
});
|
|
163
173
|
}
|
|
164
174
|
get(key) {
|
|
@@ -177,8 +187,6 @@ class AsyncMap {
|
|
|
177
187
|
});
|
|
178
188
|
}
|
|
179
189
|
entries() {
|
|
180
|
-
return
|
|
181
|
-
return this.map.entries();
|
|
182
|
-
});
|
|
190
|
+
return this.map.entries();
|
|
183
191
|
}
|
|
184
192
|
}
|