@freesignal/protocol 0.1.8 → 0.2.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/api.d.ts +38 -0
- package/api.js +118 -0
- package/double-ratchet.d.ts +1 -56
- package/double-ratchet.js +10 -97
- package/index.d.ts +12 -5
- package/index.js +11 -20
- package/package.json +4 -1
- package/test.js +5 -7
- package/types.d.ts +121 -83
- package/types.js +236 -13
- package/x3dh.d.ts +1 -1
- package/x3dh.js +2 -2
- package/crypto.d.ts +0 -24
- package/crypto.js +0 -156
- package/data.d.ts +0 -51
- package/data.js +0 -131
- package/utils.d.ts +0 -78
- package/utils.js +0 -146
package/api.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Crypto, LocalStorage } from "@freesignal/interfaces";
|
|
2
|
+
import { KeySession } from "./double-ratchet";
|
|
3
|
+
import { KeyExchange } from "./x3dh";
|
|
4
|
+
import { Datagram, IdentityKeys, EncryptedData, UserId } from "./types";
|
|
5
|
+
export declare class FreeSignalAPI {
|
|
6
|
+
protected readonly signKey: Crypto.KeyPair;
|
|
7
|
+
protected readonly boxKey: Crypto.KeyPair;
|
|
8
|
+
protected readonly sessions: LocalStorage<UserId, KeySession>;
|
|
9
|
+
protected readonly keyExchange: KeyExchange;
|
|
10
|
+
protected readonly users: LocalStorage<UserId, IdentityKeys>;
|
|
11
|
+
constructor(opts: {
|
|
12
|
+
secretSignKey: Uint8Array;
|
|
13
|
+
secretBoxKey: Uint8Array;
|
|
14
|
+
sessions: LocalStorage<UserId, KeySession>;
|
|
15
|
+
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
16
|
+
users: LocalStorage<UserId, IdentityKeys>;
|
|
17
|
+
});
|
|
18
|
+
encryptData(data: Uint8Array, userId: string): Promise<EncryptedData>;
|
|
19
|
+
decryptData(data: Uint8Array, userId: string): Promise<Uint8Array>;
|
|
20
|
+
protected digestToken(auth?: string): Promise<{
|
|
21
|
+
identityKeys: IdentityKeys;
|
|
22
|
+
userId: UserId;
|
|
23
|
+
}>;
|
|
24
|
+
createToken(publicKey: Uint8Array): string;
|
|
25
|
+
protected packDatagrams(messages: Datagram[]): Uint8Array;
|
|
26
|
+
protected unpackDatagrams(data: Uint8Array): Datagram[];
|
|
27
|
+
get identityKeys(): {
|
|
28
|
+
readonly publicKey: string;
|
|
29
|
+
readonly identityKey: string;
|
|
30
|
+
encode(): Uint8Array;
|
|
31
|
+
toString(): string;
|
|
32
|
+
toJSON(): string;
|
|
33
|
+
};
|
|
34
|
+
static createSecretIdentityKeys(): {
|
|
35
|
+
secretSignKey: Uint8Array;
|
|
36
|
+
secretBoxKey: Uint8Array;
|
|
37
|
+
};
|
|
38
|
+
}
|
package/api.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.FreeSignalAPI = void 0;
|
|
16
|
+
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
17
|
+
const x3dh_1 = require("./x3dh");
|
|
18
|
+
const utils_1 = require("@freesignal/utils");
|
|
19
|
+
const types_1 = require("./types");
|
|
20
|
+
const fflate_1 = __importDefault(require("fflate"));
|
|
21
|
+
class FreeSignalAPI {
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
const { secretSignKey, secretBoxKey, sessions, keyExchange, users } = opts;
|
|
24
|
+
this.signKey = crypto_1.default.EdDSA.keyPair(secretSignKey);
|
|
25
|
+
this.boxKey = crypto_1.default.ECDH.keyPair(secretBoxKey);
|
|
26
|
+
this.sessions = sessions;
|
|
27
|
+
this.keyExchange = new x3dh_1.KeyExchange(secretSignKey, secretBoxKey, keyExchange);
|
|
28
|
+
this.users = users;
|
|
29
|
+
}
|
|
30
|
+
encryptData(data, userId) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
const session = yield this.sessions.get(userId);
|
|
33
|
+
if (!session)
|
|
34
|
+
throw new Error('Session not found for user: ' + userId);
|
|
35
|
+
const encrypted = session.encrypt(data);
|
|
36
|
+
this.sessions.set(userId, session); // Ensure session is updated
|
|
37
|
+
return encrypted;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
decryptData(data, userId) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
const session = yield this.sessions.get(userId);
|
|
43
|
+
if (!session)
|
|
44
|
+
throw new Error('Session not found for user: ' + userId);
|
|
45
|
+
const decrypted = session.decrypt(data);
|
|
46
|
+
if (!decrypted)
|
|
47
|
+
throw new Error('Decryption failed for user: ' + userId);
|
|
48
|
+
this.sessions.set(userId, session); // Ensure session is updated
|
|
49
|
+
return decrypted;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
digestToken(auth) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
if (auth && auth.startsWith("Bearer ")) {
|
|
55
|
+
const [userId, sharedId] = auth.substring(7).split(":");
|
|
56
|
+
const identityKeys = yield this.users.get(userId);
|
|
57
|
+
if (!identityKeys)
|
|
58
|
+
throw new Error('User not found or invalid auth token');
|
|
59
|
+
if ((0, utils_1.verifyUint8Array)(crypto_1.default.hash(crypto_1.default.ECDH.sharedKey((0, utils_1.decodeBase64)(identityKeys.publicKey), this.boxKey.secretKey)), (0, utils_1.decodeBase64)(sharedId)))
|
|
60
|
+
return { identityKeys, userId: auth };
|
|
61
|
+
else
|
|
62
|
+
throw new Error('Authorization token not valid');
|
|
63
|
+
}
|
|
64
|
+
throw new Error('Authorization header is required');
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
createToken(publicKey) {
|
|
68
|
+
const sharedId = crypto_1.default.hash(crypto_1.default.ECDH.sharedKey(publicKey, this.boxKey.secretKey));
|
|
69
|
+
const userId = crypto_1.default.hash(this.signKey.publicKey);
|
|
70
|
+
return `Bearer ${(0, utils_1.encodeBase64)(userId)}:${(0, utils_1.encodeBase64)(sharedId)}`;
|
|
71
|
+
}
|
|
72
|
+
;
|
|
73
|
+
packDatagrams(messages) {
|
|
74
|
+
return fflate_1.default.deflateSync((0, utils_1.concatUint8Array)(...messages.flatMap(datagram => {
|
|
75
|
+
const encoded = types_1.Datagram.from(datagram).encode();
|
|
76
|
+
return [(0, utils_1.numberToUint8Array)(encoded.length, 8), encoded];
|
|
77
|
+
})));
|
|
78
|
+
}
|
|
79
|
+
unpackDatagrams(data) {
|
|
80
|
+
const messages = [];
|
|
81
|
+
let offset = 0;
|
|
82
|
+
data = fflate_1.default.inflateSync(data);
|
|
83
|
+
while (offset < data.length) {
|
|
84
|
+
const length = data.subarray(offset, offset + 8);
|
|
85
|
+
if (length.length < 8) {
|
|
86
|
+
throw new Error('Invalid message length');
|
|
87
|
+
}
|
|
88
|
+
const messageLength = (0, utils_1.numberFromUint8Array)(length);
|
|
89
|
+
offset += 8;
|
|
90
|
+
if (offset + messageLength > data.length) {
|
|
91
|
+
throw new Error('Invalid message length');
|
|
92
|
+
}
|
|
93
|
+
const messageData = data.subarray(offset, offset + messageLength);
|
|
94
|
+
offset += messageLength;
|
|
95
|
+
try {
|
|
96
|
+
const datagram = types_1.Datagram.from(messageData);
|
|
97
|
+
messages.push(datagram);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
throw new Error('Invalid datagram format');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return messages;
|
|
104
|
+
}
|
|
105
|
+
get identityKeys() {
|
|
106
|
+
return types_1.IdentityKeys.from({
|
|
107
|
+
publicKey: (0, utils_1.encodeBase64)(this.signKey.publicKey),
|
|
108
|
+
identityKey: (0, utils_1.encodeBase64)(this.boxKey.publicKey)
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
static createSecretIdentityKeys() {
|
|
112
|
+
return {
|
|
113
|
+
secretSignKey: crypto_1.default.EdDSA.keyPair().secretKey,
|
|
114
|
+
secretBoxKey: crypto_1.default.ECDH.keyPair().secretKey
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.FreeSignalAPI = FreeSignalAPI;
|
package/double-ratchet.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
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 {
|
|
19
|
+
import { EncryptedData } from "./types";
|
|
20
20
|
type ExportedKeySession = {
|
|
21
21
|
secretKey: string;
|
|
22
22
|
remoteKey: string;
|
|
@@ -98,59 +98,4 @@ export declare class KeySession {
|
|
|
98
98
|
static readonly keyLength = 32;
|
|
99
99
|
private static symmetricRatchet;
|
|
100
100
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Interface representing an encrypted payload.
|
|
103
|
-
* Provides metadata and de/serialization methods.
|
|
104
|
-
*/
|
|
105
|
-
export interface EncryptedData extends Encodable {
|
|
106
|
-
/**
|
|
107
|
-
* The length of the payload.
|
|
108
|
-
*/
|
|
109
|
-
readonly length: number;
|
|
110
|
-
/**
|
|
111
|
-
* Version of the payload.
|
|
112
|
-
*/
|
|
113
|
-
readonly version: number;
|
|
114
|
-
/**
|
|
115
|
-
* The current message count of the sending chain.
|
|
116
|
-
*/
|
|
117
|
-
readonly count: number;
|
|
118
|
-
/**
|
|
119
|
-
* The count of the previous sending chain.
|
|
120
|
-
*/
|
|
121
|
-
readonly previous: number;
|
|
122
|
-
/**
|
|
123
|
-
* The sender's public key used for this message.
|
|
124
|
-
*/
|
|
125
|
-
readonly publicKey: Uint8Array;
|
|
126
|
-
/**
|
|
127
|
-
* The nonce used during encryption.
|
|
128
|
-
*/
|
|
129
|
-
readonly nonce: Uint8Array;
|
|
130
|
-
/**
|
|
131
|
-
* The encrypted message content.
|
|
132
|
-
*/
|
|
133
|
-
readonly ciphertext: Uint8Array;
|
|
134
|
-
/**
|
|
135
|
-
* Serializes the payload into a Uint8Array for transport.
|
|
136
|
-
*/
|
|
137
|
-
encode(): Uint8Array;
|
|
138
|
-
/**
|
|
139
|
-
* Returns the payload as a Base64 string.
|
|
140
|
-
*/
|
|
141
|
-
toString(): string;
|
|
142
|
-
/**
|
|
143
|
-
* Returns the decoded object as a JSON string.
|
|
144
|
-
*/
|
|
145
|
-
toJSON(): string;
|
|
146
|
-
}
|
|
147
|
-
export declare class EncryptedData {
|
|
148
|
-
/**
|
|
149
|
-
* Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
|
|
150
|
-
*
|
|
151
|
-
* @param array - A previously serialized encrypted payload.
|
|
152
|
-
* @returns An instance of `EncryptedPayload`.
|
|
153
|
-
*/
|
|
154
|
-
static from(array: Uint8Array | EncryptedData): EncryptedData;
|
|
155
|
-
}
|
|
156
101
|
export {};
|
package/double-ratchet.js
CHANGED
|
@@ -21,9 +21,10 @@ 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.
|
|
25
|
-
const crypto_1 = __importDefault(require("
|
|
26
|
-
const utils_1 = require("
|
|
24
|
+
exports.KeySession = void 0;
|
|
25
|
+
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
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.
|
|
@@ -57,12 +58,12 @@ class KeySession {
|
|
|
57
58
|
setRemoteKey(key) {
|
|
58
59
|
this._remoteKey = key;
|
|
59
60
|
this.receivingChain = this.ratchetKeys();
|
|
60
|
-
if (this.receivingCount > (EncryptedDataConstructor.maxCount - KeySession.skipLimit * 2))
|
|
61
|
+
if (this.receivingCount > (types_1.EncryptedDataConstructor.maxCount - KeySession.skipLimit * 2))
|
|
61
62
|
this.receivingCount = 0;
|
|
62
63
|
this.previousCount = this.sendingCount;
|
|
63
64
|
this.keyPair = crypto_1.default.ECDH.keyPair();
|
|
64
65
|
this.sendingChain = this.ratchetKeys();
|
|
65
|
-
if (this.sendingCount > (EncryptedDataConstructor.maxCount - KeySession.skipLimit * 2))
|
|
66
|
+
if (this.sendingCount > (types_1.EncryptedDataConstructor.maxCount - KeySession.skipLimit * 2))
|
|
66
67
|
this.sendingCount = 0;
|
|
67
68
|
return this;
|
|
68
69
|
}
|
|
@@ -100,11 +101,11 @@ class KeySession {
|
|
|
100
101
|
*/
|
|
101
102
|
encrypt(message) {
|
|
102
103
|
const key = this.getSendingKey();
|
|
103
|
-
if (this.sendingCount >= EncryptedDataConstructor.maxCount || this.previousCount >= EncryptedDataConstructor.maxCount)
|
|
104
|
+
if (this.sendingCount >= types_1.EncryptedDataConstructor.maxCount || this.previousCount >= types_1.EncryptedDataConstructor.maxCount)
|
|
104
105
|
throw new Error();
|
|
105
|
-
const nonce = crypto_1.default.randomBytes(EncryptedDataConstructor.nonceLength);
|
|
106
|
+
const nonce = crypto_1.default.randomBytes(types_1.EncryptedDataConstructor.nonceLength);
|
|
106
107
|
const ciphertext = crypto_1.default.box.encrypt(message, nonce, key);
|
|
107
|
-
return new EncryptedDataConstructor(this.sendingCount, this.previousCount, this.keyPair.publicKey, nonce, ciphertext);
|
|
108
|
+
return new types_1.EncryptedDataConstructor(this.sendingCount, this.previousCount, this.keyPair.publicKey, nonce, ciphertext);
|
|
108
109
|
}
|
|
109
110
|
/**
|
|
110
111
|
* Decrypts an encrypted message.
|
|
@@ -114,7 +115,7 @@ class KeySession {
|
|
|
114
115
|
*/
|
|
115
116
|
decrypt(payload) {
|
|
116
117
|
var _a;
|
|
117
|
-
const encrypted = EncryptedData.from(payload);
|
|
118
|
+
const encrypted = types_1.EncryptedData.from(payload);
|
|
118
119
|
const publicKey = encrypted.publicKey;
|
|
119
120
|
if (!(0, utils_1.verifyUint8Array)(publicKey, this._remoteKey)) {
|
|
120
121
|
while (this.receivingCount < encrypted.previous)
|
|
@@ -188,94 +189,6 @@ KeySession.rootKeyLength = crypto_1.default.box.keyLength;
|
|
|
188
189
|
* Typically 32 bytes (256 bits) for symmetric keys.
|
|
189
190
|
*/
|
|
190
191
|
KeySession.keyLength = 32;
|
|
191
|
-
class EncryptedData {
|
|
192
|
-
/**
|
|
193
|
-
* Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
|
|
194
|
-
*
|
|
195
|
-
* @param array - A previously serialized encrypted payload.
|
|
196
|
-
* @returns An instance of `EncryptedPayload`.
|
|
197
|
-
*/
|
|
198
|
-
static from(array) {
|
|
199
|
-
return new EncryptedDataConstructor(array);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
exports.EncryptedData = EncryptedData;
|
|
203
|
-
class EncryptedDataConstructor {
|
|
204
|
-
constructor(...arrays) {
|
|
205
|
-
arrays = arrays.filter(value => value !== undefined);
|
|
206
|
-
if (arrays[0] instanceof EncryptedDataConstructor) {
|
|
207
|
-
this.raw = arrays[0].raw;
|
|
208
|
-
return this;
|
|
209
|
-
}
|
|
210
|
-
if (typeof arrays[0] === 'number')
|
|
211
|
-
arrays[0] = (0, utils_1.numberToUint8Array)(arrays[0], EncryptedDataConstructor.countLength);
|
|
212
|
-
if (typeof arrays[1] === 'number')
|
|
213
|
-
arrays[1] = (0, utils_1.numberToUint8Array)(arrays[1], EncryptedDataConstructor.countLength);
|
|
214
|
-
if (arrays.length === 6) {
|
|
215
|
-
arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToUint8Array)(arrays[5]) : arrays[5]);
|
|
216
|
-
arrays.pop();
|
|
217
|
-
}
|
|
218
|
-
else if (arrays.length > 1) {
|
|
219
|
-
arrays.unshift((0, utils_1.numberToUint8Array)(KeySession.version));
|
|
220
|
-
}
|
|
221
|
-
this.raw = (0, utils_1.concatUint8Array)(...arrays);
|
|
222
|
-
}
|
|
223
|
-
get length() { return this.raw.length; }
|
|
224
|
-
get version() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
|
|
225
|
-
get count() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
|
|
226
|
-
get previous() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
|
|
227
|
-
get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
|
|
228
|
-
get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
|
|
229
|
-
get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
|
|
230
|
-
encode() {
|
|
231
|
-
return this.raw;
|
|
232
|
-
}
|
|
233
|
-
decode() {
|
|
234
|
-
return {
|
|
235
|
-
version: this.version,
|
|
236
|
-
count: this.count,
|
|
237
|
-
previous: this.previous,
|
|
238
|
-
publicKey: (0, utils_1.encodeBase64)(this.publicKey),
|
|
239
|
-
nonce: (0, utils_1.encodeBase64)(this.nonce),
|
|
240
|
-
ciphertext: (0, utils_1.encodeBase64)(this.ciphertext)
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
toString() {
|
|
244
|
-
return (0, utils_1.encodeBase64)(this.raw);
|
|
245
|
-
}
|
|
246
|
-
toJSON() {
|
|
247
|
-
return JSON.stringify(this.decode());
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
EncryptedDataConstructor.secretKeyLength = crypto_1.default.ECDH.secretKeyLength;
|
|
251
|
-
EncryptedDataConstructor.publicKeyLength = crypto_1.default.ECDH.publicKeyLength;
|
|
252
|
-
EncryptedDataConstructor.keyLength = crypto_1.default.box.keyLength;
|
|
253
|
-
EncryptedDataConstructor.nonceLength = crypto_1.default.box.nonceLength;
|
|
254
|
-
EncryptedDataConstructor.maxCount = 65536; //32768;
|
|
255
|
-
EncryptedDataConstructor.countLength = 2;
|
|
256
|
-
class Offsets {
|
|
257
|
-
static set(start, length) {
|
|
258
|
-
class Offset {
|
|
259
|
-
constructor(start, length) {
|
|
260
|
-
this.start = start;
|
|
261
|
-
this.length = length;
|
|
262
|
-
if (typeof length === 'number')
|
|
263
|
-
this.end = start + length;
|
|
264
|
-
}
|
|
265
|
-
get get() {
|
|
266
|
-
return [this.start, this.length];
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return new Offset(start, length);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
Offsets.checksum = Offsets.set(0, 0);
|
|
273
|
-
Offsets.version = Offsets.set(Offsets.checksum.end, 1);
|
|
274
|
-
Offsets.count = Offsets.set(Offsets.version.end, EncryptedDataConstructor.countLength);
|
|
275
|
-
Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.countLength);
|
|
276
|
-
Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
|
|
277
|
-
Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
|
|
278
|
-
Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
|
|
279
192
|
class KeyMap extends Map {
|
|
280
193
|
get(key) {
|
|
281
194
|
const out = super.get(key);
|
package/index.d.ts
CHANGED
|
@@ -16,10 +16,12 @@
|
|
|
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 crypto from "
|
|
20
|
-
import { LocalStorage } from "
|
|
19
|
+
import crypto from "@freesignal/crypto";
|
|
20
|
+
import { LocalStorage, Crypto } from "@freesignal/interfaces";
|
|
21
21
|
import { KeySession } from "./double-ratchet";
|
|
22
22
|
import { KeyExchange } from "./x3dh";
|
|
23
|
+
import { IdentityKeys, UserId } from "./types";
|
|
24
|
+
import { FreeSignalAPI } from "./api";
|
|
23
25
|
/**
|
|
24
26
|
* Creates a new Double Ratchet session.
|
|
25
27
|
*
|
|
@@ -41,6 +43,11 @@ export declare function createKeySession(opts?: {
|
|
|
41
43
|
* @returns A new X3DH session.
|
|
42
44
|
*/
|
|
43
45
|
export declare function createKeyExchange(signSecretKey: Uint8Array, boxSecretKey: Uint8Array, bundleStore?: LocalStorage<string, crypto.KeyPair>): KeyExchange;
|
|
44
|
-
export
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
export declare function createAPI(opts: {
|
|
47
|
+
secretSignKey: Uint8Array;
|
|
48
|
+
secretBoxKey: Uint8Array;
|
|
49
|
+
sessions: LocalStorage<UserId, KeySession>;
|
|
50
|
+
keyExchange: LocalStorage<string, Crypto.KeyPair>;
|
|
51
|
+
users: LocalStorage<UserId, IdentityKeys>;
|
|
52
|
+
}): FreeSignalAPI;
|
|
53
|
+
export { IdentityKeys, Protocols, EncryptedData, Datagram } from "./types";
|
package/index.js
CHANGED
|
@@ -17,26 +17,14 @@
|
|
|
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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
-
if (k2 === undefined) k2 = k;
|
|
22
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
-
}
|
|
26
|
-
Object.defineProperty(o, k2, desc);
|
|
27
|
-
}) : (function(o, m, k, k2) {
|
|
28
|
-
if (k2 === undefined) k2 = k;
|
|
29
|
-
o[k2] = m[k];
|
|
30
|
-
}));
|
|
31
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
32
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
33
|
-
};
|
|
34
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.EncryptedData = exports.
|
|
21
|
+
exports.Datagram = exports.EncryptedData = exports.Protocols = exports.IdentityKeys = void 0;
|
|
36
22
|
exports.createKeySession = createKeySession;
|
|
37
23
|
exports.createKeyExchange = createKeyExchange;
|
|
24
|
+
exports.createAPI = createAPI;
|
|
38
25
|
const double_ratchet_1 = require("./double-ratchet");
|
|
39
26
|
const x3dh_1 = require("./x3dh");
|
|
27
|
+
const api_1 = require("./api");
|
|
40
28
|
/**
|
|
41
29
|
* Creates a new Double Ratchet session.
|
|
42
30
|
*
|
|
@@ -58,8 +46,11 @@ function createKeySession(opts) {
|
|
|
58
46
|
function createKeyExchange(signSecretKey, boxSecretKey, bundleStore) {
|
|
59
47
|
return new x3dh_1.KeyExchange(signSecretKey, boxSecretKey, bundleStore);
|
|
60
48
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
var
|
|
65
|
-
Object.defineProperty(exports, "
|
|
49
|
+
function createAPI(opts) {
|
|
50
|
+
return new api_1.FreeSignalAPI(opts);
|
|
51
|
+
}
|
|
52
|
+
var types_1 = require("./types");
|
|
53
|
+
Object.defineProperty(exports, "IdentityKeys", { enumerable: true, get: function () { return types_1.IdentityKeys; } });
|
|
54
|
+
Object.defineProperty(exports, "Protocols", { enumerable: true, get: function () { return types_1.Protocols; } });
|
|
55
|
+
Object.defineProperty(exports, "EncryptedData", { enumerable: true, get: function () { return types_1.EncryptedData; } });
|
|
56
|
+
Object.defineProperty(exports, "Datagram", { enumerable: true, get: function () { return types_1.Datagram; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freesignal/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Signal Protocol implementation in javascript",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"author": "Christian Braghette",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"build": "tsc && node ./build/build.js"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
+
"@freesignal/crypto": "^0.3.0",
|
|
15
|
+
"@freesignal/interfaces": "^0.1.1",
|
|
16
|
+
"@freesignal/utils": "^1.0.2",
|
|
14
17
|
"base64-js": "^1.5.1",
|
|
15
18
|
"fflate": "^0.8.2",
|
|
16
19
|
"js-sha3": "^0.9.3",
|
package/test.js
CHANGED
|
@@ -4,10 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const _1 = require(".");
|
|
7
|
-
const crypto_1 = __importDefault(require("
|
|
8
|
-
const
|
|
9
|
-
const utils_1 = require("./utils");
|
|
10
|
-
const types_1 = require("./types");
|
|
7
|
+
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
8
|
+
const utils_1 = require("@freesignal/utils");
|
|
11
9
|
const bob = (0, _1.createKeyExchange)(crypto_1.default.EdDSA.keyPair().secretKey, crypto_1.default.ECDH.keyPair().secretKey);
|
|
12
10
|
const alice = (0, _1.createKeyExchange)(crypto_1.default.EdDSA.keyPair().secretKey, crypto_1.default.ECDH.keyPair().secretKey);
|
|
13
11
|
const bobmessage = bob.generateData();
|
|
@@ -16,15 +14,15 @@ bob.digestMessage(aliceack).then(({ session: bobsession, cleartext }) => {
|
|
|
16
14
|
var _a;
|
|
17
15
|
if (bobsession && cleartext) {
|
|
18
16
|
console.log("Session established successfully between Alice and Bob.");
|
|
19
|
-
const datagram =
|
|
17
|
+
const datagram = _1.Datagram.create(bob.signatureKey, alice.signatureKey, _1.Protocols.MESSAGE, (_a = bobsession.encrypt((0, utils_1.decodeUTF8)("Hi Alice!"))) === null || _a === void 0 ? void 0 : _a.encode());
|
|
20
18
|
//console.log(datagram.payload);
|
|
21
19
|
const msg = datagram.encode();
|
|
22
|
-
console.log((0, utils_1.encodeUTF8)(alicesession.decrypt(
|
|
20
|
+
console.log((0, utils_1.encodeUTF8)(alicesession.decrypt(_1.Datagram.from(msg).payload)));
|
|
23
21
|
if (alicesession.handshaked && bobsession.handshaked)
|
|
24
22
|
console.log("Successfully handshaked");
|
|
25
23
|
else
|
|
26
24
|
console.log("Error during handshake");
|
|
27
|
-
const longmsg =
|
|
25
|
+
const longmsg = _1.Datagram.create(alice.signatureKey, bob.signatureKey, _1.Protocols.MESSAGE, alicesession.encrypt(new Uint8Array(1000000).fill(33).map(val => val + Math.floor(Math.random() * 93))));
|
|
28
26
|
console.log(longmsg.encode().length);
|
|
29
27
|
console.log(longmsg.encode(false).length);
|
|
30
28
|
}
|