@freesignal/protocol 0.1.0 → 0.1.2
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/data.d.ts +8 -0
- package/data.js +4 -4
- package/double-ratchet.d.ts +21 -37
- package/double-ratchet.js +68 -123
- package/index.d.ts +25 -0
- package/index.js +27 -0
- package/package.json +1 -1
- package/test.js +13 -5
- package/x3dh.d.ts +28 -34
- package/x3dh.js +78 -88
package/data.d.ts
CHANGED
|
@@ -104,4 +104,12 @@ declare class MessageConstructor implements Encodable, Message {
|
|
|
104
104
|
toString(): string;
|
|
105
105
|
toJSON(): string;
|
|
106
106
|
}
|
|
107
|
+
type LocalStorageIterator<T> = Iterable<T>;
|
|
108
|
+
export interface LocalStorage<K, T> {
|
|
109
|
+
set(key: K, value: T): this;
|
|
110
|
+
get(key: K): T | undefined;
|
|
111
|
+
has(key: K): boolean;
|
|
112
|
+
delete(key: K): boolean;
|
|
113
|
+
entries(): LocalStorageIterator<[K, T]>;
|
|
114
|
+
}
|
|
107
115
|
export {};
|
package/data.js
CHANGED
|
@@ -84,7 +84,7 @@ class DatagramConstructor {
|
|
|
84
84
|
constructor(data, receiver, protocol, payload) {
|
|
85
85
|
if (!receiver && !protocol && !payload) {
|
|
86
86
|
if (data instanceof Uint8Array) {
|
|
87
|
-
const obj = (0, utils_1.encodeUTF8)(data).split('
|
|
87
|
+
const obj = (0, utils_1.encodeUTF8)(data).split(':');
|
|
88
88
|
this.id = obj[0];
|
|
89
89
|
this.version = parseInt(obj[1]);
|
|
90
90
|
this.sender = obj[2];
|
|
@@ -125,7 +125,7 @@ class DatagramConstructor {
|
|
|
125
125
|
this.receiver,
|
|
126
126
|
Protocols.toCode(this.protocol).toString(),
|
|
127
127
|
this.payload ? this.payload : undefined, this.createdAt
|
|
128
|
-
].filter(x => x !== undefined).join('
|
|
128
|
+
].filter(x => x !== undefined).join(':'));
|
|
129
129
|
}
|
|
130
130
|
toString() {
|
|
131
131
|
return this.toJSON();
|
|
@@ -174,7 +174,7 @@ class MessageConstructor {
|
|
|
174
174
|
this.text = "";
|
|
175
175
|
}
|
|
176
176
|
else if (opts instanceof Uint8Array) {
|
|
177
|
-
const arr = (0, utils_1.encodeUTF8)(opts).split('
|
|
177
|
+
const arr = (0, utils_1.encodeUTF8)(opts).split(':');
|
|
178
178
|
this.version = arr[0];
|
|
179
179
|
this.text = (_a = arr[1]) !== null && _a !== void 0 ? _a : "";
|
|
180
180
|
this.group = arr[2];
|
|
@@ -215,7 +215,7 @@ class MessageConstructor {
|
|
|
215
215
|
return this;
|
|
216
216
|
}
|
|
217
217
|
encode() {
|
|
218
|
-
return (0, utils_1.decodeUTF8)([this.version, this.text, this.group, JSON.stringify(this.attachments || [])].join('
|
|
218
|
+
return (0, utils_1.decodeUTF8)([this.version, this.text, this.group, JSON.stringify(this.attachments || [])].join(':'));
|
|
219
219
|
}
|
|
220
220
|
toString() {
|
|
221
221
|
return this.toJSON();
|
package/double-ratchet.d.ts
CHANGED
|
@@ -17,23 +17,22 @@
|
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
19
|
import { Encodable } from "./data";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}): Session;
|
|
20
|
+
type ExportedKeySession = {
|
|
21
|
+
secretKey: string;
|
|
22
|
+
remoteKey: string;
|
|
23
|
+
rootKey: string;
|
|
24
|
+
sendingChain: string;
|
|
25
|
+
receivingChain: string;
|
|
26
|
+
sendingCount: number;
|
|
27
|
+
receivingCount: number;
|
|
28
|
+
previousCount: number;
|
|
29
|
+
previousKeys: [number, Uint8Array][];
|
|
30
|
+
};
|
|
32
31
|
/**
|
|
33
32
|
* Represents a secure Double Ratchet session.
|
|
34
33
|
* Used for forward-secure encryption and decryption of messages.
|
|
35
34
|
*/
|
|
36
|
-
export declare class
|
|
35
|
+
export declare class KeySession {
|
|
37
36
|
private static readonly skipLimit;
|
|
38
37
|
static readonly version = 1;
|
|
39
38
|
static readonly rootKeyLength: number;
|
|
@@ -47,8 +46,9 @@ export declare class Session {
|
|
|
47
46
|
private receivingCount;
|
|
48
47
|
private previousKeys;
|
|
49
48
|
constructor(opts?: {
|
|
49
|
+
secretKey?: Uint8Array;
|
|
50
50
|
remoteKey?: Uint8Array;
|
|
51
|
-
|
|
51
|
+
rootKey?: Uint8Array;
|
|
52
52
|
});
|
|
53
53
|
/**
|
|
54
54
|
* Whether both the sending and receiving chains are initialized.
|
|
@@ -62,11 +62,8 @@ export declare class Session {
|
|
|
62
62
|
* The last known remote public key.
|
|
63
63
|
*/
|
|
64
64
|
get remoteKey(): Uint8Array | undefined;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
*/
|
|
68
|
-
setRemoteKey(key: Uint8Array): this;
|
|
69
|
-
private dhRatchet;
|
|
65
|
+
private setRemoteKey;
|
|
66
|
+
private ratchetKeys;
|
|
70
67
|
private getSendingKey;
|
|
71
68
|
private getReceivingKey;
|
|
72
69
|
/**
|
|
@@ -75,7 +72,7 @@ export declare class Session {
|
|
|
75
72
|
* @param message - The message as a Uint8Array.
|
|
76
73
|
* @returns An EncryptedPayload or undefined if encryption fails.
|
|
77
74
|
*/
|
|
78
|
-
encrypt(message: Uint8Array): EncryptedPayload
|
|
75
|
+
encrypt(message: Uint8Array): EncryptedPayload;
|
|
79
76
|
/**
|
|
80
77
|
* Decrypts an encrypted message.
|
|
81
78
|
*
|
|
@@ -86,14 +83,14 @@ export declare class Session {
|
|
|
86
83
|
/**
|
|
87
84
|
* Export the state of the session;
|
|
88
85
|
*/
|
|
89
|
-
export():
|
|
86
|
+
export(): ExportedKeySession;
|
|
90
87
|
/**
|
|
91
88
|
* Import a state.
|
|
92
89
|
*
|
|
93
90
|
* @param json string returned by `export()` method.
|
|
94
91
|
* @returns session with the state parsed.
|
|
95
92
|
*/
|
|
96
|
-
static import(json: string):
|
|
93
|
+
static import(json: string): KeySession;
|
|
97
94
|
/**
|
|
98
95
|
* The fixed key length (in bytes) used throughout the Double Ratchet session.
|
|
99
96
|
* Typically 32 bytes (256 bits) for symmetric keys.
|
|
@@ -134,26 +131,12 @@ export interface EncryptedPayload extends Encodable {
|
|
|
134
131
|
* The encrypted message content.
|
|
135
132
|
*/
|
|
136
133
|
readonly ciphertext: Uint8Array;
|
|
137
|
-
/**
|
|
138
|
-
* The payload signature.
|
|
139
|
-
*/
|
|
140
|
-
/**
|
|
141
|
-
* Set the signature of the payload.
|
|
142
|
-
*
|
|
143
|
-
* @param signature signature
|
|
144
|
-
*/
|
|
145
|
-
/**
|
|
146
|
-
* Return the payload without the signature.
|
|
147
|
-
*/
|
|
148
134
|
/**
|
|
149
135
|
* Serializes the payload into a Uint8Array for transport.
|
|
150
136
|
*/
|
|
151
137
|
encode(): Uint8Array;
|
|
152
138
|
/**
|
|
153
|
-
*
|
|
154
|
-
*/
|
|
155
|
-
/**
|
|
156
|
-
* Returns the payload as a UTF-8 string.
|
|
139
|
+
* Returns the payload as a Base64 string.
|
|
157
140
|
*/
|
|
158
141
|
toString(): string;
|
|
159
142
|
/**
|
|
@@ -170,3 +153,4 @@ export declare class EncryptedPayload {
|
|
|
170
153
|
*/
|
|
171
154
|
static from(array: Uint8Array | EncryptedPayload): EncryptedPayload;
|
|
172
155
|
}
|
|
156
|
+
export {};
|
package/double-ratchet.js
CHANGED
|
@@ -21,37 +21,25 @@ 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.EncryptedPayload = exports.
|
|
25
|
-
exports.createSession = createSession;
|
|
24
|
+
exports.EncryptedPayload = exports.KeySession = void 0;
|
|
26
25
|
const crypto_1 = __importDefault(require("./crypto"));
|
|
27
26
|
const utils_1 = require("./utils");
|
|
28
|
-
/**
|
|
29
|
-
* Creates a new Double Ratchet session.
|
|
30
|
-
*
|
|
31
|
-
* @param opts.remoteKey The public key of the remote party.
|
|
32
|
-
* @param opts.preSharedKey An optional pre-shared key to initialize the session.
|
|
33
|
-
*
|
|
34
|
-
* @returns A new Double Ratchet session.
|
|
35
|
-
*/
|
|
36
|
-
function createSession(opts) {
|
|
37
|
-
return new Session(opts);
|
|
38
|
-
}
|
|
39
27
|
/**
|
|
40
28
|
* Represents a secure Double Ratchet session.
|
|
41
29
|
* Used for forward-secure encryption and decryption of messages.
|
|
42
30
|
*/
|
|
43
|
-
class
|
|
31
|
+
class KeySession {
|
|
44
32
|
constructor(opts = {}) {
|
|
45
|
-
this.keyPair = crypto_1.default.ECDH.keyPair();
|
|
46
33
|
this.sendingCount = 0;
|
|
47
34
|
this.previousCount = 0;
|
|
48
35
|
this.receivingCount = 0;
|
|
49
36
|
this.previousKeys = new KeyMap();
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
this.keyPair = crypto_1.default.ECDH.keyPair(opts.secretKey);
|
|
38
|
+
if (opts.rootKey)
|
|
39
|
+
this.rootKey = opts.rootKey;
|
|
52
40
|
if (opts.remoteKey) {
|
|
53
41
|
this._remoteKey = opts.remoteKey;
|
|
54
|
-
this.sendingChain = this.
|
|
42
|
+
this.sendingChain = this.ratchetKeys();
|
|
55
43
|
}
|
|
56
44
|
}
|
|
57
45
|
/**
|
|
@@ -66,46 +54,43 @@ class Session {
|
|
|
66
54
|
* The last known remote public key.
|
|
67
55
|
*/
|
|
68
56
|
get remoteKey() { return this._remoteKey; }
|
|
69
|
-
/**
|
|
70
|
-
* Set remote public key.
|
|
71
|
-
*/
|
|
72
57
|
setRemoteKey(key) {
|
|
73
58
|
this._remoteKey = key;
|
|
74
|
-
this.receivingChain = this.
|
|
75
|
-
if (this.receivingCount > (EncryptedPayloadConstructor.maxCount -
|
|
59
|
+
this.receivingChain = this.ratchetKeys();
|
|
60
|
+
if (this.receivingCount > (EncryptedPayloadConstructor.maxCount - KeySession.skipLimit * 2))
|
|
76
61
|
this.receivingCount = 0;
|
|
77
62
|
this.previousCount = this.sendingCount;
|
|
78
63
|
this.keyPair = crypto_1.default.ECDH.keyPair();
|
|
79
|
-
this.sendingChain = this.
|
|
80
|
-
if (this.sendingCount > (EncryptedPayloadConstructor.maxCount -
|
|
64
|
+
this.sendingChain = this.ratchetKeys();
|
|
65
|
+
if (this.sendingCount > (EncryptedPayloadConstructor.maxCount - KeySession.skipLimit * 2))
|
|
81
66
|
this.sendingCount = 0;
|
|
82
67
|
return this;
|
|
83
68
|
}
|
|
84
|
-
|
|
69
|
+
ratchetKeys(info) {
|
|
85
70
|
if (!this._remoteKey)
|
|
86
71
|
throw new Error();
|
|
87
72
|
const sharedKey = crypto_1.default.scalarMult(this.keyPair.secretKey, this._remoteKey);
|
|
88
73
|
if (!this.rootKey)
|
|
89
74
|
this.rootKey = crypto_1.default.hash(sharedKey);
|
|
90
|
-
const hashkey = crypto_1.default.hkdf(sharedKey, this.rootKey, info,
|
|
91
|
-
this.rootKey = hashkey.slice(0,
|
|
92
|
-
return hashkey.slice(
|
|
75
|
+
const hashkey = crypto_1.default.hkdf(sharedKey, this.rootKey, info, KeySession.keyLength * 2);
|
|
76
|
+
this.rootKey = hashkey.slice(0, KeySession.keyLength);
|
|
77
|
+
return hashkey.slice(KeySession.keyLength);
|
|
93
78
|
}
|
|
94
79
|
getSendingKey() {
|
|
95
80
|
if (!this.sendingChain)
|
|
96
81
|
throw new Error;
|
|
97
|
-
const
|
|
98
|
-
this.sendingChain =
|
|
82
|
+
const { chainKey, sharedKey } = KeySession.symmetricRatchet(this.sendingChain);
|
|
83
|
+
this.sendingChain = chainKey;
|
|
99
84
|
this.sendingCount++;
|
|
100
|
-
return
|
|
85
|
+
return sharedKey;
|
|
101
86
|
}
|
|
102
87
|
getReceivingKey() {
|
|
103
88
|
if (!this.receivingChain)
|
|
104
89
|
throw new Error();
|
|
105
|
-
const
|
|
106
|
-
this.receivingChain =
|
|
90
|
+
const { chainKey, sharedKey } = KeySession.symmetricRatchet(this.receivingChain);
|
|
91
|
+
this.receivingChain = chainKey;
|
|
107
92
|
this.receivingCount++;
|
|
108
|
-
return
|
|
93
|
+
return sharedKey;
|
|
109
94
|
}
|
|
110
95
|
/**
|
|
111
96
|
* Encrypts a message payload using the current sending chain.
|
|
@@ -114,17 +99,12 @@ class Session {
|
|
|
114
99
|
* @returns An EncryptedPayload or undefined if encryption fails.
|
|
115
100
|
*/
|
|
116
101
|
encrypt(message) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return new EncryptedPayloadConstructor(this.sendingCount, this.previousCount, this.keyPair.publicKey, nonce, ciphertext);
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
102
|
+
const key = this.getSendingKey();
|
|
103
|
+
if (this.sendingCount >= EncryptedPayloadConstructor.maxCount || this.previousCount >= EncryptedPayloadConstructor.maxCount)
|
|
104
|
+
throw new Error();
|
|
105
|
+
const nonce = crypto_1.default.randomBytes(EncryptedPayloadConstructor.nonceLength);
|
|
106
|
+
const ciphertext = crypto_1.default.box.encrypt(message, nonce, key);
|
|
107
|
+
return new EncryptedPayloadConstructor(this.sendingCount, this.previousCount, this.keyPair.publicKey, nonce, ciphertext);
|
|
128
108
|
}
|
|
129
109
|
/**
|
|
130
110
|
* Decrypts an encrypted message.
|
|
@@ -134,47 +114,44 @@ class Session {
|
|
|
134
114
|
*/
|
|
135
115
|
decrypt(payload) {
|
|
136
116
|
var _a;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
this.previousKeys.set(this.receivingCount, this.getReceivingKey());
|
|
151
|
-
}
|
|
152
|
-
key = this.getReceivingKey();
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
key = this.previousKeys.get(count);
|
|
117
|
+
const encrypted = EncryptedPayload.from(payload);
|
|
118
|
+
const publicKey = encrypted.publicKey;
|
|
119
|
+
if (!(0, utils_1.verifyUint8Array)(publicKey, this._remoteKey)) {
|
|
120
|
+
while (this.receivingCount < encrypted.previous)
|
|
121
|
+
this.previousKeys.set(this.receivingCount, this.getReceivingKey());
|
|
122
|
+
this.setRemoteKey(publicKey);
|
|
123
|
+
}
|
|
124
|
+
let key;
|
|
125
|
+
const count = encrypted.count;
|
|
126
|
+
if (this.receivingCount < count) {
|
|
127
|
+
let i = 0;
|
|
128
|
+
while (this.receivingCount < count - 1 && i < KeySession.skipLimit) {
|
|
129
|
+
this.previousKeys.set(this.receivingCount, this.getReceivingKey());
|
|
156
130
|
}
|
|
157
|
-
|
|
158
|
-
return undefined;
|
|
159
|
-
return (_a = crypto_1.default.box.decrypt(encrypted.ciphertext, encrypted.nonce, key)) !== null && _a !== void 0 ? _a : undefined;
|
|
131
|
+
key = this.getReceivingKey();
|
|
160
132
|
}
|
|
161
|
-
|
|
162
|
-
|
|
133
|
+
else {
|
|
134
|
+
key = this.previousKeys.get(count);
|
|
163
135
|
}
|
|
136
|
+
if (!key)
|
|
137
|
+
return undefined;
|
|
138
|
+
return (_a = crypto_1.default.box.decrypt(encrypted.ciphertext, encrypted.nonce, key)) !== null && _a !== void 0 ? _a : undefined;
|
|
164
139
|
}
|
|
165
140
|
/**
|
|
166
141
|
* Export the state of the session;
|
|
167
142
|
*/
|
|
168
143
|
export() {
|
|
169
|
-
return
|
|
144
|
+
return {
|
|
145
|
+
secretKey: (0, utils_1.encodeBase64)((0, utils_1.concatUint8Array)(this.keyPair.secretKey)),
|
|
170
146
|
remoteKey: (0, utils_1.encodeBase64)(this._remoteKey),
|
|
171
147
|
rootKey: (0, utils_1.encodeBase64)(this.rootKey),
|
|
172
148
|
sendingChain: (0, utils_1.encodeBase64)(this.sendingChain),
|
|
173
149
|
receivingChain: (0, utils_1.encodeBase64)(this.receivingChain),
|
|
174
150
|
sendingCount: this.sendingCount,
|
|
175
151
|
receivingCount: this.receivingCount,
|
|
176
|
-
|
|
177
|
-
|
|
152
|
+
previousCount: this.previousCount,
|
|
153
|
+
previousKeys: Array.from(this.previousKeys.entries())
|
|
154
|
+
};
|
|
178
155
|
}
|
|
179
156
|
/**
|
|
180
157
|
* Import a state.
|
|
@@ -184,27 +161,33 @@ class Session {
|
|
|
184
161
|
*/
|
|
185
162
|
static import(json) {
|
|
186
163
|
const data = JSON.parse(json);
|
|
187
|
-
const session = new
|
|
164
|
+
const session = new KeySession({ secretKey: (0, utils_1.decodeBase64)(data.secretKey), rootKey: (0, utils_1.decodeBase64)(data.rootKey) });
|
|
165
|
+
session._remoteKey = (0, utils_1.decodeBase64)(data.remoteKey);
|
|
188
166
|
session.sendingChain = (0, utils_1.decodeBase64)(data.sendingChain);
|
|
189
167
|
session.receivingChain = (0, utils_1.decodeBase64)(data.receivingChain);
|
|
190
168
|
session.sendingCount = data.sendingCount;
|
|
191
169
|
session.receivingCount = data.receivingCount;
|
|
170
|
+
session.previousCount = data.previousCount;
|
|
171
|
+
session.previousKeys = new KeyMap(data.previousKeys);
|
|
192
172
|
return session;
|
|
193
173
|
}
|
|
194
174
|
static symmetricRatchet(chain, salt, info) {
|
|
195
|
-
const hash = crypto_1.default.hkdf(chain, salt !== null && salt !== void 0 ? salt : new Uint8Array(), info,
|
|
196
|
-
return
|
|
175
|
+
const hash = crypto_1.default.hkdf(chain, salt !== null && salt !== void 0 ? salt : new Uint8Array(), info, KeySession.keyLength * 2);
|
|
176
|
+
return {
|
|
177
|
+
chainKey: new Uint8Array(hash.buffer, 0, KeySession.keyLength),
|
|
178
|
+
sharedKey: new Uint8Array(hash.buffer, KeySession.keyLength)
|
|
179
|
+
};
|
|
197
180
|
}
|
|
198
181
|
}
|
|
199
|
-
exports.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
182
|
+
exports.KeySession = KeySession;
|
|
183
|
+
KeySession.skipLimit = 1000;
|
|
184
|
+
KeySession.version = 1;
|
|
185
|
+
KeySession.rootKeyLength = crypto_1.default.box.keyLength;
|
|
203
186
|
/**
|
|
204
187
|
* The fixed key length (in bytes) used throughout the Double Ratchet session.
|
|
205
188
|
* Typically 32 bytes (256 bits) for symmetric keys.
|
|
206
189
|
*/
|
|
207
|
-
|
|
190
|
+
KeySession.keyLength = 32;
|
|
208
191
|
class EncryptedPayload {
|
|
209
192
|
/**
|
|
210
193
|
* Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
|
|
@@ -230,7 +213,7 @@ class EncryptedPayloadConstructor {
|
|
|
230
213
|
if (typeof arrays[1] === 'number')
|
|
231
214
|
arrays[1] = (0, utils_1.numberToUint8Array)(arrays[1], EncryptedPayloadConstructor.countLength);
|
|
232
215
|
if (arrays.length > 1) {
|
|
233
|
-
arrays.unshift((_a = (typeof arrays[5] === 'number' ? (0, utils_1.numberToUint8Array)(arrays[5]) : arrays[5])) !== null && _a !== void 0 ? _a : (0, utils_1.numberToUint8Array)(
|
|
216
|
+
arrays.unshift((_a = (typeof arrays[5] === 'number' ? (0, utils_1.numberToUint8Array)(arrays[5]) : arrays[5])) !== null && _a !== void 0 ? _a : (0, utils_1.numberToUint8Array)(KeySession.version));
|
|
234
217
|
}
|
|
235
218
|
this.raw = (0, utils_1.concatUint8Array)(...arrays);
|
|
236
219
|
}
|
|
@@ -241,32 +224,6 @@ class EncryptedPayloadConstructor {
|
|
|
241
224
|
get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
|
|
242
225
|
get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
|
|
243
226
|
get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
|
|
244
|
-
/*public get signature() { return this.signed ? new Uint8Array(this.raw.buffer, this.raw.length - EncryptedPayloadConstructor.signatureLength) : undefined }
|
|
245
|
-
|
|
246
|
-
public setSignature(signature: Uint8Array): this {
|
|
247
|
-
this.raw = concatUint8Array(this.raw, signature);
|
|
248
|
-
this.signed = true;
|
|
249
|
-
return this;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
public encodeUnsigned(): Uint8Array {
|
|
253
|
-
return !this.signed ? this.raw : new Uint8Array(this.raw.buffer, 0, this.raw.length - EncryptedPayloadConstructor.signatureLength);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
public encode(fixedLength?: number): Uint8Array {
|
|
257
|
-
if (fixedLength) {
|
|
258
|
-
var padStart = Math.floor(Math.random() * fixedLength - 2 - this.raw.length);
|
|
259
|
-
var padEnd = Math.floor(padStart + 2 + this.raw.length);
|
|
260
|
-
} else {
|
|
261
|
-
padStart = Math.floor(Math.random() * (EncryptedPayloadConstructor.maxPadLength - EncryptedPayloadConstructor.minPadLength) + 1) + EncryptedPayloadConstructor.minPadLength;
|
|
262
|
-
padEnd = Math.floor(Math.random() * (EncryptedPayloadConstructor.maxPadLength - EncryptedPayloadConstructor.minPadLength) + 1) + EncryptedPayloadConstructor.minPadLength;
|
|
263
|
-
}
|
|
264
|
-
return concatUint8Array(
|
|
265
|
-
randomBytes(padStart - 1).map((value) => value !== 255 ? value : (value - 1)),
|
|
266
|
-
new Uint8Array(1).fill(255), this.raw, new Uint8Array(1).fill(255),
|
|
267
|
-
randomBytes(padEnd).map((value) => value !== 255 ? value : (value - 1)),
|
|
268
|
-
);
|
|
269
|
-
}*/
|
|
270
227
|
encode() {
|
|
271
228
|
return this.raw;
|
|
272
229
|
}
|
|
@@ -277,24 +234,20 @@ class EncryptedPayloadConstructor {
|
|
|
277
234
|
previous: this.previous,
|
|
278
235
|
publicKey: (0, utils_1.encodeBase64)(this.publicKey),
|
|
279
236
|
nonce: (0, utils_1.encodeBase64)(this.nonce),
|
|
280
|
-
ciphertext: (0, utils_1.
|
|
281
|
-
//signature: encodeBase64(this.signature)
|
|
237
|
+
ciphertext: (0, utils_1.encodeBase64)(this.ciphertext)
|
|
282
238
|
};
|
|
283
239
|
}
|
|
284
240
|
toString() {
|
|
285
|
-
return (0, utils_1.
|
|
241
|
+
return (0, utils_1.encodeBase64)(this.raw);
|
|
286
242
|
}
|
|
287
243
|
toJSON() {
|
|
288
244
|
return JSON.stringify(this.decode());
|
|
289
245
|
}
|
|
290
246
|
}
|
|
291
|
-
//public static readonly signatureLength = crypto.EdDSA.signatureLength;
|
|
292
247
|
EncryptedPayloadConstructor.secretKeyLength = crypto_1.default.ECDH.secretKeyLength;
|
|
293
248
|
EncryptedPayloadConstructor.publicKeyLength = crypto_1.default.ECDH.publicKeyLength;
|
|
294
249
|
EncryptedPayloadConstructor.keyLength = crypto_1.default.box.keyLength;
|
|
295
250
|
EncryptedPayloadConstructor.nonceLength = crypto_1.default.box.nonceLength;
|
|
296
|
-
//public static readonly minPadLength = 6;
|
|
297
|
-
//public static readonly maxPadLength = 14;
|
|
298
251
|
EncryptedPayloadConstructor.maxCount = 65536; //32768;
|
|
299
252
|
EncryptedPayloadConstructor.countLength = 2;
|
|
300
253
|
class Offsets {
|
|
@@ -320,15 +273,7 @@ Offsets.previous = Offsets.set(Offsets.count.end, EncryptedPayloadConstructor.co
|
|
|
320
273
|
Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedPayloadConstructor.publicKeyLength);
|
|
321
274
|
Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedPayloadConstructor.nonceLength);
|
|
322
275
|
Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
|
|
323
|
-
class KeyMap {
|
|
324
|
-
constructor(iterable) {
|
|
325
|
-
return new KeyMapConstructor(iterable);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
class KeyMapConstructor extends Map {
|
|
329
|
-
constructor(iterable) {
|
|
330
|
-
super(iterable);
|
|
331
|
-
}
|
|
276
|
+
class KeyMap extends Map {
|
|
332
277
|
get(key) {
|
|
333
278
|
const out = super.get(key);
|
|
334
279
|
if (out && !super.delete(key))
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import crypto from "./crypto";
|
|
2
|
+
import { LocalStorage } from "./data";
|
|
3
|
+
import { KeySession } from "./double-ratchet";
|
|
4
|
+
import { KeyExchange } from "./x3dh";
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new Double Ratchet session.
|
|
7
|
+
*
|
|
8
|
+
* @param opts.remoteKey The public key of the remote party.
|
|
9
|
+
* @param opts.preSharedKey An optional pre-shared key to initialize the session.
|
|
10
|
+
*
|
|
11
|
+
* @returns A new Double Ratchet session.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createKeySession(opts?: {
|
|
14
|
+
secretKey?: Uint8Array;
|
|
15
|
+
remoteKey?: Uint8Array;
|
|
16
|
+
rootKey?: Uint8Array;
|
|
17
|
+
}): KeySession;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new X3DH session.
|
|
20
|
+
*
|
|
21
|
+
* @param signKeyPair
|
|
22
|
+
* @param bundleStore
|
|
23
|
+
* @returns A new X3DH session.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createKeyExchange(signKeyPair: crypto.KeyPair, bundleStore?: LocalStorage<string, crypto.KeyPair>): KeyExchange;
|
package/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createKeySession = createKeySession;
|
|
4
|
+
exports.createKeyExchange = createKeyExchange;
|
|
5
|
+
const double_ratchet_1 = require("./double-ratchet");
|
|
6
|
+
const x3dh_1 = require("./x3dh");
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new Double Ratchet session.
|
|
9
|
+
*
|
|
10
|
+
* @param opts.remoteKey The public key of the remote party.
|
|
11
|
+
* @param opts.preSharedKey An optional pre-shared key to initialize the session.
|
|
12
|
+
*
|
|
13
|
+
* @returns A new Double Ratchet session.
|
|
14
|
+
*/
|
|
15
|
+
function createKeySession(opts) {
|
|
16
|
+
return new double_ratchet_1.KeySession(opts);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new X3DH session.
|
|
20
|
+
*
|
|
21
|
+
* @param signKeyPair
|
|
22
|
+
* @param bundleStore
|
|
23
|
+
* @returns A new X3DH session.
|
|
24
|
+
*/
|
|
25
|
+
function createKeyExchange(signKeyPair, bundleStore) {
|
|
26
|
+
return new x3dh_1.KeyExchange(signKeyPair, bundleStore);
|
|
27
|
+
}
|
package/package.json
CHANGED
package/test.js
CHANGED
|
@@ -2,16 +2,24 @@
|
|
|
2
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
|
+
var _a, _b;
|
|
5
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
+
const _1 = require(".");
|
|
7
8
|
const crypto_1 = __importDefault(require("./crypto"));
|
|
8
9
|
const utils_1 = require("./utils");
|
|
9
|
-
const bob =
|
|
10
|
-
const alice =
|
|
10
|
+
const bob = (0, _1.createKeyExchange)(crypto_1.default.EdDSA.keyPair());
|
|
11
|
+
const alice = (0, _1.createKeyExchange)(crypto_1.default.EdDSA.keyPair());
|
|
11
12
|
const bobmessage = bob.generateSyn();
|
|
12
|
-
const {
|
|
13
|
-
|
|
13
|
+
const { session: alicesession, ackMessage: aliceack } = alice.digestSyn(bobmessage);
|
|
14
|
+
const { session: bobsession, cleartext } = (_a = bob.digestAck(aliceack)) !== null && _a !== void 0 ? _a : {};
|
|
15
|
+
if (bobsession && cleartext) {
|
|
14
16
|
console.log("Session established successfully between Alice and Bob.");
|
|
17
|
+
const msg = (_b = bobsession.encrypt((0, utils_1.decodeUTF8)("Hi Alice!"))) === null || _b === void 0 ? void 0 : _b.encode();
|
|
18
|
+
console.log((0, utils_1.encodeUTF8)(alicesession.decrypt(msg)));
|
|
19
|
+
if (alicesession.handshaked && bobsession.handshaked)
|
|
20
|
+
console.log("Successfully handshaked");
|
|
21
|
+
else
|
|
22
|
+
console.log("Error during handshake");
|
|
15
23
|
}
|
|
16
24
|
else
|
|
17
25
|
console.log("Error");
|
package/x3dh.d.ts
CHANGED
|
@@ -17,58 +17,52 @@
|
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
19
|
import crypto from "./crypto";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
type ExportedX3DH = [
|
|
23
|
-
SignKeyPair,
|
|
24
|
-
[
|
|
25
|
-
Array<[string, BoxKeyPair]>,
|
|
26
|
-
Array<[string, BoxKeyPair]>
|
|
27
|
-
]
|
|
28
|
-
];
|
|
20
|
+
import { LocalStorage } from "./data";
|
|
21
|
+
import { KeySession } from "./double-ratchet";
|
|
29
22
|
interface SynMessage {
|
|
30
23
|
readonly version: number;
|
|
31
|
-
readonly
|
|
32
|
-
readonly
|
|
33
|
-
readonly
|
|
34
|
-
readonly
|
|
35
|
-
readonly
|
|
24
|
+
readonly publicKey: string;
|
|
25
|
+
readonly identityKey: string;
|
|
26
|
+
readonly signedPreKey: string;
|
|
27
|
+
readonly signature: string;
|
|
28
|
+
readonly onetimePreKey: string;
|
|
36
29
|
}
|
|
37
30
|
interface AckMessage {
|
|
38
31
|
readonly version: number;
|
|
39
|
-
readonly
|
|
40
|
-
readonly
|
|
41
|
-
readonly
|
|
42
|
-
readonly
|
|
43
|
-
readonly
|
|
44
|
-
readonly
|
|
32
|
+
readonly publicKey: string;
|
|
33
|
+
readonly identityKey: string;
|
|
34
|
+
readonly ephemeralKey: string;
|
|
35
|
+
readonly signedPreKeyHash: string;
|
|
36
|
+
readonly onetimePreKeyHash: string;
|
|
37
|
+
readonly associatedData: string;
|
|
45
38
|
}
|
|
46
39
|
export interface Bundle {
|
|
47
40
|
readonly version: number;
|
|
48
|
-
readonly
|
|
49
|
-
readonly
|
|
50
|
-
readonly
|
|
51
|
-
readonly
|
|
52
|
-
readonly
|
|
41
|
+
readonly publicKey: string;
|
|
42
|
+
readonly identityKey: string;
|
|
43
|
+
readonly signedPreKey: string;
|
|
44
|
+
readonly signature: string;
|
|
45
|
+
readonly onetimePreKeyHash: string[];
|
|
53
46
|
}
|
|
54
|
-
export declare class
|
|
47
|
+
export declare class KeyExchange {
|
|
55
48
|
static readonly version = 1;
|
|
56
49
|
private static readonly hkdfInfo;
|
|
57
50
|
private static readonly maxOPK;
|
|
58
|
-
private readonly
|
|
59
|
-
private readonly
|
|
51
|
+
private readonly publicKey;
|
|
52
|
+
private readonly identityKey;
|
|
60
53
|
private readonly bundleStore;
|
|
61
|
-
constructor(signKeyPair:
|
|
54
|
+
constructor(signKeyPair: crypto.KeyPair, bundleStore?: LocalStorage<string, crypto.KeyPair>);
|
|
62
55
|
private generateSPK;
|
|
63
56
|
private generateOPK;
|
|
64
57
|
generateBundle(length?: number): Bundle;
|
|
65
58
|
generateSyn(): SynMessage;
|
|
66
|
-
digestSyn(message: SynMessage
|
|
67
|
-
|
|
59
|
+
digestSyn(message: SynMessage): {
|
|
60
|
+
session: KeySession;
|
|
68
61
|
ackMessage: AckMessage;
|
|
69
62
|
};
|
|
70
|
-
digestAck(message: AckMessage
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
digestAck(message: AckMessage): {
|
|
64
|
+
session: KeySession;
|
|
65
|
+
cleartext: Uint8Array;
|
|
66
|
+
};
|
|
73
67
|
}
|
|
74
68
|
export {};
|
package/x3dh.js
CHANGED
|
@@ -21,116 +21,106 @@ 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.
|
|
24
|
+
exports.KeyExchange = void 0;
|
|
25
25
|
const crypto_1 = __importDefault(require("./crypto"));
|
|
26
26
|
const double_ratchet_1 = require("./double-ratchet");
|
|
27
27
|
const utils_1 = require("./utils");
|
|
28
|
-
class
|
|
29
|
-
constructor(signKeyPair,
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.bundleStore =
|
|
33
|
-
SPK: new Map(instance ? instance[0] : []),
|
|
34
|
-
OPK: new Map(instance ? instance[1] : [])
|
|
35
|
-
};
|
|
28
|
+
class KeyExchange {
|
|
29
|
+
constructor(signKeyPair, bundleStore) {
|
|
30
|
+
this.publicKey = signKeyPair;
|
|
31
|
+
this.identityKey = crypto_1.default.ECDH.keyPair(crypto_1.default.hash(signKeyPair.secretKey));
|
|
32
|
+
this.bundleStore = bundleStore !== null && bundleStore !== void 0 ? bundleStore : new Map();
|
|
36
33
|
}
|
|
37
34
|
generateSPK() {
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
this.bundleStore.
|
|
41
|
-
return {
|
|
35
|
+
const signedPreKey = crypto_1.default.ECDH.keyPair();
|
|
36
|
+
const signedPreKeyHash = crypto_1.default.hash(signedPreKey.publicKey);
|
|
37
|
+
this.bundleStore.set((0, utils_1.encodeBase64)(signedPreKeyHash), signedPreKey);
|
|
38
|
+
return { signedPreKey, signedPreKeyHash };
|
|
42
39
|
}
|
|
43
40
|
generateOPK(spkHash) {
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
this.bundleStore.
|
|
47
|
-
return {
|
|
41
|
+
const onetimePreKey = crypto_1.default.ECDH.keyPair();
|
|
42
|
+
const onetimePreKeyHash = crypto_1.default.hash(onetimePreKey.publicKey);
|
|
43
|
+
this.bundleStore.set((0, utils_1.encodeBase64)(spkHash).concat((0, utils_1.encodeBase64)(onetimePreKeyHash)), onetimePreKey);
|
|
44
|
+
return { onetimePreKey, onetimePreKeyHash };
|
|
48
45
|
}
|
|
49
46
|
generateBundle(length) {
|
|
50
|
-
const {
|
|
51
|
-
const
|
|
47
|
+
const { signedPreKey, signedPreKeyHash } = this.generateSPK();
|
|
48
|
+
const onetimePreKey = new Array(length !== null && length !== void 0 ? length : KeyExchange.maxOPK).fill(0).map(() => this.generateOPK(signedPreKeyHash).onetimePreKey);
|
|
52
49
|
return {
|
|
53
|
-
version:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
version: KeyExchange.version,
|
|
51
|
+
publicKey: (0, utils_1.encodeBase64)(this.publicKey.publicKey),
|
|
52
|
+
identityKey: (0, utils_1.encodeBase64)(this.identityKey.publicKey),
|
|
53
|
+
signedPreKey: (0, utils_1.encodeBase64)(signedPreKey.publicKey),
|
|
54
|
+
signature: (0, utils_1.encodeBase64)(crypto_1.default.EdDSA.sign(signedPreKeyHash, this.publicKey.secretKey)),
|
|
55
|
+
onetimePreKeyHash: onetimePreKey.map(opk => (0, utils_1.encodeBase64)(opk.publicKey))
|
|
59
56
|
};
|
|
60
57
|
}
|
|
61
58
|
generateSyn() {
|
|
62
|
-
const {
|
|
63
|
-
const {
|
|
59
|
+
const { signedPreKey, signedPreKeyHash } = this.generateSPK();
|
|
60
|
+
const { onetimePreKey } = this.generateOPK(signedPreKeyHash);
|
|
64
61
|
return {
|
|
65
|
-
version:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
version: KeyExchange.version,
|
|
63
|
+
publicKey: (0, utils_1.encodeBase64)(this.publicKey.publicKey),
|
|
64
|
+
identityKey: (0, utils_1.encodeBase64)(this.identityKey.publicKey),
|
|
65
|
+
signedPreKey: (0, utils_1.encodeBase64)(signedPreKey.publicKey),
|
|
66
|
+
signature: (0, utils_1.encodeBase64)(crypto_1.default.EdDSA.sign(signedPreKeyHash, this.publicKey.secretKey)),
|
|
67
|
+
onetimePreKey: (0, utils_1.encodeBase64)(onetimePreKey.publicKey)
|
|
71
68
|
};
|
|
72
69
|
}
|
|
73
|
-
digestSyn(message
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
70
|
+
digestSyn(message) {
|
|
71
|
+
const ephemeralKey = crypto_1.default.ECDH.keyPair();
|
|
72
|
+
const signedPreKey = (0, utils_1.decodeBase64)(message.signedPreKey);
|
|
73
|
+
const identityKey = (0, utils_1.decodeBase64)(message.identityKey);
|
|
74
|
+
const onetimePreKey = message.onetimePreKey ? (0, utils_1.decodeBase64)(message.onetimePreKey) : undefined;
|
|
75
|
+
const signedPreKeyHash = crypto_1.default.hash(signedPreKey);
|
|
76
|
+
const onetimePreKeyHash = onetimePreKey ? crypto_1.default.hash(onetimePreKey) : new Uint8Array();
|
|
80
77
|
const rootKey = crypto_1.default.hkdf(new Uint8Array([
|
|
81
|
-
...crypto_1.default.scalarMult(this.
|
|
82
|
-
...crypto_1.default.scalarMult(
|
|
83
|
-
...crypto_1.default.scalarMult(
|
|
84
|
-
...
|
|
85
|
-
]), new Uint8Array(double_ratchet_1.
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
...crypto_1.default.scalarMult(this.identityKey.secretKey, signedPreKey),
|
|
79
|
+
...crypto_1.default.scalarMult(ephemeralKey.secretKey, identityKey),
|
|
80
|
+
...crypto_1.default.scalarMult(ephemeralKey.secretKey, signedPreKey),
|
|
81
|
+
...onetimePreKey ? crypto_1.default.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
|
|
82
|
+
]), new Uint8Array(double_ratchet_1.KeySession.rootKeyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.rootKeyLength);
|
|
83
|
+
const session = new double_ratchet_1.KeySession({ secretKey: this.identityKey.secretKey, remoteKey: identityKey, rootKey });
|
|
84
|
+
const cyphertext = session.encrypt((0, utils_1.concatUint8Array)(crypto_1.default.hash(this.identityKey.publicKey), crypto_1.default.hash(identityKey)));
|
|
85
|
+
if (!cyphertext)
|
|
86
|
+
throw new Error();
|
|
88
87
|
return {
|
|
89
|
-
|
|
88
|
+
session,
|
|
90
89
|
ackMessage: {
|
|
91
|
-
version:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
version: KeyExchange.version,
|
|
91
|
+
publicKey: (0, utils_1.encodeBase64)(this.publicKey.publicKey),
|
|
92
|
+
identityKey: (0, utils_1.encodeBase64)(this.identityKey.publicKey),
|
|
93
|
+
ephemeralKey: (0, utils_1.encodeBase64)(ephemeralKey.publicKey),
|
|
94
|
+
signedPreKeyHash: (0, utils_1.encodeBase64)(signedPreKeyHash),
|
|
95
|
+
onetimePreKeyHash: (0, utils_1.encodeBase64)(onetimePreKeyHash),
|
|
96
|
+
associatedData: (0, utils_1.encodeBase64)(cyphertext.encode())
|
|
98
97
|
}
|
|
99
98
|
};
|
|
100
99
|
}
|
|
101
|
-
digestAck(message
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
digestAck(message) {
|
|
101
|
+
const signedPreKey = this.bundleStore.get(message.signedPreKeyHash);
|
|
102
|
+
const hash = message.signedPreKeyHash.concat(message.onetimePreKeyHash);
|
|
103
|
+
const onetimePreKey = this.bundleStore.get(hash);
|
|
104
|
+
if (!signedPreKey || !onetimePreKey || !message.identityKey || !message.ephemeralKey)
|
|
105
|
+
throw new Error("ACK message malformed");
|
|
106
|
+
if (!this.bundleStore.delete(hash))
|
|
107
|
+
throw new Error("Bundle store deleting error");
|
|
108
|
+
const identityKey = (0, utils_1.decodeBase64)(message.identityKey);
|
|
109
|
+
const ephemeralKey = (0, utils_1.decodeBase64)(message.ephemeralKey);
|
|
108
110
|
const rootKey = crypto_1.default.hkdf(new Uint8Array([
|
|
109
|
-
...crypto_1.default.scalarMult(
|
|
110
|
-
...crypto_1.default.scalarMult(this.
|
|
111
|
-
...crypto_1.default.scalarMult(
|
|
112
|
-
...
|
|
113
|
-
]), new Uint8Array(double_ratchet_1.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (!
|
|
117
|
-
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
export() {
|
|
121
|
-
return [
|
|
122
|
-
this.IK,
|
|
123
|
-
[
|
|
124
|
-
Array.from(this.bundleStore.SPK.entries()),
|
|
125
|
-
Array.from(this.bundleStore.OPK.entries())
|
|
126
|
-
]
|
|
127
|
-
];
|
|
128
|
-
}
|
|
129
|
-
static import(input) {
|
|
130
|
-
return new X3DH(...input);
|
|
111
|
+
...crypto_1.default.scalarMult(signedPreKey.secretKey, identityKey),
|
|
112
|
+
...crypto_1.default.scalarMult(this.identityKey.secretKey, ephemeralKey),
|
|
113
|
+
...crypto_1.default.scalarMult(signedPreKey.secretKey, ephemeralKey),
|
|
114
|
+
...onetimePreKey ? crypto_1.default.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
|
|
115
|
+
]), new Uint8Array(double_ratchet_1.KeySession.rootKeyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.rootKeyLength);
|
|
116
|
+
const session = new double_ratchet_1.KeySession({ secretKey: this.identityKey.secretKey, rootKey });
|
|
117
|
+
const cleartext = session.decrypt((0, utils_1.decodeBase64)(message.associatedData));
|
|
118
|
+
if (!cleartext)
|
|
119
|
+
throw new Error("Error decrypting ACK message");
|
|
120
|
+
return { session, cleartext };
|
|
131
121
|
}
|
|
132
122
|
}
|
|
133
|
-
exports.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
123
|
+
exports.KeyExchange = KeyExchange;
|
|
124
|
+
KeyExchange.version = 1;
|
|
125
|
+
KeyExchange.hkdfInfo = (0, utils_1.decodeUTF8)("freesignal/x3dh/" + KeyExchange.version);
|
|
126
|
+
KeyExchange.maxOPK = 10;
|