@freesignal/protocol 0.5.6 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/double-ratchet.d.ts +6 -9
- package/dist/double-ratchet.js +36 -26
- package/dist/node.d.ts +7 -7
- package/dist/node.js +61 -22
- package/dist/test.js +7 -5
- package/dist/types.d.ts +23 -16
- package/dist/types.js +120 -97
- package/dist/x3dh.d.ts +0 -2
- package/dist/x3dh.js +9 -9
- 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;
|
|
@@ -40,24 +42,21 @@ export declare class KeySession {
|
|
|
40
42
|
static readonly version = 1;
|
|
41
43
|
static readonly info: string;
|
|
42
44
|
static readonly maxCount = 65536;
|
|
43
|
-
readonly
|
|
45
|
+
readonly identityKey: IdentityKey;
|
|
44
46
|
private keyPair;
|
|
45
47
|
private rootKey?;
|
|
46
48
|
private sendingChain?;
|
|
47
49
|
private receivingChain?;
|
|
48
50
|
private nextHeaderKey?;
|
|
49
51
|
private previousKeys;
|
|
50
|
-
constructor(
|
|
51
|
-
|
|
52
|
+
constructor({ identityKey, secretKey, remoteKey, rootKey }: {
|
|
53
|
+
identityKey: IdentityKey;
|
|
52
54
|
secretKey?: Uint8Array;
|
|
53
55
|
remoteKey?: Uint8Array;
|
|
54
56
|
rootKey?: Uint8Array;
|
|
55
57
|
});
|
|
58
|
+
get userId(): UserId;
|
|
56
59
|
private getChain;
|
|
57
|
-
getHeaderKeys(): {
|
|
58
|
-
readonly sending?: Uint8Array;
|
|
59
|
-
readonly receiving?: Uint8Array;
|
|
60
|
-
};
|
|
61
60
|
getSendingKey(): PrivateEncryptionKeys | undefined;
|
|
62
61
|
getReceivingKey(encryptionKeys: EncryptionKeys): Uint8Array | undefined;
|
|
63
62
|
/**
|
|
@@ -84,8 +83,6 @@ interface ExportedKeyChain {
|
|
|
84
83
|
publicKey: string;
|
|
85
84
|
remoteKey: string;
|
|
86
85
|
chainKey: string;
|
|
87
|
-
nextHeaderKey: string;
|
|
88
|
-
headerKey?: string;
|
|
89
86
|
count: number;
|
|
90
87
|
previousCount: number;
|
|
91
88
|
}
|
package/dist/double-ratchet.js
CHANGED
|
@@ -24,40 +24,47 @@ 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(
|
|
34
|
-
var _a;
|
|
34
|
+
constructor({ identityKey, secretKey, remoteKey, rootKey }) {
|
|
35
35
|
this.previousKeys = new KeyMap();
|
|
36
|
-
this.
|
|
37
|
-
this.keyPair = crypto_1.default.ECDH.keyPair(
|
|
38
|
-
if (
|
|
39
|
-
this.rootKey =
|
|
36
|
+
this.identityKey = identityKey;
|
|
37
|
+
this.keyPair = crypto_1.default.ECDH.keyPair(secretKey);
|
|
38
|
+
if (rootKey)
|
|
39
|
+
this.rootKey = rootKey;
|
|
40
40
|
//if (opts.nextHeaderKey)
|
|
41
41
|
// this.nextHeaderKey = opts.nextHeaderKey;
|
|
42
|
-
if (
|
|
43
|
-
this.sendingChain = this.getChain(
|
|
42
|
+
if (remoteKey) {
|
|
43
|
+
this.sendingChain = this.getChain(remoteKey); //, opts.headerKey);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
get userId() {
|
|
47
|
+
return this.identityKey.userId;
|
|
48
|
+
}
|
|
49
|
+
//headerKey?: Uint8Array,
|
|
50
|
+
getChain(remoteKey, previousCount) {
|
|
47
51
|
const sharedKey = crypto_1.default.ECDH.scalarMult(this.keyPair.secretKey, remoteKey);
|
|
48
52
|
if (!this.rootKey)
|
|
49
53
|
this.rootKey = crypto_1.default.hash(sharedKey);
|
|
50
54
|
const hashkey = crypto_1.default.hkdf(sharedKey, this.rootKey, KeySession.info, KeySession.keyLength * 3);
|
|
51
55
|
this.rootKey = hashkey.subarray(0, KeySession.keyLength);
|
|
52
|
-
|
|
56
|
+
//hashkey.subarray(KeySession.keyLength * 2), headerKey,
|
|
57
|
+
return new KeyChain(this.publicKey, remoteKey, hashkey.subarray(KeySession.keyLength, KeySession.keyLength * 2), previousCount);
|
|
53
58
|
}
|
|
54
|
-
getHeaderKeys() {
|
|
55
|
-
|
|
59
|
+
/*public getHeaderKeys(): {
|
|
60
|
+
readonly sending?: Uint8Array,
|
|
61
|
+
readonly receiving?: Uint8Array
|
|
62
|
+
} {
|
|
56
63
|
return {
|
|
57
|
-
sending:
|
|
58
|
-
receiving: (
|
|
59
|
-
}
|
|
60
|
-
}
|
|
64
|
+
sending: this.sendingChain?.headerKey,
|
|
65
|
+
receiving: (this.receivingChain?.headerKey ?? this.receivingChain?.nextHeaderKey) ?? this.nextHeaderKey
|
|
66
|
+
}
|
|
67
|
+
}*/
|
|
61
68
|
getSendingKey() {
|
|
62
69
|
if (!this.sendingChain)
|
|
63
70
|
return;
|
|
@@ -71,17 +78,19 @@ class KeySession {
|
|
|
71
78
|
};
|
|
72
79
|
}
|
|
73
80
|
getReceivingKey(encryptionKeys) {
|
|
74
|
-
var _a, _b, _c, _d
|
|
81
|
+
var _a, _b, _c, _d;
|
|
75
82
|
if (!this.previousKeys.has((0, utils_1.decodeBase64)(encryptionKeys.publicKey) + encryptionKeys.count.toString())) {
|
|
76
83
|
if (!(0, utils_1.compareBytes)(encryptionKeys.publicKey, (_b = (_a = this.receivingChain) === null || _a === void 0 ? void 0 : _a.remoteKey) !== null && _b !== void 0 ? _b : new Uint8Array())) {
|
|
77
84
|
while (this.receivingChain && this.receivingChain.count < encryptionKeys.previous) {
|
|
78
85
|
const key = this.receivingChain.getKey();
|
|
79
86
|
this.previousKeys.set((0, utils_1.decodeBase64)(this.receivingChain.remoteKey) + this.receivingChain.count.toString(), key);
|
|
80
87
|
}
|
|
81
|
-
this.
|
|
88
|
+
//this.nextHeaderKey ?? this.receivingChain?.nextHeaderKey
|
|
89
|
+
this.receivingChain = this.getChain(encryptionKeys.publicKey, (_c = this.receivingChain) === null || _c === void 0 ? void 0 : _c.count);
|
|
82
90
|
this.nextHeaderKey = undefined;
|
|
83
91
|
this.keyPair = crypto_1.default.ECDH.keyPair();
|
|
84
|
-
this.sendingChain
|
|
92
|
+
//this.sendingChain?.nextHeaderKey,
|
|
93
|
+
this.sendingChain = this.getChain(encryptionKeys.publicKey, (_d = this.sendingChain) === null || _d === void 0 ? void 0 : _d.count);
|
|
85
94
|
}
|
|
86
95
|
if (!this.receivingChain)
|
|
87
96
|
throw new Error("Error initializing receivingChain");
|
|
@@ -106,6 +115,7 @@ class KeySession {
|
|
|
106
115
|
toJSON() {
|
|
107
116
|
var _a, _b;
|
|
108
117
|
return {
|
|
118
|
+
identityKey: this.identityKey.toString(),
|
|
109
119
|
secretKey: (0, utils_1.decodeBase64)(this.keyPair.secretKey),
|
|
110
120
|
rootKey: this.rootKey ? (0, utils_1.decodeBase64)(this.rootKey) : undefined,
|
|
111
121
|
sendingChain: (_a = this.sendingChain) === null || _a === void 0 ? void 0 : _a.toJSON(),
|
|
@@ -120,7 +130,7 @@ class KeySession {
|
|
|
120
130
|
* @returns session with the state parsed.
|
|
121
131
|
*/
|
|
122
132
|
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 });
|
|
133
|
+
const session = new KeySession({ identityKey: types_1.IdentityKey.from(data.identityKey), secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: data.rootKey ? (0, utils_1.encodeBase64)(data.rootKey) : undefined });
|
|
124
134
|
session.sendingChain = data.sendingChain ? KeyChain.from(data.sendingChain) : undefined;
|
|
125
135
|
session.receivingChain = data.receivingChain ? KeyChain.from(data.receivingChain) : undefined;
|
|
126
136
|
session.previousKeys = new KeyMap(data.previousKeys);
|
|
@@ -133,12 +143,11 @@ KeySession.version = 1;
|
|
|
133
143
|
KeySession.info = "/freesignal/double-ratchet/v0." + KeySession.version;
|
|
134
144
|
KeySession.maxCount = 65536;
|
|
135
145
|
class KeyChain {
|
|
136
|
-
|
|
146
|
+
//public readonly nextHeaderKey: Uint8Array, public readonly headerKey?: Uint8Array,
|
|
147
|
+
constructor(publicKey, remoteKey, chainKey, previousCount = 0) {
|
|
137
148
|
this.publicKey = publicKey;
|
|
138
149
|
this.remoteKey = remoteKey;
|
|
139
150
|
this.chainKey = chainKey;
|
|
140
|
-
this.nextHeaderKey = nextHeaderKey;
|
|
141
|
-
this.headerKey = headerKey;
|
|
142
151
|
this.previousCount = previousCount;
|
|
143
152
|
this._count = 0;
|
|
144
153
|
}
|
|
@@ -160,14 +169,15 @@ class KeyChain {
|
|
|
160
169
|
publicKey: (0, utils_1.decodeBase64)(this.publicKey),
|
|
161
170
|
remoteKey: (0, utils_1.decodeBase64)(this.remoteKey),
|
|
162
171
|
chainKey: (0, utils_1.decodeBase64)(this.chainKey),
|
|
163
|
-
nextHeaderKey:
|
|
164
|
-
headerKey: this.headerKey ?
|
|
172
|
+
//nextHeaderKey: decodeBase64(this.nextHeaderKey),
|
|
173
|
+
//headerKey: this.headerKey ? decodeBase64(this.headerKey) : undefined,
|
|
165
174
|
count: this.count,
|
|
166
175
|
previousCount: this.previousCount
|
|
167
176
|
};
|
|
168
177
|
}
|
|
169
178
|
static from(obj) {
|
|
170
|
-
|
|
179
|
+
//encodeBase64(obj.nextHeaderKey), obj.headerKey ? encodeBase64(obj.headerKey) : undefined,
|
|
180
|
+
const chain = new KeyChain((0, utils_1.encodeBase64)(obj.publicKey), (0, utils_1.encodeBase64)(obj.remoteKey), (0, utils_1.encodeBase64)(obj.chainKey), obj.previousCount);
|
|
171
181
|
chain._count = obj.count;
|
|
172
182
|
return chain;
|
|
173
183
|
}
|
package/dist/node.d.ts
CHANGED
|
@@ -23,17 +23,16 @@ import { ExportedKeySession, KeySession } from "./double-ratchet";
|
|
|
23
23
|
export declare class BootstrapRequest {
|
|
24
24
|
#private;
|
|
25
25
|
readonly senderId: UserId | string;
|
|
26
|
-
private readonly
|
|
27
|
-
constructor(senderId: UserId | string,
|
|
26
|
+
private readonly datagram;
|
|
27
|
+
constructor(senderId: UserId | string, datagram: Datagram);
|
|
28
28
|
get status(): "pending" | "accepted" | "denied";
|
|
29
|
-
get(): Promise<
|
|
30
|
-
accept(): Promise<
|
|
29
|
+
get(): Promise<Datagram | undefined>;
|
|
30
|
+
accept(): Promise<Datagram | undefined>;
|
|
31
31
|
deny(): void;
|
|
32
32
|
}
|
|
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,20 +40,21 @@ 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);
|
|
48
46
|
get identityKey(): IdentityKey;
|
|
49
47
|
get userId(): UserId;
|
|
50
48
|
onRequest: (request: BootstrapRequest) => void;
|
|
51
|
-
getRequest(userId: string): Promise<
|
|
49
|
+
getRequest(userId: string): Promise<Datagram | 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>;
|
|
57
56
|
packBootstrap(receiverId: string | UserId): Promise<Datagram>;
|
|
57
|
+
packGetBootstrap(receiverId: string | UserId): Promise<Datagram>;
|
|
58
58
|
protected decrypt(datagram: Datagram): Promise<Uint8Array>;
|
|
59
59
|
/**
|
|
60
60
|
* Open the datagram and execute operation of Discover and Handshake.
|
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,10 +48,11 @@ 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
|
-
constructor(senderId,
|
|
53
|
+
constructor(senderId, datagram) {
|
|
50
54
|
this.senderId = senderId;
|
|
51
|
-
this.
|
|
55
|
+
this.datagram = datagram;
|
|
52
56
|
_BootstrapRequest_status.set(this, 'pending');
|
|
53
57
|
}
|
|
54
58
|
get status() {
|
|
@@ -56,7 +60,7 @@ class BootstrapRequest {
|
|
|
56
60
|
}
|
|
57
61
|
get() {
|
|
58
62
|
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
-
return __classPrivateFieldGet(this, _BootstrapRequest_status, "f") === 'accepted' ? this.
|
|
63
|
+
return __classPrivateFieldGet(this, _BootstrapRequest_status, "f") === 'accepted' ? this.datagram : undefined;
|
|
60
64
|
});
|
|
61
65
|
}
|
|
62
66
|
accept() {
|
|
@@ -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,21 +114,33 @@ 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
|
+
//console.debug("Packing Handshake Ack");
|
|
120
|
+
const userId = data.toString();
|
|
121
|
+
const identityKey = (_a = (yield this.sessions.get(userId))) === null || _a === void 0 ? void 0 : _a.identityKey;
|
|
122
|
+
if (!identityKey)
|
|
123
|
+
throw new Error("Missing user");
|
|
124
|
+
const res = yield this.encrypt(userId, types_1.Protocols.HANDSHAKE, crypto_1.default.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, identityKey.exchangeKey));
|
|
125
|
+
return res;
|
|
126
|
+
}
|
|
127
|
+
//console.debug("Packing Handshake Syn");
|
|
128
|
+
const { session, message } = yield this.keyExchange.digestData(data, (0, utils_1.encodeData)(yield this.keyExchange.generateBundle()));
|
|
129
|
+
yield this.sessions.set(session.userId.toString(), session);
|
|
118
130
|
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
131
|
});
|
|
120
132
|
}
|
|
121
133
|
packData(receiverId, data) {
|
|
134
|
+
//console.debug("Packing Data");
|
|
122
135
|
return this.encrypt(receiverId, types_1.Protocols.MESSAGE, (0, utils_1.encodeData)(data));
|
|
123
136
|
}
|
|
124
137
|
packRelay(receiverId, data) {
|
|
138
|
+
//console.debug("Packing Relay");
|
|
125
139
|
return this.encrypt(receiverId, types_1.Protocols.RELAY, (0, utils_1.encodeData)(data));
|
|
126
140
|
}
|
|
127
141
|
packDiscover(receiverId, discoverId) {
|
|
128
142
|
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
//console.debug("Packing Discover");
|
|
129
144
|
if (receiverId instanceof types_1.UserId)
|
|
130
145
|
receiverId = receiverId.toString();
|
|
131
146
|
if (discoverId instanceof types_1.UserId)
|
|
@@ -140,15 +155,23 @@ class FreeSignalNode {
|
|
|
140
155
|
}
|
|
141
156
|
packBootstrap(receiverId) {
|
|
142
157
|
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
//console.debug("Packing Bootstrap");
|
|
143
159
|
return new types_1.Datagram(this.userId.toString(), receiverId.toString(), types_1.Protocols.BOOTSTRAP, (0, utils_1.encodeData)(yield this.keyExchange.generateData()));
|
|
144
160
|
});
|
|
145
161
|
}
|
|
162
|
+
packGetBootstrap(receiverId) {
|
|
163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
//console.debug("Packing GetBootstrap");
|
|
165
|
+
return new types_1.Datagram(this.userId.toString(), receiverId.toString(), types_1.Protocols.BOOTSTRAP);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
146
168
|
decrypt(datagram) {
|
|
147
169
|
return __awaiter(this, void 0, void 0, function* () {
|
|
148
|
-
|
|
149
|
-
|
|
170
|
+
var _a;
|
|
171
|
+
const identityKey = (_a = (yield this.sessions.get(datagram.sender))) === null || _a === void 0 ? void 0 : _a.identityKey;
|
|
172
|
+
if (!identityKey)
|
|
150
173
|
throw new Error("User IdentityKey not found");
|
|
151
|
-
if (!types_1.Datagram.verify(datagram,
|
|
174
|
+
if (!types_1.Datagram.verify(datagram, identityKey.signatureKey))
|
|
152
175
|
throw new Error("Signature not verified");
|
|
153
176
|
const session = yield this.sessions.get(datagram.sender);
|
|
154
177
|
if (!session)
|
|
@@ -168,7 +191,7 @@ class FreeSignalNode {
|
|
|
168
191
|
*/
|
|
169
192
|
open(datagram) {
|
|
170
193
|
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
-
var _a;
|
|
194
|
+
var _a, _b;
|
|
172
195
|
if (datagram instanceof Uint8Array)
|
|
173
196
|
datagram = types_1.Datagram.from(datagram);
|
|
174
197
|
let out = {
|
|
@@ -178,24 +201,39 @@ class FreeSignalNode {
|
|
|
178
201
|
case types_1.Protocols.HANDSHAKE:
|
|
179
202
|
if (!datagram.payload)
|
|
180
203
|
throw new Error("Missing payload");
|
|
204
|
+
if (yield this.sessions.has(datagram.sender)) {
|
|
205
|
+
//console.debug("Opening Handshake Ack");
|
|
206
|
+
const payload = yield this.decrypt(datagram);
|
|
207
|
+
const identityKey = (_a = (yield this.sessions.get(datagram.sender))) === null || _a === void 0 ? void 0 : _a.identityKey;
|
|
208
|
+
if (!identityKey)
|
|
209
|
+
throw new Error("Missing user");
|
|
210
|
+
if (!(0, utils_1.compareBytes)(payload, crypto_1.default.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, identityKey.exchangeKey)))
|
|
211
|
+
throw new Error("Error validating handshake data");
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
//console.debug("Opening Handshake Syn");
|
|
181
215
|
const data = (0, utils_1.decodeData)(datagram.payload);
|
|
182
216
|
if (!types_1.Datagram.verify(datagram, types_1.IdentityKey.from(data.identityKey).signatureKey))
|
|
183
217
|
throw new Error("Signature not verified");
|
|
184
|
-
const { session,
|
|
185
|
-
|
|
186
|
-
yield this.
|
|
187
|
-
yield this.
|
|
188
|
-
|
|
218
|
+
const { session, associatedData } = yield this.keyExchange.digestMessage(data);
|
|
219
|
+
yield this.sessions.set(session.userId.toString(), session);
|
|
220
|
+
yield this.bundles.set(session.userId.toString(), (0, utils_1.decodeData)(associatedData));
|
|
221
|
+
out.datagram = yield this.packHandshake(session.userId);
|
|
222
|
+
if (!out.datagram)
|
|
223
|
+
throw new Error("Error during handshake");
|
|
189
224
|
return out;
|
|
190
225
|
case types_1.Protocols.MESSAGE:
|
|
226
|
+
//console.debug("Opening Message");
|
|
191
227
|
out.payload = yield this.decrypt(datagram);
|
|
192
228
|
return out;
|
|
193
229
|
case types_1.Protocols.RELAY:
|
|
230
|
+
//console.debug("Opening Relay");
|
|
194
231
|
out.payload = yield this.decrypt(datagram);
|
|
195
232
|
return out;
|
|
196
233
|
case types_1.Protocols.DISCOVER:
|
|
234
|
+
//console.debug("Opening Discover");
|
|
197
235
|
const message = (0, utils_1.decodeData)(yield this.decrypt(datagram));
|
|
198
|
-
if (message.type === types_1.DiscoverType.REQUEST && message.discoverId && !(yield this.
|
|
236
|
+
if (message.type === types_1.DiscoverType.REQUEST && message.discoverId && !(yield this.sessions.has(message.discoverId))) {
|
|
199
237
|
let data;
|
|
200
238
|
if (message.discoverId === this.userId.toString()) {
|
|
201
239
|
data = yield this.keyExchange.generateData();
|
|
@@ -228,18 +266,19 @@ class FreeSignalNode {
|
|
|
228
266
|
}
|
|
229
267
|
return out;
|
|
230
268
|
case types_1.Protocols.BOOTSTRAP:
|
|
269
|
+
//console.debug("Opening Bootstrap");
|
|
231
270
|
if (datagram.payload) {
|
|
232
271
|
const data = (0, utils_1.decodeData)(datagram.payload);
|
|
233
272
|
if (!(0, utils_1.compareBytes)(types_1.UserId.fromKey(data.identityKey).toBytes(), (0, utils_1.encodeBase64)(datagram.sender)))
|
|
234
273
|
new Error("Malicious bootstrap request");
|
|
235
|
-
const request = new BootstrapRequest(datagram.sender, data);
|
|
274
|
+
const request = new BootstrapRequest(datagram.sender, yield this.packHandshake(data));
|
|
236
275
|
yield this.bootstraps.set(datagram.sender, request);
|
|
237
276
|
this.onRequest(request);
|
|
238
277
|
}
|
|
239
278
|
;
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
242
|
-
out.datagram =
|
|
279
|
+
const handshakeDatagram = yield ((_b = (yield this.bootstraps.get(datagram.sender))) === null || _b === void 0 ? void 0 : _b.get());
|
|
280
|
+
if (handshakeDatagram)
|
|
281
|
+
out.datagram = handshakeDatagram;
|
|
243
282
|
return out;
|
|
244
283
|
case types_1.Protocols.PING:
|
|
245
284
|
return out;
|
package/dist/test.js
CHANGED
|
@@ -18,15 +18,17 @@ setImmediate(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
18
18
|
const bobBootstrap = yield bob.packBootstrap(alice.userId);
|
|
19
19
|
alice.onRequest = (request) => { request.accept(); };
|
|
20
20
|
const test = (yield alice.open(bobBootstrap)).datagram;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!bobRequest)
|
|
21
|
+
const aliceHandshake = yield alice.getRequest(bob.userId.toString());
|
|
22
|
+
if (!aliceHandshake)
|
|
24
23
|
throw new Error("Bootstrap Failed");
|
|
25
|
-
const
|
|
26
|
-
|
|
24
|
+
const bobHandshake = (yield bob.open(aliceHandshake)).datagram;
|
|
25
|
+
if (!bobHandshake)
|
|
26
|
+
throw new Error("Handshake Failed");
|
|
27
|
+
console.log(!!(yield alice.open(bobHandshake)).header);
|
|
27
28
|
const first = (yield bob.packData(alice.userId, "Hi Alice!")).toBytes();
|
|
28
29
|
console.log("Bob: ", (0, utils_1.decodeData)((yield alice.open(first)).payload));
|
|
29
30
|
const second = yield alice.packData(bob.userId, "Hi Bob!");
|
|
31
|
+
console.log("Test");
|
|
30
32
|
console.log("Alice: ", (0, utils_1.decodeData)((yield bob.open(second)).payload));
|
|
31
33
|
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)));
|
|
32
34
|
third.forEach((data) => __awaiter(void 0, void 0, void 0, function* () {
|
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
|
@@ -35,32 +35,66 @@ 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
|
-
function encryptData(session, data) {
|
|
39
|
-
//console.log(session.id, ' Sending: ', decodeBase64(session.getHeaderKeys().sending ?? new Uint8Array()));
|
|
38
|
+
/*export function encryptData(session: KeySession, data: Uint8Array): EncryptedData {
|
|
40
39
|
const key = session.getSendingKey();
|
|
41
40
|
if (!key)
|
|
42
41
|
throw new Error("Error generating key");
|
|
43
|
-
const nonce =
|
|
44
|
-
const ciphertext =
|
|
42
|
+
const nonce = crypto.randomBytes(EncryptionHeader.nonceLength);
|
|
43
|
+
const ciphertext = crypto.box.encrypt(data, nonce, key.secretKey);
|
|
45
44
|
const headerKey = session.getHeaderKeys().sending;
|
|
45
|
+
//console.debug(session.userId.toString(), "Sending: ", decodeBase64(headerKey ?? new Uint8Array()))
|
|
46
46
|
let header = new EncryptionHeader(key, nonce).toBytes();
|
|
47
|
-
const headerNonce =
|
|
47
|
+
const headerNonce = crypto.randomBytes(EncryptionHeader.nonceLength)
|
|
48
48
|
if (headerKey)
|
|
49
|
-
header =
|
|
49
|
+
header = crypto.box.encrypt(header, headerNonce, headerKey);
|
|
50
50
|
const test = new EncryptedData(header, headerNonce, ciphertext);
|
|
51
51
|
return test;
|
|
52
52
|
}
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
|
|
54
|
+
export function decryptData(session: KeySession, encryptedData: Uint8Array): Uint8Array {
|
|
55
55
|
const encrypted = EncryptedData.from(encryptedData);
|
|
56
56
|
const headerKey = session.getHeaderKeys().receiving;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const nextHeaderKey = session.getHeaderKeys().nextReciving;
|
|
58
|
+
let headerData: Uint8Array | undefined;
|
|
59
|
+
try {
|
|
60
|
+
if (!headerKey)
|
|
61
|
+
throw new Error("Error generating key");
|
|
62
|
+
headerData = crypto.box.decrypt(encrypted.header, encrypted.nonce, headerKey);
|
|
60
63
|
if (!headerData)
|
|
61
64
|
throw new Error("Error calculating header");
|
|
65
|
+
//console.debug(session.userId.toString(), "Receiving: ", decodeBase64(session.getHeaderKeys().receiving ?? new Uint8Array()))
|
|
66
|
+
} catch {
|
|
67
|
+
if (!nextHeaderKey)
|
|
68
|
+
throw new Error("Error generating key");
|
|
69
|
+
headerData = crypto.box.decrypt(encrypted.header, encrypted.nonce, nextHeaderKey);
|
|
70
|
+
if (!headerData) {
|
|
71
|
+
//console.debug(session.toJSON());
|
|
72
|
+
throw new Error("Error calculating header");
|
|
73
|
+
}
|
|
74
|
+
//console.debug(session.userId.toString(), "NextReceiving: ", decodeBase64(session.getHeaderKeys().nextReciving ?? new Uint8Array()))
|
|
62
75
|
}
|
|
63
|
-
const header = EncryptionHeader.from(headerData);
|
|
76
|
+
const header = EncryptionHeader.from(headerData!);
|
|
77
|
+
const key = session.getReceivingKey(header);
|
|
78
|
+
if (!key)
|
|
79
|
+
throw new Error("Error calculating key");
|
|
80
|
+
const decrypted = crypto.box.decrypt(encrypted.payload, header.nonce, key);
|
|
81
|
+
if (!decrypted)
|
|
82
|
+
throw new Error("Error decrypting data");
|
|
83
|
+
return decrypted;
|
|
84
|
+
}*/
|
|
85
|
+
function encryptData(session, data) {
|
|
86
|
+
const key = session.getSendingKey();
|
|
87
|
+
if (!key)
|
|
88
|
+
throw new Error("Error generating key");
|
|
89
|
+
const nonce = crypto_1.default.randomBytes(EncryptionHeader.nonceLength);
|
|
90
|
+
const ciphertext = crypto_1.default.box.encrypt(data, nonce, key.secretKey);
|
|
91
|
+
let header = new EncryptionHeader(key, nonce).toBytes();
|
|
92
|
+
const test = new EncryptedData(header, crypto_1.default.randomBytes(EncryptionHeader.nonceLength), ciphertext);
|
|
93
|
+
return test;
|
|
94
|
+
}
|
|
95
|
+
function decryptData(session, encryptedData) {
|
|
96
|
+
const encrypted = EncryptedData.from(encryptedData);
|
|
97
|
+
const header = EncryptionHeader.from(encrypted.header);
|
|
64
98
|
const key = session.getReceivingKey(header);
|
|
65
99
|
if (!key)
|
|
66
100
|
throw new Error("Error calculating key");
|
|
@@ -86,8 +120,8 @@ class UserId {
|
|
|
86
120
|
static fromKey(identityKey) {
|
|
87
121
|
if (typeof identityKey === 'string')
|
|
88
122
|
identityKey = (0, utils_1.encodeBase64)(identityKey);
|
|
89
|
-
else if (IdentityKey
|
|
90
|
-
identityKey = identityKey.toBytes();
|
|
123
|
+
else if (identityKey instanceof IdentityKey)
|
|
124
|
+
identityKey = (identityKey).toBytes();
|
|
91
125
|
return new UserId(crypto_1.default.hkdf(identityKey, new Uint8Array(32).fill(0), "/freesignal/userid"));
|
|
92
126
|
}
|
|
93
127
|
static from(userId) {
|
|
@@ -97,112 +131,101 @@ class UserId {
|
|
|
97
131
|
}
|
|
98
132
|
}
|
|
99
133
|
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
|
-
}
|
|
121
|
-
}
|
|
122
|
-
get userId() {
|
|
123
|
-
return UserId.fromKey(this.toBytes()).toString();
|
|
124
|
-
}
|
|
125
|
-
toBytes() {
|
|
126
|
-
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
134
|
+
class IdentityKey {
|
|
135
|
+
constructor(identityKey) {
|
|
136
|
+
if (identityKey instanceof IdentityKey) {
|
|
137
|
+
this.info = identityKey.info;
|
|
138
|
+
this.signatureKey = identityKey.signatureKey;
|
|
139
|
+
this.exchangeKey = identityKey.exchangeKey;
|
|
127
140
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
141
|
+
else {
|
|
142
|
+
if (typeof identityKey === 'string')
|
|
143
|
+
identityKey = (0, utils_1.encodeBase64)(identityKey);
|
|
144
|
+
if (identityKey.length !== IdentityKey.keyLength)
|
|
145
|
+
throw new Error("Invalid key length");
|
|
146
|
+
this.info = identityKey[0];
|
|
147
|
+
this.signatureKey = identityKey.subarray(1, crypto_1.default.EdDSA.publicKeyLength + 1);
|
|
148
|
+
this.exchangeKey = identityKey.subarray(crypto_1.default.EdDSA.publicKeyLength + 1, IdentityKey.keyLength);
|
|
133
149
|
}
|
|
134
150
|
}
|
|
135
|
-
|
|
136
|
-
return (
|
|
151
|
+
get userId() {
|
|
152
|
+
return UserId.fromKey(this.toBytes());
|
|
153
|
+
}
|
|
154
|
+
toBytes() {
|
|
155
|
+
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
156
|
+
}
|
|
157
|
+
toString() {
|
|
158
|
+
return (0, utils_1.decodeBase64)(this.toBytes());
|
|
159
|
+
}
|
|
160
|
+
toJSON() {
|
|
161
|
+
return this.toString();
|
|
137
162
|
}
|
|
138
|
-
|
|
139
|
-
function from(...keys) {
|
|
163
|
+
static from(...keys) {
|
|
140
164
|
keys = keys.map(key => {
|
|
141
|
-
if (key instanceof
|
|
165
|
+
if (key instanceof IdentityKey)
|
|
142
166
|
return key.toBytes();
|
|
143
167
|
else if (typeof key === 'string')
|
|
144
168
|
return (0, utils_1.encodeBase64)(key);
|
|
145
169
|
else
|
|
146
170
|
return key;
|
|
147
171
|
});
|
|
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());
|
|
172
|
+
return new IdentityKey(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(IdentityKey.info + IdentityKey.version, 1), ...keys) : keys[0]);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.IdentityKey = IdentityKey;
|
|
176
|
+
IdentityKey.keyLength = crypto_1.default.EdDSA.publicKeyLength + crypto_1.default.ECDH.publicKeyLength + 1;
|
|
177
|
+
IdentityKey.version = 1;
|
|
178
|
+
IdentityKey.info = 0x70;
|
|
179
|
+
class PrivateIdentityKey {
|
|
180
|
+
constructor(privateIdentityKey) {
|
|
181
|
+
if (privateIdentityKey instanceof PrivateIdentityKey) {
|
|
182
|
+
this.info = privateIdentityKey.info;
|
|
183
|
+
this.signatureKey = privateIdentityKey.signatureKey;
|
|
184
|
+
this.exchangeKey = privateIdentityKey.exchangeKey;
|
|
185
|
+
this.identityKey = privateIdentityKey.identityKey;
|
|
184
186
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
else {
|
|
188
|
+
if (typeof privateIdentityKey === 'string')
|
|
189
|
+
privateIdentityKey = (0, utils_1.encodeBase64)(privateIdentityKey);
|
|
190
|
+
if (!PrivateIdentityKey.isIdentityKeys(privateIdentityKey))
|
|
191
|
+
throw new Error("Invalid key length");
|
|
192
|
+
this.info = privateIdentityKey[0];
|
|
193
|
+
this.signatureKey = privateIdentityKey.subarray(1, crypto_1.default.EdDSA.secretKeyLength + 1);
|
|
194
|
+
this.exchangeKey = privateIdentityKey.subarray(crypto_1.default.EdDSA.secretKeyLength + 1, PrivateIdentityKey.keyLength);
|
|
195
|
+
this.identityKey = IdentityKey.from(crypto_1.default.EdDSA.keyPair(this.signatureKey).publicKey, crypto_1.default.ECDH.keyPair(this.exchangeKey).publicKey);
|
|
187
196
|
}
|
|
188
197
|
}
|
|
189
|
-
|
|
190
|
-
return (
|
|
198
|
+
get userId() {
|
|
199
|
+
return UserId.fromKey(this.identityKey.toBytes()).toString();
|
|
200
|
+
}
|
|
201
|
+
toBytes() {
|
|
202
|
+
return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
|
|
203
|
+
}
|
|
204
|
+
toString() {
|
|
205
|
+
return (0, utils_1.decodeBase64)(this.toBytes());
|
|
206
|
+
}
|
|
207
|
+
toJSON() {
|
|
208
|
+
return this.toString();
|
|
209
|
+
}
|
|
210
|
+
static isIdentityKeys(obj) {
|
|
211
|
+
return (obj instanceof Uint8Array && obj.length === PrivateIdentityKey.keyLength) || obj instanceof PrivateIdentityKey;
|
|
191
212
|
}
|
|
192
|
-
|
|
193
|
-
function from(...keys) {
|
|
213
|
+
static from(...keys) {
|
|
194
214
|
keys = keys.map(key => {
|
|
195
|
-
if (key instanceof
|
|
215
|
+
if (key instanceof PrivateIdentityKey)
|
|
196
216
|
return key.toBytes();
|
|
197
217
|
else if (typeof key === 'string')
|
|
198
218
|
return (0, utils_1.encodeBase64)(key);
|
|
199
219
|
else
|
|
200
220
|
return key;
|
|
201
221
|
});
|
|
202
|
-
return new
|
|
222
|
+
return new PrivateIdentityKey(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(PrivateIdentityKey.info + PrivateIdentityKey.version, 1), ...keys) : keys[0]);
|
|
203
223
|
}
|
|
204
|
-
|
|
205
|
-
|
|
224
|
+
}
|
|
225
|
+
exports.PrivateIdentityKey = PrivateIdentityKey;
|
|
226
|
+
PrivateIdentityKey.keyLength = crypto_1.default.EdDSA.secretKeyLength + crypto_1.default.ECDH.secretKeyLength + 1;
|
|
227
|
+
PrivateIdentityKey.version = 1;
|
|
228
|
+
PrivateIdentityKey.info = 0x4E;
|
|
206
229
|
var DiscoverType;
|
|
207
230
|
(function (DiscoverType) {
|
|
208
231
|
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
|
@@ -84,6 +84,7 @@ class KeyExchange {
|
|
|
84
84
|
}
|
|
85
85
|
digestData(message, associatedData) {
|
|
86
86
|
return __awaiter(this, void 0, void 0, function* () {
|
|
87
|
+
//console.debug("Digest Data")
|
|
87
88
|
const ephemeralKey = crypto_1.default.ECDH.keyPair();
|
|
88
89
|
const signedPreKey = (0, utils_1.encodeBase64)(message.signedPreKey);
|
|
89
90
|
const identityKey = types_1.IdentityKey.from(message.identityKey);
|
|
@@ -97,9 +98,9 @@ class KeyExchange {
|
|
|
97
98
|
...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, identityKey.exchangeKey),
|
|
98
99
|
...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, signedPreKey),
|
|
99
100
|
...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
|
-
//, 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) });
|
|
101
|
+
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 3);
|
|
102
|
+
//, headerKey: derivedKey.subarray(KeySession.keyLength, KeySession.keyLength * 2), nextHeaderKey: derivedKey.subarray(KeySession.keyLength * 2)
|
|
103
|
+
const session = new double_ratchet_1.KeySession({ identityKey, remoteKey: identityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
|
|
103
104
|
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
105
|
if (!encrypted)
|
|
105
106
|
throw new Error("Decryption error");
|
|
@@ -112,13 +113,13 @@ class KeyExchange {
|
|
|
112
113
|
signedPreKeyHash: (0, utils_1.decodeBase64)(signedPreKeyHash),
|
|
113
114
|
onetimePreKeyHash: (0, utils_1.decodeBase64)(onetimePreKeyHash),
|
|
114
115
|
associatedData: (0, utils_1.decodeBase64)(encrypted.toBytes())
|
|
115
|
-
}
|
|
116
|
-
identityKey
|
|
116
|
+
}
|
|
117
117
|
};
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
digestMessage(message) {
|
|
121
121
|
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
//console.debug("Digest Message")
|
|
122
123
|
const signedPreKey = yield this.storage.get(message.signedPreKeyHash);
|
|
123
124
|
const hash = message.signedPreKeyHash.concat(message.onetimePreKeyHash);
|
|
124
125
|
const onetimePreKey = yield this.storage.get(hash);
|
|
@@ -133,9 +134,9 @@ class KeyExchange {
|
|
|
133
134
|
...crypto_1.default.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, ephemeralKey),
|
|
134
135
|
...crypto_1.default.ECDH.scalarMult(signedPreKey.secretKey, ephemeralKey),
|
|
135
136
|
...onetimePreKey ? crypto_1.default.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
|
|
136
|
-
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength *
|
|
137
|
-
|
|
138
|
-
const session = new double_ratchet_1.KeySession({ secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
|
|
137
|
+
]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 3);
|
|
138
|
+
//nextHeaderKey: derivedKey.subarray(KeySession.keyLength, KeySession.keyLength * 2), headerKey: derivedKey.subarray(KeySession.keyLength * 2)
|
|
139
|
+
const session = new double_ratchet_1.KeySession({ identityKey, secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
|
|
139
140
|
const data = (0, types_1.decryptData)(session, (0, utils_1.encodeBase64)(message.associatedData));
|
|
140
141
|
if (!data)
|
|
141
142
|
throw new Error("Error decrypting ACK message");
|
|
@@ -143,7 +144,6 @@ class KeyExchange {
|
|
|
143
144
|
throw new Error("Error verifing Associated Data");
|
|
144
145
|
return {
|
|
145
146
|
session,
|
|
146
|
-
identityKey,
|
|
147
147
|
associatedData: data.subarray(64)
|
|
148
148
|
};
|
|
149
149
|
});
|