@freesignal/protocol 0.5.6 → 0.6.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 +5 -1
- package/dist/double-ratchet.js +8 -2
- package/dist/node.d.ts +1 -2
- package/dist/node.js +36 -16
- package/dist/types.d.ts +23 -16
- package/dist/types.js +74 -85
- package/dist/x3dh.d.ts +0 -2
- package/dist/x3dh.js +3 -5
- package/package.json +1 -1
package/dist/double-ratchet.d.ts
CHANGED
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
* You should have received a copy of the GNU General Public License
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
|
+
import { IdentityKey, UserId } from "./types";
|
|
19
20
|
export interface ExportedKeySession {
|
|
21
|
+
identityKey: string;
|
|
20
22
|
secretKey: string;
|
|
21
23
|
rootKey?: string;
|
|
22
24
|
sendingChain?: ExportedKeyChain;
|
|
@@ -41,18 +43,20 @@ export declare class KeySession {
|
|
|
41
43
|
static readonly info: string;
|
|
42
44
|
static readonly maxCount = 65536;
|
|
43
45
|
readonly id: string;
|
|
46
|
+
readonly identityKey: IdentityKey;
|
|
44
47
|
private keyPair;
|
|
45
48
|
private rootKey?;
|
|
46
49
|
private sendingChain?;
|
|
47
50
|
private receivingChain?;
|
|
48
51
|
private nextHeaderKey?;
|
|
49
52
|
private previousKeys;
|
|
50
|
-
constructor(opts?: {
|
|
53
|
+
constructor(identityKey: IdentityKey, opts?: {
|
|
51
54
|
id?: string;
|
|
52
55
|
secretKey?: Uint8Array;
|
|
53
56
|
remoteKey?: Uint8Array;
|
|
54
57
|
rootKey?: Uint8Array;
|
|
55
58
|
});
|
|
59
|
+
get userId(): UserId;
|
|
56
60
|
private getChain;
|
|
57
61
|
getHeaderKeys(): {
|
|
58
62
|
readonly sending?: Uint8Array;
|
package/dist/double-ratchet.js
CHANGED
|
@@ -24,15 +24,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
24
24
|
exports.KeySession = void 0;
|
|
25
25
|
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
26
26
|
const utils_1 = require("@freesignal/utils");
|
|
27
|
+
const types_1 = require("./types");
|
|
27
28
|
/**
|
|
28
29
|
* Represents a secure Double Ratchet session.
|
|
29
30
|
* Used for forward-secure encryption and decryption of messages.
|
|
30
31
|
*/
|
|
31
32
|
class KeySession {
|
|
32
33
|
//headerKey?: Uint8Array, nextHeaderKey?: Uint8Array,
|
|
33
|
-
constructor(opts = {}) {
|
|
34
|
+
constructor(identityKey, opts = {}) {
|
|
34
35
|
var _a;
|
|
35
36
|
this.previousKeys = new KeyMap();
|
|
37
|
+
this.identityKey = identityKey;
|
|
36
38
|
this.id = (_a = opts.id) !== null && _a !== void 0 ? _a : crypto_1.default.UUID.generate().toString();
|
|
37
39
|
this.keyPair = crypto_1.default.ECDH.keyPair(opts.secretKey);
|
|
38
40
|
if (opts.rootKey)
|
|
@@ -43,6 +45,9 @@ class KeySession {
|
|
|
43
45
|
this.sendingChain = this.getChain(opts.remoteKey); //, opts.headerKey);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
48
|
+
get userId() {
|
|
49
|
+
return this.identityKey.userId;
|
|
50
|
+
}
|
|
46
51
|
getChain(remoteKey, headerKey, previousCount) {
|
|
47
52
|
const sharedKey = crypto_1.default.ECDH.scalarMult(this.keyPair.secretKey, remoteKey);
|
|
48
53
|
if (!this.rootKey)
|
|
@@ -106,6 +111,7 @@ class KeySession {
|
|
|
106
111
|
toJSON() {
|
|
107
112
|
var _a, _b;
|
|
108
113
|
return {
|
|
114
|
+
identityKey: this.identityKey.toString(),
|
|
109
115
|
secretKey: (0, utils_1.decodeBase64)(this.keyPair.secretKey),
|
|
110
116
|
rootKey: this.rootKey ? (0, utils_1.decodeBase64)(this.rootKey) : undefined,
|
|
111
117
|
sendingChain: (_a = this.sendingChain) === null || _a === void 0 ? void 0 : _a.toJSON(),
|
|
@@ -120,7 +126,7 @@ class KeySession {
|
|
|
120
126
|
* @returns session with the state parsed.
|
|
121
127
|
*/
|
|
122
128
|
static from(data) {
|
|
123
|
-
const session = new KeySession({ secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: data.rootKey ? (0, utils_1.encodeBase64)(data.rootKey) : undefined });
|
|
129
|
+
const session = new KeySession(types_1.IdentityKey.from(data.identityKey), { secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: data.rootKey ? (0, utils_1.encodeBase64)(data.rootKey) : undefined });
|
|
124
130
|
session.sendingChain = data.sendingChain ? KeyChain.from(data.sendingChain) : undefined;
|
|
125
131
|
session.receivingChain = data.receivingChain ? KeyChain.from(data.receivingChain) : undefined;
|
|
126
132
|
session.previousKeys = new KeyMap(data.previousKeys);
|
package/dist/node.d.ts
CHANGED
|
@@ -33,7 +33,6 @@ export declare class BootstrapRequest {
|
|
|
33
33
|
export declare class FreeSignalNode {
|
|
34
34
|
protected readonly privateIdentityKey: PrivateIdentityKey;
|
|
35
35
|
protected readonly sessions: SessionMap;
|
|
36
|
-
protected readonly users: LocalStorage<string, IdentityKey>;
|
|
37
36
|
protected readonly bundles: LocalStorage<string, KeyExchangeDataBundle>;
|
|
38
37
|
protected readonly keyExchange: KeyExchange;
|
|
39
38
|
protected readonly discovers: Set<string>;
|
|
@@ -41,7 +40,6 @@ export declare class FreeSignalNode {
|
|
|
41
40
|
constructor(storage: Database<{
|
|
42
41
|
sessions: LocalStorage<string, ExportedKeySession>;
|
|
43
42
|
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
44
|
-
users: LocalStorage<string, IdentityKey>;
|
|
45
43
|
bundles: LocalStorage<string, KeyExchangeDataBundle>;
|
|
46
44
|
bootstraps: LocalStorage<string, BootstrapRequest>;
|
|
47
45
|
}>, privateIdentityKey?: PrivateIdentityKey);
|
|
@@ -51,6 +49,7 @@ export declare class FreeSignalNode {
|
|
|
51
49
|
getRequest(userId: string): Promise<KeyExchangeData | undefined>;
|
|
52
50
|
protected encrypt(receiverId: string | UserId, protocol: Protocols, data: Uint8Array): Promise<Datagram>;
|
|
53
51
|
packHandshake(data: KeyExchangeData): Promise<Datagram>;
|
|
52
|
+
packHandshake(receiverId: string | UserId): Promise<Datagram>;
|
|
54
53
|
packData<T>(receiverId: string | UserId, data: T): Promise<Datagram>;
|
|
55
54
|
packRelay(receiverId: string | UserId, data: Datagram): Promise<Datagram>;
|
|
56
55
|
packDiscover(receiverId: string | UserId, discoverId: string | UserId): Promise<Datagram>;
|
package/dist/node.js
CHANGED
|
@@ -37,6 +37,9 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
37
37
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
38
38
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
39
39
|
};
|
|
40
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
40
43
|
var _BootstrapRequest_status;
|
|
41
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
45
|
exports.FreeSignalNode = exports.BootstrapRequest = void 0;
|
|
@@ -45,6 +48,7 @@ const x3dh_1 = require("./x3dh");
|
|
|
45
48
|
const double_ratchet_1 = require("./double-ratchet");
|
|
46
49
|
const _1 = require(".");
|
|
47
50
|
const utils_1 = require("@freesignal/utils");
|
|
51
|
+
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
48
52
|
class BootstrapRequest {
|
|
49
53
|
constructor(senderId, data) {
|
|
50
54
|
this.senderId = senderId;
|
|
@@ -81,7 +85,6 @@ class FreeSignalNode {
|
|
|
81
85
|
this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : (0, _1.createIdentity)();
|
|
82
86
|
this.sessions = new SessionMap(storage.sessions);
|
|
83
87
|
this.keyExchange = new x3dh_1.KeyExchange(storage.keyExchange, this.privateIdentityKey);
|
|
84
|
-
this.users = storage.users;
|
|
85
88
|
this.bundles = storage.bundles;
|
|
86
89
|
this.bootstraps = storage.bootstraps;
|
|
87
90
|
}
|
|
@@ -111,10 +114,16 @@ class FreeSignalNode {
|
|
|
111
114
|
}
|
|
112
115
|
packHandshake(data) {
|
|
113
116
|
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
var _a;
|
|
118
|
+
if (typeof data === 'string' || data instanceof types_1.UserId) {
|
|
119
|
+
const userId = data.toString();
|
|
120
|
+
const identityKey = (_a = (yield this.sessions.get(userId))) === null || _a === void 0 ? void 0 : _a.identityKey;
|
|
121
|
+
if (!identityKey)
|
|
122
|
+
throw new Error("Missing user");
|
|
123
|
+
return yield this.encrypt(userId, types_1.Protocols.HANDSHAKE, crypto_1.default.ECDH.scalarMult(identityKey.exchangeKey, this.privateIdentityKey.exchangeKey));
|
|
124
|
+
}
|
|
125
|
+
const { session, message } = yield this.keyExchange.digestData(data, (0, utils_1.encodeData)(yield this.keyExchange.generateBundle()));
|
|
126
|
+
yield this.sessions.set(session.userId.toString(), session);
|
|
118
127
|
return new types_1.Datagram(this.userId.toString(), types_1.UserId.fromKey(data.identityKey).toString(), types_1.Protocols.HANDSHAKE, (0, utils_1.encodeData)(message)).sign(this.privateIdentityKey.signatureKey);
|
|
119
128
|
});
|
|
120
129
|
}
|
|
@@ -145,10 +154,11 @@ class FreeSignalNode {
|
|
|
145
154
|
}
|
|
146
155
|
decrypt(datagram) {
|
|
147
156
|
return __awaiter(this, void 0, void 0, function* () {
|
|
148
|
-
|
|
149
|
-
|
|
157
|
+
var _a;
|
|
158
|
+
const identityKey = (_a = (yield this.sessions.get(datagram.sender))) === null || _a === void 0 ? void 0 : _a.identityKey;
|
|
159
|
+
if (!identityKey)
|
|
150
160
|
throw new Error("User IdentityKey not found");
|
|
151
|
-
if (!types_1.Datagram.verify(datagram,
|
|
161
|
+
if (!types_1.Datagram.verify(datagram, identityKey.signatureKey))
|
|
152
162
|
throw new Error("Signature not verified");
|
|
153
163
|
const session = yield this.sessions.get(datagram.sender);
|
|
154
164
|
if (!session)
|
|
@@ -168,7 +178,7 @@ class FreeSignalNode {
|
|
|
168
178
|
*/
|
|
169
179
|
open(datagram) {
|
|
170
180
|
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
-
var _a;
|
|
181
|
+
var _a, _b;
|
|
172
182
|
if (datagram instanceof Uint8Array)
|
|
173
183
|
datagram = types_1.Datagram.from(datagram);
|
|
174
184
|
let out = {
|
|
@@ -178,14 +188,24 @@ class FreeSignalNode {
|
|
|
178
188
|
case types_1.Protocols.HANDSHAKE:
|
|
179
189
|
if (!datagram.payload)
|
|
180
190
|
throw new Error("Missing payload");
|
|
191
|
+
if (yield this.sessions.has(datagram.sender)) {
|
|
192
|
+
const payload = yield this.decrypt(datagram);
|
|
193
|
+
const identityKey = (_a = (yield this.sessions.get(datagram.sender))) === null || _a === void 0 ? void 0 : _a.identityKey;
|
|
194
|
+
if (!identityKey)
|
|
195
|
+
throw new Error("Missing user");
|
|
196
|
+
if (!(0, utils_1.compareBytes)(payload, crypto_1.default.ECDH.scalarMult(identityKey.exchangeKey, this.privateIdentityKey.exchangeKey)))
|
|
197
|
+
throw new Error("Error validating handshake data");
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
181
200
|
const data = (0, utils_1.decodeData)(datagram.payload);
|
|
182
201
|
if (!types_1.Datagram.verify(datagram, types_1.IdentityKey.from(data.identityKey).signatureKey))
|
|
183
202
|
throw new Error("Signature not verified");
|
|
184
|
-
const { session,
|
|
185
|
-
|
|
186
|
-
yield this.
|
|
187
|
-
yield this.
|
|
188
|
-
|
|
203
|
+
const { session, associatedData } = yield this.keyExchange.digestMessage(data);
|
|
204
|
+
yield this.sessions.set(session.userId.toString(), session);
|
|
205
|
+
yield this.bundles.set(session.userId.toString(), (0, utils_1.decodeData)(associatedData));
|
|
206
|
+
out.datagram = yield this.packHandshake(session.userId);
|
|
207
|
+
if (!out.datagram)
|
|
208
|
+
throw new Error("Error during handshake");
|
|
189
209
|
return out;
|
|
190
210
|
case types_1.Protocols.MESSAGE:
|
|
191
211
|
out.payload = yield this.decrypt(datagram);
|
|
@@ -195,7 +215,7 @@ class FreeSignalNode {
|
|
|
195
215
|
return out;
|
|
196
216
|
case types_1.Protocols.DISCOVER:
|
|
197
217
|
const message = (0, utils_1.decodeData)(yield this.decrypt(datagram));
|
|
198
|
-
if (message.type === types_1.DiscoverType.REQUEST && message.discoverId && !(yield this.
|
|
218
|
+
if (message.type === types_1.DiscoverType.REQUEST && message.discoverId && !(yield this.sessions.has(message.discoverId))) {
|
|
199
219
|
let data;
|
|
200
220
|
if (message.discoverId === this.userId.toString()) {
|
|
201
221
|
data = yield this.keyExchange.generateData();
|
|
@@ -237,7 +257,7 @@ class FreeSignalNode {
|
|
|
237
257
|
this.onRequest(request);
|
|
238
258
|
}
|
|
239
259
|
;
|
|
240
|
-
const bootstrap = yield ((
|
|
260
|
+
const bootstrap = yield ((_b = (yield this.bootstraps.get(datagram.sender))) === null || _b === void 0 ? void 0 : _b.get());
|
|
241
261
|
if (bootstrap)
|
|
242
262
|
out.datagram = yield this.packHandshake(bootstrap);
|
|
243
263
|
return out;
|
package/dist/types.d.ts
CHANGED
|
@@ -29,30 +29,37 @@ export declare class UserId implements Encodable {
|
|
|
29
29
|
static fromKey(identityKey: string | Uint8Array | IdentityKey): UserId;
|
|
30
30
|
static from(userId: string | Uint8Array | UserId): UserId;
|
|
31
31
|
}
|
|
32
|
-
export
|
|
32
|
+
export declare class IdentityKey implements Encodable {
|
|
33
|
+
static readonly keyLength: number;
|
|
34
|
+
static readonly version = 1;
|
|
35
|
+
private static readonly info;
|
|
33
36
|
readonly info: number;
|
|
34
37
|
readonly signatureKey: Uint8Array;
|
|
35
38
|
readonly exchangeKey: Uint8Array;
|
|
39
|
+
constructor(identityKey: IdentityKey | Uint8Array | string);
|
|
40
|
+
get userId(): UserId;
|
|
41
|
+
toBytes(): Uint8Array;
|
|
42
|
+
toString(): string;
|
|
43
|
+
toJSON(): string;
|
|
44
|
+
static from(identityKey: IdentityKey | Uint8Array | string): IdentityKey;
|
|
45
|
+
static from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): IdentityKey;
|
|
36
46
|
}
|
|
37
|
-
export declare
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
function from(identityKey: IdentityKey | Uint8Array | string): IdentityKey;
|
|
42
|
-
function from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): IdentityKey;
|
|
43
|
-
}
|
|
44
|
-
export interface PrivateIdentityKey {
|
|
47
|
+
export declare class PrivateIdentityKey implements Encodable {
|
|
48
|
+
static readonly keyLength: number;
|
|
49
|
+
static readonly version = 1;
|
|
50
|
+
private static readonly info;
|
|
45
51
|
readonly info: number;
|
|
46
52
|
readonly signatureKey: Uint8Array;
|
|
47
53
|
readonly exchangeKey: Uint8Array;
|
|
48
54
|
readonly identityKey: IdentityKey;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
constructor(privateIdentityKey: PrivateIdentityKey | Uint8Array | string);
|
|
56
|
+
get userId(): string;
|
|
57
|
+
toBytes(): Uint8Array;
|
|
58
|
+
toString(): string;
|
|
59
|
+
toJSON(): string;
|
|
60
|
+
static isIdentityKeys(obj: any): boolean;
|
|
61
|
+
static from(identityKey: PrivateIdentityKey | Uint8Array | string): PrivateIdentityKey;
|
|
62
|
+
static from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): PrivateIdentityKey;
|
|
56
63
|
}
|
|
57
64
|
export declare enum DiscoverType {
|
|
58
65
|
REQUEST = 0,
|
package/dist/types.js
CHANGED
|
@@ -86,8 +86,8 @@ class UserId {
|
|
|
86
86
|
static fromKey(identityKey) {
|
|
87
87
|
if (typeof identityKey === 'string')
|
|
88
88
|
identityKey = (0, utils_1.encodeBase64)(identityKey);
|
|
89
|
-
else if (IdentityKey
|
|
90
|
-
identityKey = identityKey.toBytes();
|
|
89
|
+
else if (identityKey instanceof IdentityKey)
|
|
90
|
+
identityKey = (identityKey).toBytes();
|
|
91
91
|
return new UserId(crypto_1.default.hkdf(identityKey, new Uint8Array(32).fill(0), "/freesignal/userid"));
|
|
92
92
|
}
|
|
93
93
|
static from(userId) {
|
|
@@ -97,112 +97,101 @@ class UserId {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
exports.UserId = UserId;
|
|
100
|
-
|
|
101
|
-
(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
constructor(identityKey) {
|
|
107
|
-
if (identityKey instanceof IdentityKeyConstructor) {
|
|
108
|
-
this.info = identityKey.info;
|
|
109
|
-
this.signatureKey = identityKey.signatureKey;
|
|
110
|
-
this.exchangeKey = identityKey.exchangeKey;
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
if (typeof identityKey === 'string')
|
|
114
|
-
identityKey = (0, utils_1.encodeBase64)(identityKey);
|
|
115
|
-
if (!isIdentityKeys(identityKey))
|
|
116
|
-
throw new Error("Invalid key length");
|
|
117
|
-
this.info = identityKey[0];
|
|
118
|
-
this.signatureKey = identityKey.subarray(1, crypto_1.default.EdDSA.publicKeyLength + 1);
|
|
119
|
-
this.exchangeKey = identityKey.subarray(crypto_1.default.EdDSA.publicKeyLength + 1, IdentityKey.keyLength);
|
|
120
|
-
}
|
|
100
|
+
class IdentityKey {
|
|
101
|
+
constructor(identityKey) {
|
|
102
|
+
if (identityKey instanceof IdentityKey) {
|
|
103
|
+
this.info = identityKey.info;
|
|
104
|
+
this.signatureKey = identityKey.signatureKey;
|
|
105
|
+
this.exchangeKey = identityKey.exchangeKey;
|
|
121
106
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
toJSON() {
|
|
132
|
-
return this.toString();
|
|
107
|
+
else {
|
|
108
|
+
if (typeof identityKey === 'string')
|
|
109
|
+
identityKey = (0, utils_1.encodeBase64)(identityKey);
|
|
110
|
+
if (identityKey.length !== IdentityKey.keyLength)
|
|
111
|
+
throw new Error("Invalid key length");
|
|
112
|
+
this.info = identityKey[0];
|
|
113
|
+
this.signatureKey = identityKey.subarray(1, crypto_1.default.EdDSA.publicKeyLength + 1);
|
|
114
|
+
this.exchangeKey = identityKey.subarray(crypto_1.default.EdDSA.publicKeyLength + 1, IdentityKey.keyLength);
|
|
133
115
|
}
|
|
134
116
|
}
|
|
135
|
-
|
|
136
|
-
return (
|
|
117
|
+
get userId() {
|
|
118
|
+
return UserId.fromKey(this.toBytes());
|
|
137
119
|
}
|
|
138
|
-
|
|
139
|
-
|
|
120
|
+
toBytes() {
|
|
121
|
+
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
122
|
+
}
|
|
123
|
+
toString() {
|
|
124
|
+
return (0, utils_1.decodeBase64)(this.toBytes());
|
|
125
|
+
}
|
|
126
|
+
toJSON() {
|
|
127
|
+
return this.toString();
|
|
128
|
+
}
|
|
129
|
+
static from(...keys) {
|
|
140
130
|
keys = keys.map(key => {
|
|
141
|
-
if (key instanceof
|
|
131
|
+
if (key instanceof IdentityKey)
|
|
142
132
|
return key.toBytes();
|
|
143
133
|
else if (typeof key === 'string')
|
|
144
134
|
return (0, utils_1.encodeBase64)(key);
|
|
145
135
|
else
|
|
146
136
|
return key;
|
|
147
137
|
});
|
|
148
|
-
return new
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
this.exchangeKey = privateIdentityKey.exchangeKey;
|
|
163
|
-
this.identityKey = privateIdentityKey.identityKey;
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
if (typeof privateIdentityKey === 'string')
|
|
167
|
-
privateIdentityKey = (0, utils_1.encodeBase64)(privateIdentityKey);
|
|
168
|
-
if (!isIdentityKeys(privateIdentityKey))
|
|
169
|
-
throw new Error("Invalid key length");
|
|
170
|
-
this.info = privateIdentityKey[0];
|
|
171
|
-
this.signatureKey = privateIdentityKey.subarray(1, crypto_1.default.EdDSA.secretKeyLength + 1);
|
|
172
|
-
this.exchangeKey = privateIdentityKey.subarray(crypto_1.default.EdDSA.secretKeyLength + 1, PrivateIdentityKey.keyLength);
|
|
173
|
-
this.identityKey = IdentityKey.from(crypto_1.default.EdDSA.keyPair(this.signatureKey).publicKey, crypto_1.default.ECDH.keyPair(this.exchangeKey).publicKey);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
get userId() {
|
|
177
|
-
return UserId.fromKey(this.identityKey.toBytes()).toString();
|
|
178
|
-
}
|
|
179
|
-
toBytes() {
|
|
180
|
-
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
181
|
-
}
|
|
182
|
-
toString() {
|
|
183
|
-
return (0, utils_1.decodeBase64)(this.toBytes());
|
|
138
|
+
return new IdentityKey(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(IdentityKey.info + IdentityKey.version, 1), ...keys) : keys[0]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.IdentityKey = IdentityKey;
|
|
142
|
+
IdentityKey.keyLength = crypto_1.default.EdDSA.publicKeyLength + crypto_1.default.ECDH.publicKeyLength + 1;
|
|
143
|
+
IdentityKey.version = 1;
|
|
144
|
+
IdentityKey.info = 0x70;
|
|
145
|
+
class PrivateIdentityKey {
|
|
146
|
+
constructor(privateIdentityKey) {
|
|
147
|
+
if (privateIdentityKey instanceof PrivateIdentityKey) {
|
|
148
|
+
this.info = privateIdentityKey.info;
|
|
149
|
+
this.signatureKey = privateIdentityKey.signatureKey;
|
|
150
|
+
this.exchangeKey = privateIdentityKey.exchangeKey;
|
|
151
|
+
this.identityKey = privateIdentityKey.identityKey;
|
|
184
152
|
}
|
|
185
|
-
|
|
186
|
-
|
|
153
|
+
else {
|
|
154
|
+
if (typeof privateIdentityKey === 'string')
|
|
155
|
+
privateIdentityKey = (0, utils_1.encodeBase64)(privateIdentityKey);
|
|
156
|
+
if (!PrivateIdentityKey.isIdentityKeys(privateIdentityKey))
|
|
157
|
+
throw new Error("Invalid key length");
|
|
158
|
+
this.info = privateIdentityKey[0];
|
|
159
|
+
this.signatureKey = privateIdentityKey.subarray(1, crypto_1.default.EdDSA.secretKeyLength + 1);
|
|
160
|
+
this.exchangeKey = privateIdentityKey.subarray(crypto_1.default.EdDSA.secretKeyLength + 1, PrivateIdentityKey.keyLength);
|
|
161
|
+
this.identityKey = IdentityKey.from(crypto_1.default.EdDSA.keyPair(this.signatureKey).publicKey, crypto_1.default.ECDH.keyPair(this.exchangeKey).publicKey);
|
|
187
162
|
}
|
|
188
163
|
}
|
|
189
|
-
|
|
190
|
-
return (
|
|
164
|
+
get userId() {
|
|
165
|
+
return UserId.fromKey(this.identityKey.toBytes()).toString();
|
|
191
166
|
}
|
|
192
|
-
|
|
193
|
-
|
|
167
|
+
toBytes() {
|
|
168
|
+
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
169
|
+
}
|
|
170
|
+
toString() {
|
|
171
|
+
return (0, utils_1.decodeBase64)(this.toBytes());
|
|
172
|
+
}
|
|
173
|
+
toJSON() {
|
|
174
|
+
return this.toString();
|
|
175
|
+
}
|
|
176
|
+
static isIdentityKeys(obj) {
|
|
177
|
+
return (obj instanceof Uint8Array && obj.length === PrivateIdentityKey.keyLength) || obj instanceof PrivateIdentityKey;
|
|
178
|
+
}
|
|
179
|
+
static from(...keys) {
|
|
194
180
|
keys = keys.map(key => {
|
|
195
|
-
if (key instanceof
|
|
181
|
+
if (key instanceof PrivateIdentityKey)
|
|
196
182
|
return key.toBytes();
|
|
197
183
|
else if (typeof key === 'string')
|
|
198
184
|
return (0, utils_1.encodeBase64)(key);
|
|
199
185
|
else
|
|
200
186
|
return key;
|
|
201
187
|
});
|
|
202
|
-
return new
|
|
188
|
+
return new PrivateIdentityKey(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(PrivateIdentityKey.info + PrivateIdentityKey.version, 1), ...keys) : keys[0]);
|
|
203
189
|
}
|
|
204
|
-
|
|
205
|
-
|
|
190
|
+
}
|
|
191
|
+
exports.PrivateIdentityKey = PrivateIdentityKey;
|
|
192
|
+
PrivateIdentityKey.keyLength = crypto_1.default.EdDSA.secretKeyLength + crypto_1.default.ECDH.secretKeyLength + 1;
|
|
193
|
+
PrivateIdentityKey.version = 1;
|
|
194
|
+
PrivateIdentityKey.info = 0x4E;
|
|
206
195
|
var DiscoverType;
|
|
207
196
|
(function (DiscoverType) {
|
|
208
197
|
DiscoverType[DiscoverType["REQUEST"] = 0] = "REQUEST";
|
package/dist/x3dh.d.ts
CHANGED
|
@@ -38,11 +38,9 @@ export declare class KeyExchange {
|
|
|
38
38
|
digestData(message: KeyExchangeData, associatedData?: Uint8Array): Promise<{
|
|
39
39
|
session: KeySession;
|
|
40
40
|
message: KeyExchangeSynMessage;
|
|
41
|
-
identityKey: IdentityKey;
|
|
42
41
|
}>;
|
|
43
42
|
digestMessage(message: KeyExchangeSynMessage): Promise<{
|
|
44
43
|
session: KeySession;
|
|
45
|
-
identityKey: IdentityKey;
|
|
46
44
|
associatedData: Uint8Array;
|
|
47
45
|
}>;
|
|
48
46
|
}
|
package/dist/x3dh.js
CHANGED
|
@@ -99,7 +99,7 @@ class KeyExchange {
|
|
|
99
99
|
...onetimePreKey ? crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
|
|
100
100
|
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 2);
|
|
101
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
|
+
const session = new double_ratchet_1.KeySession(identityKey, { remoteKey: identityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
|
|
103
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()));
|
|
104
104
|
if (!encrypted)
|
|
105
105
|
throw new Error("Decryption error");
|
|
@@ -112,8 +112,7 @@ class KeyExchange {
|
|
|
112
112
|
signedPreKeyHash: (0, utils_1.decodeBase64)(signedPreKeyHash),
|
|
113
113
|
onetimePreKeyHash: (0, utils_1.decodeBase64)(onetimePreKeyHash),
|
|
114
114
|
associatedData: (0, utils_1.decodeBase64)(encrypted.toBytes())
|
|
115
|
-
}
|
|
116
|
-
identityKey
|
|
115
|
+
}
|
|
117
116
|
};
|
|
118
117
|
});
|
|
119
118
|
}
|
|
@@ -135,7 +134,7 @@ class KeyExchange {
|
|
|
135
134
|
...onetimePreKey ? crypto_1.default.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
|
|
136
135
|
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 2);
|
|
137
136
|
//, 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
|
+
const session = new double_ratchet_1.KeySession(identityKey, { secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
|
|
139
138
|
const data = (0, types_1.decryptData)(session, (0, utils_1.encodeBase64)(message.associatedData));
|
|
140
139
|
if (!data)
|
|
141
140
|
throw new Error("Error decrypting ACK message");
|
|
@@ -143,7 +142,6 @@ class KeyExchange {
|
|
|
143
142
|
throw new Error("Error verifing Associated Data");
|
|
144
143
|
return {
|
|
145
144
|
session,
|
|
146
|
-
identityKey,
|
|
147
145
|
associatedData: data.subarray(64)
|
|
148
146
|
};
|
|
149
147
|
});
|