@freesignal/protocol 0.4.7 → 0.5.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.
@@ -51,8 +51,6 @@ export declare class KeySession {
51
51
  id?: string;
52
52
  secretKey?: Uint8Array;
53
53
  remoteKey?: Uint8Array;
54
- headerKey?: Uint8Array;
55
- nextHeaderKey?: Uint8Array;
56
54
  rootKey?: Uint8Array;
57
55
  });
58
56
  private getChain;
@@ -29,6 +29,7 @@ const utils_1 = require("@freesignal/utils");
29
29
  * Used for forward-secure encryption and decryption of messages.
30
30
  */
31
31
  class KeySession {
32
+ //headerKey?: Uint8Array, nextHeaderKey?: Uint8Array,
32
33
  constructor(opts = {}) {
33
34
  var _a;
34
35
  this.previousKeys = new KeyMap();
@@ -36,10 +37,10 @@ class KeySession {
36
37
  this.keyPair = crypto_1.default.ECDH.keyPair(opts.secretKey);
37
38
  if (opts.rootKey)
38
39
  this.rootKey = opts.rootKey;
39
- if (opts.nextHeaderKey)
40
- this.nextHeaderKey = opts.nextHeaderKey;
40
+ //if (opts.nextHeaderKey)
41
+ // this.nextHeaderKey = opts.nextHeaderKey;
41
42
  if (opts.remoteKey) {
42
- this.sendingChain = this.getChain(opts.remoteKey, opts.headerKey);
43
+ this.sendingChain = this.getChain(opts.remoteKey); //, opts.headerKey);
43
44
  }
44
45
  }
45
46
  getChain(remoteKey, headerKey, previousCount) {
@@ -51,10 +52,10 @@ class KeySession {
51
52
  return new KeyChain(this.publicKey, remoteKey, hashkey.subarray(KeySession.keyLength, KeySession.keyLength * 2), hashkey.subarray(KeySession.keyLength * 2), headerKey, previousCount);
52
53
  }
53
54
  getHeaderKeys() {
54
- var _a, _b, _c;
55
+ var _a, _b, _c, _d, _e;
55
56
  return {
56
57
  sending: (_a = this.sendingChain) === null || _a === void 0 ? void 0 : _a.headerKey,
57
- receiving: (_b = this.nextHeaderKey) !== null && _b !== void 0 ? _b : (_c = this.receivingChain) === null || _c === void 0 ? void 0 : _c.headerKey
58
+ receiving: (_e = ((_c = (_b = this.receivingChain) === null || _b === void 0 ? void 0 : _b.headerKey) !== null && _c !== void 0 ? _c : (_d = this.receivingChain) === null || _d === void 0 ? void 0 : _d.nextHeaderKey)) !== null && _e !== void 0 ? _e : this.nextHeaderKey
58
59
  };
59
60
  }
60
61
  getSendingKey() {
package/dist/node.js CHANGED
@@ -241,6 +241,8 @@ class FreeSignalNode {
241
241
  if (bootstrap)
242
242
  out.datagram = yield this.packHandshake(bootstrap);
243
243
  return out;
244
+ case types_1.Protocols.NULL:
245
+ return out;
244
246
  default:
245
247
  throw new Error("Invalid protocol");
246
248
  }
package/dist/test.js CHANGED
@@ -18,7 +18,7 @@ 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
- console.log(_1.Datagram.from(test).toJSON());
21
+ console.log("Valid bootstrap: ", !!test);
22
22
  const bobRequest = yield alice.getRequest(bob.userId.toString());
23
23
  if (!bobRequest)
24
24
  throw new Error("Bootstrap Failed");
@@ -34,6 +34,10 @@ setImmediate(() => __awaiter(void 0, void 0, void 0, function* () {
34
34
  }));
35
35
  const fourth = yield alice.packData(bob.userId, "Not so bad my man");
36
36
  console.log("Alice: ", (0, utils_1.decodeData)((yield bob.open(fourth)).payload));
37
+ const fifth = yield Promise.all(["I'm thinking...", "His this secure?"].map(msg => bob.packData(alice.userId, msg)));
38
+ fifth.forEach((data) => __awaiter(void 0, void 0, void 0, function* () {
39
+ console.log("Bob: ", (0, utils_1.decodeData)((yield alice.open(data)).payload));
40
+ }));
37
41
  //const testone = await Promise.all(Array(400).fill(0).map(() => alice.packData(bob.userId, decodeBase64(crypto.randomBytes(64)))));
38
42
  //console.log(((await bob.open(testone[350])).payload));
39
43
  }));
package/dist/types.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * along with this program. If not, see <https://www.gnu.org/licenses/>
18
18
  */
19
19
  import { LocalStorage, Encodable, KeyExchangeData } from "@freesignal/interfaces";
20
- import { KeySession } from "./double-ratchet";
20
+ import { EncryptionKeys, KeySession } from "./double-ratchet";
21
21
  export declare function encryptData(session: KeySession, data: Uint8Array): EncryptedData;
22
22
  export declare function decryptData(session: KeySession, encryptedData: Uint8Array): Uint8Array;
23
23
  export declare class UserId implements Encodable {
@@ -130,67 +130,41 @@ export declare class Datagram implements Encodable, DatagramHeader {
130
130
  static verify(datagram: Datagram, publicKey: Uint8Array): boolean;
131
131
  static from(data: Uint8Array | Datagram | string): Datagram;
132
132
  }
133
- /**
134
- * Interface representing an encrypted payload.
135
- * Provides metadata and de/serialization methods.
136
- */
137
- export interface EncryptedData extends Encodable {
138
- /**
139
- * The length of the payload.
140
- */
141
- readonly length: number;
142
- /**
143
- * Version of the payload.
144
- */
145
- readonly version: number;
146
- /**
147
- * The current message count of the sending chain.
148
- */
133
+ export declare class EncryptionHeader implements EncryptionKeys, Encodable {
134
+ readonly nonce: Uint8Array;
135
+ static readonly keyLength: number;
136
+ static readonly nonceLength: number;
137
+ static readonly countLength = 2;
149
138
  readonly count: number;
150
- /**
151
- * The count of the previous sending chain.
152
- */
153
139
  readonly previous: number;
154
- /**
155
- * The sender's public key used for this message.
156
- */
157
140
  readonly publicKey: Uint8Array;
158
- /**
159
- * The nonce used during encryption.
160
- */
161
- readonly nonce: Uint8Array;
162
- /**
163
- * The encrypted message content.
164
- */
165
- readonly ciphertext: Uint8Array;
166
- /**
167
- * Serializes the payload into a Uint8Array for transport.
168
- */
141
+ constructor(keys: EncryptionKeys, nonce: Uint8Array);
169
142
  toBytes(): Uint8Array;
170
- /**
171
- * Returns the payload as a Base64 string.
172
- */
173
- toString(): string;
174
- /**
175
- * Returns the decoded object as a JSON string.
176
- */
177
143
  toJSON(): {
178
- version: number;
179
144
  count: number;
180
145
  previous: number;
181
146
  publicKey: string;
182
- nonce: string;
183
- ciphertext: string;
184
147
  };
148
+ static from(data: Uint8Array | EncryptionHeader): EncryptionHeader;
185
149
  }
186
- export declare namespace EncryptedData {
187
- /**
188
- * Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
189
- *
190
- * @param array - A previously serialized encrypted payload.
191
- * @returns An instance of `EncryptedPayload`.
192
- */
193
- function from(array: Uint8Array | EncryptedData): EncryptedData;
150
+ export declare class EncryptedData implements Encodable {
151
+ readonly header: Uint8Array;
152
+ readonly nonce: Uint8Array;
153
+ readonly payload: Uint8Array;
154
+ static readonly version = 1;
155
+ static readonly nonceLength: number;
156
+ private _version;
157
+ constructor(header: Uint8Array, nonce: Uint8Array, payload: Uint8Array);
158
+ get version(): number;
159
+ get length(): number;
160
+ toBytes(): Uint8Array;
161
+ toJSON(): {
162
+ version: number;
163
+ header: string;
164
+ nonce: string;
165
+ payload: string;
166
+ };
167
+ static from(data: Uint8Array | EncryptedData): EncryptedData;
194
168
  }
195
169
  export declare class AsyncMap<K, V> implements LocalStorage<K, V> {
196
170
  private readonly map;
package/dist/types.js CHANGED
@@ -30,26 +30,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
30
30
  return (mod && mod.__esModule) ? mod : { "default": mod };
31
31
  };
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
- exports.AsyncMap = exports.EncryptedData = exports.Datagram = exports.DatagramHeader = exports.Protocols = exports.DiscoverType = exports.PrivateIdentityKey = exports.IdentityKey = exports.UserId = void 0;
33
+ exports.AsyncMap = exports.EncryptedData = exports.EncryptionHeader = exports.Datagram = exports.DatagramHeader = exports.Protocols = exports.DiscoverType = exports.PrivateIdentityKey = exports.IdentityKey = exports.UserId = void 0;
34
34
  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
- const double_ratchet_1 = require("./double-ratchet");
39
38
  function encryptData(session, data) {
39
+ //console.log(session.id, ' Sending: ', decodeBase64(session.getHeaderKeys().sending ?? new Uint8Array()));
40
40
  const key = session.getSendingKey();
41
41
  if (!key)
42
42
  throw new Error("Error generating key");
43
- const nonce = crypto_1.default.randomBytes(EncryptedDataConstructor.nonceLength);
43
+ const nonce = crypto_1.default.randomBytes(EncryptionHeader.nonceLength);
44
44
  const ciphertext = crypto_1.default.box.encrypt(data, nonce, key.secretKey);
45
- return new EncryptedDataConstructor(key.count, key.previous, key.publicKey, nonce, ciphertext);
45
+ const headerKey = session.getHeaderKeys().sending;
46
+ let header = new EncryptionHeader(key, nonce).toBytes();
47
+ const headerNonce = crypto_1.default.randomBytes(EncryptionHeader.nonceLength);
48
+ if (headerKey)
49
+ header = crypto_1.default.box.encrypt(header, headerNonce, headerKey);
50
+ const test = new EncryptedData(header, headerNonce, ciphertext);
51
+ return test;
46
52
  }
47
53
  function decryptData(session, encryptedData) {
54
+ //console.log(session.id, ' Receiving: ', decodeBase64(session.getHeaderKeys().receiving ?? new Uint8Array()));
48
55
  const encrypted = EncryptedData.from(encryptedData);
49
- const key = session.getReceivingKey(encrypted);
56
+ const headerKey = session.getHeaderKeys().receiving;
57
+ let headerData = encrypted.header;
58
+ if (headerKey) {
59
+ headerData = crypto_1.default.box.decrypt(headerData, encrypted.nonce, headerKey);
60
+ if (!headerData)
61
+ throw new Error("Error calculating header");
62
+ }
63
+ const header = EncryptionHeader.from(headerData);
64
+ const key = session.getReceivingKey(header);
50
65
  if (!key)
51
66
  throw new Error("Error calculating key");
52
- const decrypted = crypto_1.default.box.decrypt(encrypted.ciphertext, encrypted.nonce, key);
67
+ const decrypted = crypto_1.default.box.decrypt(encrypted.payload, header.nonce, key);
53
68
  if (!decrypted)
54
69
  throw new Error("Error decrypting data");
55
70
  return decrypted;
@@ -343,91 +358,73 @@ class Datagram {
343
358
  exports.Datagram = Datagram;
344
359
  Datagram.version = 1;
345
360
  Datagram.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
346
- var EncryptedData;
347
- (function (EncryptedData) {
348
- /**
349
- * Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
350
- *
351
- * @param array - A previously serialized encrypted payload.
352
- * @returns An instance of `EncryptedPayload`.
353
- */
354
- function from(array) {
355
- return new EncryptedDataConstructor(array);
356
- }
357
- EncryptedData.from = from;
358
- })(EncryptedData || (exports.EncryptedData = EncryptedData = {}));
359
- class EncryptedDataConstructor {
360
- constructor(...arrays) {
361
- arrays = arrays.filter(value => value !== undefined);
362
- if (arrays[0] instanceof EncryptedDataConstructor) {
363
- this.raw = arrays[0].raw;
364
- return this;
365
- }
366
- if (typeof arrays[0] === 'number')
367
- arrays[0] = (0, utils_1.numberToBytes)(arrays[0], EncryptedDataConstructor.countLength);
368
- if (typeof arrays[1] === 'number')
369
- arrays[1] = (0, utils_1.numberToBytes)(arrays[1], EncryptedDataConstructor.countLength);
370
- if (arrays.length === 6) {
371
- arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToBytes)(arrays[5], 1) : arrays[5]);
372
- arrays.pop();
373
- }
374
- else if (arrays.length > 1) {
375
- arrays.unshift((0, utils_1.numberToBytes)(double_ratchet_1.KeySession.version, 1));
376
- }
377
- this.raw = (0, utils_1.concatBytes)(...arrays);
378
- }
379
- get length() { return this.raw.length; }
380
- get version() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
381
- get count() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
382
- get previous() { return (0, utils_1.bytesToNumber)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
383
- get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
384
- get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
385
- get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
386
- toBytes() {
387
- return this.raw;
361
+ class EncryptionHeader {
362
+ constructor(keys, nonce) {
363
+ this.nonce = nonce;
364
+ this.count = keys.count;
365
+ this.previous = keys.previous;
366
+ this.publicKey = keys.publicKey;
388
367
  }
389
- toString() {
390
- return (0, utils_1.decodeBase64)(this.raw);
368
+ toBytes() {
369
+ return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.count, EncryptionHeader.countLength), (0, utils_1.numberToBytes)(this.previous, EncryptionHeader.countLength), this.publicKey, this.nonce);
391
370
  }
392
371
  toJSON() {
393
372
  return {
394
- version: this.version,
395
373
  count: this.count,
396
374
  previous: this.previous,
397
- publicKey: (0, utils_1.decodeBase64)(this.publicKey),
398
- nonce: (0, utils_1.decodeBase64)(this.nonce),
399
- ciphertext: (0, utils_1.decodeBase64)(this.ciphertext)
375
+ publicKey: (0, utils_1.decodeBase64)(this.publicKey)
400
376
  };
401
377
  }
378
+ static from(data) {
379
+ if (data instanceof EncryptionHeader)
380
+ data = data.toBytes();
381
+ return new EncryptionHeader({
382
+ count: (0, utils_1.bytesToNumber)(data.subarray(0, EncryptionHeader.countLength)),
383
+ previous: (0, utils_1.bytesToNumber)(data.subarray(EncryptionHeader.countLength, EncryptionHeader.countLength * 2)),
384
+ publicKey: data.subarray(EncryptionHeader.countLength * 2, EncryptionHeader.countLength * 2 + EncryptionHeader.keyLength)
385
+ }, data.subarray(EncryptionHeader.countLength * 2 + EncryptionHeader.keyLength, EncryptionHeader.countLength * 2 + EncryptionHeader.keyLength + EncryptedData.nonceLength));
386
+ }
402
387
  }
403
- EncryptedDataConstructor.secretKeyLength = crypto_1.default.ECDH.secretKeyLength;
404
- EncryptedDataConstructor.publicKeyLength = crypto_1.default.ECDH.publicKeyLength;
405
- EncryptedDataConstructor.keyLength = crypto_1.default.box.keyLength;
406
- EncryptedDataConstructor.nonceLength = crypto_1.default.box.nonceLength;
407
- EncryptedDataConstructor.countLength = 2;
408
- class Offsets {
409
- static set(start, length) {
410
- class Offset {
411
- constructor(start, length) {
412
- this.start = start;
413
- this.length = length;
414
- if (typeof length === 'number')
415
- this.end = start + length;
416
- }
417
- get get() {
418
- return [this.start, this.length];
419
- }
420
- }
421
- return new Offset(start, length);
388
+ exports.EncryptionHeader = EncryptionHeader;
389
+ EncryptionHeader.keyLength = crypto_1.default.box.keyLength;
390
+ EncryptionHeader.nonceLength = crypto_1.default.box.nonceLength;
391
+ EncryptionHeader.countLength = 2;
392
+ class EncryptedData {
393
+ constructor(header, nonce, payload) {
394
+ this.header = header;
395
+ this.nonce = nonce;
396
+ this.payload = payload;
397
+ this._version = EncryptedData.version;
398
+ }
399
+ get version() {
400
+ return this._version;
401
+ }
402
+ get length() {
403
+ return this.toBytes().length;
404
+ }
405
+ toBytes() {
406
+ return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this._version, 1), (0, utils_1.numberToBytes)(this.header.length, 3), this.header, this.nonce, this.payload);
407
+ }
408
+ toJSON() {
409
+ return {
410
+ version: this._version,
411
+ header: (0, utils_1.decodeBase64)(this.header),
412
+ nonce: (0, utils_1.decodeBase64)(this.nonce),
413
+ payload: (0, utils_1.decodeBase64)(this.payload)
414
+ };
415
+ }
416
+ static from(data) {
417
+ if (data instanceof EncryptedData)
418
+ data = data.toBytes();
419
+ const headerLength = (0, utils_1.bytesToNumber)(data.subarray(1, 4));
420
+ const obj = new EncryptedData(data.subarray(4, 4 + headerLength), data.subarray(4 + headerLength, 4 + headerLength + this.nonceLength), data.subarray(4 + headerLength + this.nonceLength));
421
+ obj._version = (0, utils_1.bytesToNumber)(data.subarray(0, 1));
422
+ return obj;
422
423
  }
423
424
  }
424
- Offsets.checksum = Offsets.set(0, 0);
425
- Offsets.version = Offsets.set(Offsets.checksum.end, 1);
426
- Offsets.count = Offsets.set(Offsets.version.end, EncryptedDataConstructor.countLength);
427
- Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.countLength);
428
- Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
429
- Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
430
- Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
425
+ exports.EncryptedData = EncryptedData;
426
+ EncryptedData.version = 1;
427
+ EncryptedData.nonceLength = crypto_1.default.box.nonceLength;
431
428
  class AsyncMap {
432
429
  constructor(iterable) {
433
430
  this.map = new Map(iterable);
package/dist/x3dh.js CHANGED
@@ -92,13 +92,14 @@ class KeyExchange {
92
92
  const onetimePreKey = message.onetimePreKey ? (0, utils_1.encodeBase64)(message.onetimePreKey) : undefined;
93
93
  const signedPreKeyHash = crypto_1.default.hash(signedPreKey);
94
94
  const onetimePreKeyHash = onetimePreKey ? crypto_1.default.hash(onetimePreKey) : new Uint8Array();
95
- const rootKey = crypto_1.default.hkdf(new Uint8Array([
95
+ const derivedKey = crypto_1.default.hkdf(new Uint8Array([
96
96
  ...crypto_1.default.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, signedPreKey),
97
97
  ...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, identityKey.exchangeKey),
98
98
  ...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, signedPreKey),
99
99
  ...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
- const session = new double_ratchet_1.KeySession({ remoteKey: identityKey.exchangeKey, rootKey });
100
+ ]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 2);
101
+ //, headerKey: derivedKey.subarray(KeySession.keyLength)
102
+ const session = new double_ratchet_1.KeySession({ remoteKey: identityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
102
103
  const encrypted = (0, types_1.encryptData)(session, (0, utils_1.concatBytes)(crypto_1.default.hash(this.identityKey.toBytes()), crypto_1.default.hash(identityKey.toBytes()), associatedData !== null && associatedData !== void 0 ? associatedData : new Uint8Array()));
103
104
  if (!encrypted)
104
105
  throw new Error("Decryption error");
@@ -127,13 +128,14 @@ class KeyExchange {
127
128
  if (!this.storage.delete(hash))
128
129
  throw new Error("Bundle store deleting error");
129
130
  const ephemeralKey = (0, utils_1.encodeBase64)(message.ephemeralKey);
130
- const rootKey = crypto_1.default.hkdf(new Uint8Array([
131
+ const derivedKey = crypto_1.default.hkdf(new Uint8Array([
131
132
  ...crypto_1.default.ECDH.scalarMult(signedPreKey.secretKey, identityKey.exchangeKey),
132
133
  ...crypto_1.default.ECDH.scalarMult(this.privateIdentityKey.exchangeKey, ephemeralKey),
133
134
  ...crypto_1.default.ECDH.scalarMult(signedPreKey.secretKey, ephemeralKey),
134
135
  ...onetimePreKey ? crypto_1.default.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
135
- ]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength);
136
- const session = new double_ratchet_1.KeySession({ secretKey: this.privateIdentityKey.exchangeKey, rootKey });
136
+ ]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 2);
137
+ //, nextHeaderKey: derivedKey.subarray(KeySession.keyLength)
138
+ const session = new double_ratchet_1.KeySession({ secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
137
139
  const data = (0, types_1.decryptData)(session, (0, utils_1.encodeBase64)(message.associatedData));
138
140
  if (!data)
139
141
  throw new Error("Error decrypting ACK message");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freesignal/protocol",
3
- "version": "0.4.7",
3
+ "version": "0.5.1",
4
4
  "description": "Signal Protocol implementation in javascript",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "author": "Christian Braghette",