@freesignal/protocol 0.2.6 → 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 +3 -3
- package/api.js +25 -10
- package/double-ratchet.d.ts +3 -4
- package/double-ratchet.js +1 -2
- package/package.json +1 -1
- package/types.d.ts +21 -4
- package/types.js +8 -8
- package/x3dh.d.ts +7 -0
- package/x3dh.js +14 -6
package/api.d.ts
CHANGED
|
@@ -17,21 +17,21 @@
|
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
19
|
import { Crypto, KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage } from "@freesignal/interfaces";
|
|
20
|
-
import {
|
|
20
|
+
import { ExportedKeySession } from "./double-ratchet";
|
|
21
21
|
import { KeyExchange } from "./x3dh";
|
|
22
22
|
import { Datagram, IdentityKeys, EncryptedData, UserId } from "./types";
|
|
23
23
|
type DatagramId = string;
|
|
24
24
|
export declare class FreeSignalAPI {
|
|
25
25
|
protected readonly signKey: Crypto.KeyPair;
|
|
26
26
|
protected readonly boxKey: Crypto.KeyPair;
|
|
27
|
-
protected readonly sessions: LocalStorage<UserId,
|
|
27
|
+
protected readonly sessions: LocalStorage<UserId, ExportedKeySession>;
|
|
28
28
|
protected readonly keyExchange: KeyExchange;
|
|
29
29
|
protected readonly users: LocalStorage<UserId, IdentityKeys>;
|
|
30
30
|
readonly userId: UserId;
|
|
31
31
|
constructor(opts: {
|
|
32
32
|
secretSignKey: Uint8Array;
|
|
33
33
|
secretBoxKey: Uint8Array;
|
|
34
|
-
sessions: LocalStorage<UserId,
|
|
34
|
+
sessions: LocalStorage<UserId, ExportedKeySession>;
|
|
35
35
|
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
36
36
|
users: LocalStorage<UserId, IdentityKeys>;
|
|
37
37
|
});
|
package/api.js
CHANGED
|
@@ -32,6 +32,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
33
|
exports.FreeSignalAPI = void 0;
|
|
34
34
|
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
35
|
+
const double_ratchet_1 = require("./double-ratchet");
|
|
35
36
|
const x3dh_1 = require("./x3dh");
|
|
36
37
|
const utils_1 = require("@freesignal/utils");
|
|
37
38
|
const types_1 = require("./types");
|
|
@@ -53,23 +54,25 @@ class FreeSignalAPI {
|
|
|
53
54
|
}
|
|
54
55
|
encryptData(data, userId) {
|
|
55
56
|
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
-
const
|
|
57
|
-
if (!
|
|
57
|
+
const sessionJson = yield this.sessions.get(userId);
|
|
58
|
+
if (!sessionJson)
|
|
58
59
|
throw new Error('Session not found for user: ' + userId);
|
|
60
|
+
const session = double_ratchet_1.KeySession.from(sessionJson);
|
|
59
61
|
const encrypted = session.encrypt(data);
|
|
60
|
-
this.sessions.set(userId, session); // Ensure session is updated
|
|
62
|
+
this.sessions.set(userId, session.toJSON()); // Ensure session is updated
|
|
61
63
|
return encrypted;
|
|
62
64
|
});
|
|
63
65
|
}
|
|
64
66
|
decryptData(data, userId) {
|
|
65
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
-
const
|
|
67
|
-
if (!
|
|
68
|
+
const sessionJson = yield this.sessions.get(userId);
|
|
69
|
+
if (!sessionJson)
|
|
68
70
|
throw new Error('Session not found for user: ' + userId);
|
|
71
|
+
const session = double_ratchet_1.KeySession.from(sessionJson);
|
|
69
72
|
const decrypted = session.decrypt(data);
|
|
70
73
|
if (!decrypted)
|
|
71
74
|
throw new Error('Decryption failed for user: ' + userId);
|
|
72
|
-
this.sessions.set(userId, session); // Ensure session is updated
|
|
75
|
+
this.sessions.set(userId, session.toJSON()); // Ensure session is updated
|
|
73
76
|
return decrypted;
|
|
74
77
|
});
|
|
75
78
|
}
|
|
@@ -78,7 +81,10 @@ class FreeSignalAPI {
|
|
|
78
81
|
const res = yield fetch(`${url}/${userId !== null && userId !== void 0 ? userId : ''}`, {
|
|
79
82
|
method: 'GET'
|
|
80
83
|
});
|
|
81
|
-
|
|
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;
|
|
82
88
|
});
|
|
83
89
|
}
|
|
84
90
|
postHandshake(url, message) {
|
|
@@ -125,7 +131,10 @@ class FreeSignalAPI {
|
|
|
125
131
|
authorization: this.createToken(publicKey instanceof Uint8Array ? publicKey : (0, utils_1.encodeBase64)(publicKey))
|
|
126
132
|
}
|
|
127
133
|
});
|
|
128
|
-
|
|
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));
|
|
129
138
|
});
|
|
130
139
|
}
|
|
131
140
|
postDatagrams(datagrams, publicKey, url) {
|
|
@@ -139,7 +148,10 @@ class FreeSignalAPI {
|
|
|
139
148
|
},
|
|
140
149
|
body: types_1.XFreeSignal.encodeBody('data', data.encode())
|
|
141
150
|
});
|
|
142
|
-
|
|
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;
|
|
143
155
|
});
|
|
144
156
|
}
|
|
145
157
|
deleteDatagrams(datagramIds, publicKey, url) {
|
|
@@ -153,7 +165,10 @@ class FreeSignalAPI {
|
|
|
153
165
|
},
|
|
154
166
|
body: types_1.XFreeSignal.encodeBody('data', data.encode())
|
|
155
167
|
});
|
|
156
|
-
|
|
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;
|
|
157
172
|
});
|
|
158
173
|
}
|
|
159
174
|
createToken(publicKey) {
|
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
|
@@ -162,8 +162,7 @@ class KeySession {
|
|
|
162
162
|
* @param json string returned by `export()` method.
|
|
163
163
|
* @returns session with the state parsed.
|
|
164
164
|
*/
|
|
165
|
-
static
|
|
166
|
-
const data = JSON.parse(json);
|
|
165
|
+
static from(data) {
|
|
167
166
|
const session = new KeySession({ secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: (0, utils_1.encodeBase64)(data.rootKey) });
|
|
168
167
|
session._remoteKey = (0, utils_1.encodeBase64)(data.remoteKey);
|
|
169
168
|
session.sendingChain = (0, utils_1.encodeBase64)(data.sendingChain);
|
package/package.json
CHANGED
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,7 +181,14 @@ 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
|
}
|
|
179
193
|
declare enum DataType {
|
|
180
194
|
UKNOWN = -1,
|
|
@@ -196,7 +210,7 @@ export declare class DataEncoder<T> implements Encodable {
|
|
|
196
210
|
protected get _type(): DataType;
|
|
197
211
|
encode(): Uint8Array;
|
|
198
212
|
toString(): string;
|
|
199
|
-
toJSON():
|
|
213
|
+
toJSON(): T;
|
|
200
214
|
static from<T = any>(array: Uint8Array): DataEncoder<T>;
|
|
201
215
|
}
|
|
202
216
|
export declare namespace XFreeSignal {
|
|
@@ -210,7 +224,10 @@ export declare namespace XFreeSignal {
|
|
|
210
224
|
constructor(type: 'data' | 'error', data: T);
|
|
211
225
|
encode(compressed?: boolean): Uint8Array;
|
|
212
226
|
toString(): string;
|
|
213
|
-
toJSON():
|
|
227
|
+
toJSON(): {
|
|
228
|
+
type: "data" | "error";
|
|
229
|
+
data: T;
|
|
230
|
+
};
|
|
214
231
|
static from<T = any>(array: Uint8Array): Body<T>;
|
|
215
232
|
}
|
|
216
233
|
export {};
|
package/types.js
CHANGED
|
@@ -37,7 +37,7 @@ var UserId;
|
|
|
37
37
|
return (0, utils_1.decodeBase64)(this.array);
|
|
38
38
|
}
|
|
39
39
|
toJSON() {
|
|
40
|
-
return
|
|
40
|
+
return this.toString();
|
|
41
41
|
}
|
|
42
42
|
toUint8Array() {
|
|
43
43
|
return this.array;
|
|
@@ -75,7 +75,7 @@ var IdentityKeys;
|
|
|
75
75
|
throw (0, utils_1.decodeBase64)(this.encode());
|
|
76
76
|
}
|
|
77
77
|
toJSON() {
|
|
78
|
-
throw
|
|
78
|
+
throw this.toString();
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
function isIdentityKeys(obj) {
|
|
@@ -197,7 +197,7 @@ var Datagram;
|
|
|
197
197
|
return (0, utils_1.decodeBase64)(this.encode());
|
|
198
198
|
}
|
|
199
199
|
toJSON() {
|
|
200
|
-
return
|
|
200
|
+
return this.toString();
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
DatagramConstructor.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
|
|
@@ -265,14 +265,14 @@ class EncryptedDataConstructor {
|
|
|
265
265
|
return (0, utils_1.decodeBase64)(this.raw);
|
|
266
266
|
}
|
|
267
267
|
toJSON() {
|
|
268
|
-
return
|
|
268
|
+
return {
|
|
269
269
|
version: this.version,
|
|
270
270
|
count: this.count,
|
|
271
271
|
previous: this.previous,
|
|
272
272
|
publicKey: (0, utils_1.decodeBase64)(this.publicKey),
|
|
273
273
|
nonce: (0, utils_1.decodeBase64)(this.nonce),
|
|
274
274
|
ciphertext: (0, utils_1.decodeBase64)(this.ciphertext)
|
|
275
|
-
}
|
|
275
|
+
};
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
exports.EncryptedDataConstructor = EncryptedDataConstructor;
|
|
@@ -368,7 +368,7 @@ class DataEncoder {
|
|
|
368
368
|
return "[Object XFreeSignalData]";
|
|
369
369
|
}
|
|
370
370
|
toJSON() {
|
|
371
|
-
return
|
|
371
|
+
return this.data;
|
|
372
372
|
}
|
|
373
373
|
static from(array) {
|
|
374
374
|
const type = array[0];
|
|
@@ -448,10 +448,10 @@ var XFreeSignal;
|
|
|
448
448
|
return "[Object XFreeSignalBody]";
|
|
449
449
|
}
|
|
450
450
|
toJSON() {
|
|
451
|
-
return
|
|
451
|
+
return {
|
|
452
452
|
type: this.type,
|
|
453
453
|
data: this.data
|
|
454
|
-
}
|
|
454
|
+
};
|
|
455
455
|
}
|
|
456
456
|
static from(array) {
|
|
457
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);
|
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
|
@@ -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
|
}
|