@freesignal/protocol 0.1.5 → 0.1.6
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/package.json +1 -1
- package/test.js +18 -17
- package/types.d.ts +5 -5
- package/x3dh.d.ts +2 -2
- package/x3dh.js +66 -24
package/package.json
CHANGED
package/test.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
|
-
var _a, _b;
|
|
6
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
6
|
const _1 = require(".");
|
|
8
7
|
const crypto_1 = __importDefault(require("./crypto"));
|
|
@@ -12,20 +11,22 @@ const bob = (0, _1.createKeyExchange)(crypto_1.default.EdDSA.keyPair().secretKey
|
|
|
12
11
|
const alice = (0, _1.createKeyExchange)(crypto_1.default.EdDSA.keyPair().secretKey, crypto_1.default.ECDH.keyPair().secretKey);
|
|
13
12
|
const bobmessage = bob.generateData();
|
|
14
13
|
const { session: alicesession, message: aliceack } = alice.digestData(bobmessage);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
bob.digestMessage(aliceack).then(({ session: bobsession, cleartext }) => {
|
|
15
|
+
var _a;
|
|
16
|
+
if (bobsession && cleartext) {
|
|
17
|
+
console.log("Session established successfully between Alice and Bob.");
|
|
18
|
+
const datagram = data_1.Datagram.create(bob.signatureKey, alice.signatureKey, data_1.Protocols.MESSAGE, (_a = bobsession.encrypt((0, utils_1.decodeUTF8)("Hi Alice!"))) === null || _a === void 0 ? void 0 : _a.encode());
|
|
19
|
+
//console.log(datagram.payload);
|
|
20
|
+
const msg = datagram.encode();
|
|
21
|
+
console.log((0, utils_1.encodeUTF8)(alicesession.decrypt(data_1.Datagram.from(msg).payload)));
|
|
22
|
+
if (alicesession.handshaked && bobsession.handshaked)
|
|
23
|
+
console.log("Successfully handshaked");
|
|
24
|
+
else
|
|
25
|
+
console.log("Error during handshake");
|
|
26
|
+
const longmsg = data_1.Datagram.create(alice.signatureKey, bob.signatureKey, data_1.Protocols.MESSAGE, alicesession.encrypt(new Uint8Array(1000000).fill(33).map(val => val + Math.floor(Math.random() * 93))));
|
|
27
|
+
console.log(longmsg.encode().length);
|
|
28
|
+
console.log(longmsg.encode(false).length);
|
|
29
|
+
}
|
|
24
30
|
else
|
|
25
|
-
console.log("Error
|
|
26
|
-
|
|
27
|
-
console.log(longmsg.encode().length);
|
|
28
|
-
console.log(longmsg.encode(false).length);
|
|
29
|
-
}
|
|
30
|
-
else
|
|
31
|
-
console.log("Error");
|
|
31
|
+
console.log("Error");
|
|
32
|
+
});
|
package/types.d.ts
CHANGED
|
@@ -27,11 +27,11 @@ export declare namespace Encodable {
|
|
|
27
27
|
}
|
|
28
28
|
type LocalStorageIterator<T> = Iterable<T>;
|
|
29
29
|
export interface LocalStorage<K, T> {
|
|
30
|
-
set(key: K, value: T): this
|
|
31
|
-
get(key: K): T | undefined
|
|
32
|
-
has(key: K): boolean
|
|
33
|
-
delete(key: K): boolean
|
|
34
|
-
entries(): LocalStorageIterator<[K, T]
|
|
30
|
+
set(key: K, value: T): Promise<this>;
|
|
31
|
+
get(key: K): Promise<T | undefined>;
|
|
32
|
+
has(key: K): Promise<boolean>;
|
|
33
|
+
delete(key: K): Promise<boolean>;
|
|
34
|
+
entries(): Promise<LocalStorageIterator<[K, T]>>;
|
|
35
35
|
}
|
|
36
36
|
export interface KeyExchangeData {
|
|
37
37
|
readonly version: number;
|
package/x3dh.d.ts
CHANGED
|
@@ -37,8 +37,8 @@ export declare class KeyExchange {
|
|
|
37
37
|
session: KeySession;
|
|
38
38
|
message: KeyExchangeSynMessage;
|
|
39
39
|
};
|
|
40
|
-
digestMessage(message: KeyExchangeSynMessage): {
|
|
40
|
+
digestMessage(message: KeyExchangeSynMessage): Promise<{
|
|
41
41
|
session: KeySession;
|
|
42
42
|
cleartext: Uint8Array;
|
|
43
|
-
}
|
|
43
|
+
}>;
|
|
44
44
|
}
|
package/x3dh.js
CHANGED
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
* You should have received a copy of the GNU General Public License
|
|
18
18
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
19
19
|
*/
|
|
20
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
21
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
22
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
23
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
24
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
25
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
26
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
27
|
+
});
|
|
28
|
+
};
|
|
20
29
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
30
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
31
|
};
|
|
@@ -29,7 +38,7 @@ class KeyExchange {
|
|
|
29
38
|
constructor(signSecretKey, boxSecretKey, bundleStore) {
|
|
30
39
|
this._signatureKey = crypto_1.default.EdDSA.keyPair(signSecretKey);
|
|
31
40
|
this._identityKey = crypto_1.default.ECDH.keyPair(boxSecretKey);
|
|
32
|
-
this.bundleStore = bundleStore !== null && bundleStore !== void 0 ? bundleStore : new
|
|
41
|
+
this.bundleStore = bundleStore !== null && bundleStore !== void 0 ? bundleStore : new AsyncMap();
|
|
33
42
|
}
|
|
34
43
|
get signatureKey() { return this._signatureKey.publicKey; }
|
|
35
44
|
get identityKey() { return this._identityKey.publicKey; }
|
|
@@ -72,7 +81,7 @@ class KeyExchange {
|
|
|
72
81
|
digestData(message) {
|
|
73
82
|
const ephemeralKey = crypto_1.default.ECDH.keyPair();
|
|
74
83
|
const signedPreKey = (0, utils_1.decodeBase64)(message.signedPreKey);
|
|
75
|
-
if (!crypto_1.default.EdDSA.verify(signedPreKey, (0, utils_1.decodeBase64)(message.signature), (0, utils_1.decodeBase64)(message.publicKey)))
|
|
84
|
+
if (!crypto_1.default.EdDSA.verify(crypto_1.default.hash(signedPreKey), (0, utils_1.decodeBase64)(message.signature), (0, utils_1.decodeBase64)(message.publicKey)))
|
|
76
85
|
throw new Error("Signature verification failed");
|
|
77
86
|
const identityKey = (0, utils_1.decodeBase64)(message.identityKey);
|
|
78
87
|
const onetimePreKey = message.onetimePreKey ? (0, utils_1.decodeBase64)(message.onetimePreKey) : undefined;
|
|
@@ -102,31 +111,64 @@ class KeyExchange {
|
|
|
102
111
|
};
|
|
103
112
|
}
|
|
104
113
|
digestMessage(message) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
115
|
+
const signedPreKey = yield this.bundleStore.get(message.signedPreKeyHash);
|
|
116
|
+
const hash = message.signedPreKeyHash.concat(message.onetimePreKeyHash);
|
|
117
|
+
const onetimePreKey = yield this.bundleStore.get(hash);
|
|
118
|
+
if (!signedPreKey || !onetimePreKey || !message.identityKey || !message.ephemeralKey)
|
|
119
|
+
throw new Error("ACK message malformed");
|
|
120
|
+
if (!this.bundleStore.delete(hash))
|
|
121
|
+
throw new Error("Bundle store deleting error");
|
|
122
|
+
const identityKey = (0, utils_1.decodeBase64)(message.identityKey);
|
|
123
|
+
const ephemeralKey = (0, utils_1.decodeBase64)(message.ephemeralKey);
|
|
124
|
+
const rootKey = crypto_1.default.hkdf(new Uint8Array([
|
|
125
|
+
...crypto_1.default.scalarMult(signedPreKey.secretKey, identityKey),
|
|
126
|
+
...crypto_1.default.scalarMult(this._identityKey.secretKey, ephemeralKey),
|
|
127
|
+
...crypto_1.default.scalarMult(signedPreKey.secretKey, ephemeralKey),
|
|
128
|
+
...onetimePreKey ? crypto_1.default.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
|
|
129
|
+
]), new Uint8Array(double_ratchet_1.KeySession.rootKeyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.rootKeyLength);
|
|
130
|
+
const session = new double_ratchet_1.KeySession({ secretKey: this._identityKey.secretKey, rootKey });
|
|
131
|
+
const cleartext = session.decrypt((0, utils_1.decodeBase64)(message.associatedData));
|
|
132
|
+
if (!cleartext)
|
|
133
|
+
throw new Error("Error decrypting ACK message");
|
|
134
|
+
if (!(0, utils_1.verifyUint8Array)(cleartext, (0, utils_1.concatUint8Array)(crypto_1.default.hash(identityKey), crypto_1.default.hash(this._identityKey.publicKey))))
|
|
135
|
+
throw new Error("Error verifing Associated Data");
|
|
136
|
+
return { session, cleartext };
|
|
137
|
+
});
|
|
127
138
|
}
|
|
128
139
|
}
|
|
129
140
|
exports.KeyExchange = KeyExchange;
|
|
130
141
|
KeyExchange.version = 1;
|
|
131
142
|
KeyExchange.hkdfInfo = (0, utils_1.decodeUTF8)("freesignal/x3dh/" + KeyExchange.version);
|
|
132
143
|
KeyExchange.maxOPK = 10;
|
|
144
|
+
class AsyncMap {
|
|
145
|
+
constructor() {
|
|
146
|
+
this.map = new Map();
|
|
147
|
+
}
|
|
148
|
+
set(key, value) {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
this.map.set(key, value);
|
|
151
|
+
return this;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
get(key) {
|
|
155
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
156
|
+
return this.map.get(key);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
has(key) {
|
|
160
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
return this.map.has(key);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
delete(key) {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
+
return this.map.delete(key);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
entries() {
|
|
170
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
return this.map.entries();
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|