@freesignal/protocol 0.1.5 → 0.1.7

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/crypto.d.ts CHANGED
@@ -16,61 +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
- export type HashAlgorithms = 'sha224' | 'sha256' | 'sha384' | 'sha512';
20
- export type HmacAlgorithms = 'kmac128' | 'kmac256';
21
- interface UUIDv4 {
22
- toString(): string;
23
- toJSON(): string;
24
- toBuffer(): Uint8Array;
25
- }
26
- export interface Crypto {
27
- hash(message: Uint8Array, algorithm?: HashAlgorithms): Uint8Array;
28
- hmac(key: Uint8Array, message: Uint8Array, length?: number, algorithm?: HmacAlgorithms): Uint8Array;
29
- hkdf(key: Uint8Array, salt: Uint8Array, info?: Uint8Array | string, length?: number): Uint8Array;
30
- readonly KeyPair: typeof Crypto.KeyPair;
31
- readonly box: Crypto.box;
32
- readonly ECDH: Crypto.ECDH;
33
- readonly EdDSA: Crypto.EdDSA;
34
- readonly UUID: Crypto.UUID;
35
- randomBytes(n: number): Uint8Array;
36
- scalarMult(n: Uint8Array, p: Uint8Array): Uint8Array;
37
- }
38
- export declare namespace Crypto {
39
- type KeyPair = {
40
- readonly publicKey: Uint8Array;
41
- readonly secretKey: Uint8Array;
42
- };
43
- namespace KeyPair {
44
- function isKeyPair(obj: any): boolean;
45
- }
46
- interface box {
47
- readonly keyLength: number;
48
- readonly nonceLength: number;
49
- encrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
50
- decrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | undefined;
51
- }
52
- interface ECDH {
53
- readonly publicKeyLength: number;
54
- readonly secretKeyLength: number;
55
- keyPair(secretKey?: Uint8Array): KeyPair;
56
- sharedKey(publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
57
- }
58
- interface EdDSA {
59
- readonly publicKeyLength: number;
60
- readonly secretKeyLength: number;
61
- readonly signatureLength: number;
62
- readonly seedLength: number;
63
- keyPair(secretKey?: Uint8Array): KeyPair;
64
- keyPairFromSeed(seed: Uint8Array): KeyPair;
65
- sign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
66
- verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): boolean;
67
- }
68
- interface UUID {
69
- generate(): UUIDv4;
70
- stringify(arr: Uint8Array, offset?: number): string;
71
- parse(uuid: string): Uint8Array;
72
- }
73
- }
19
+ import { Crypto } from './types';
74
20
  declare const crypto: Crypto;
75
21
  declare namespace crypto {
76
22
  type KeyPair = Crypto.KeyPair;
package/crypto.js CHANGED
@@ -21,14 +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.Crypto = void 0;
25
24
  const js_sha3_1 = require("js-sha3");
26
25
  const tweetnacl_1 = __importDefault(require("tweetnacl"));
27
26
  const uuid_1 = require("uuid");
28
27
  const utils_1 = require("./utils");
29
- var Crypto;
30
- (function (Crypto) {
31
- })(Crypto || (exports.Crypto = Crypto = {}));
32
28
  class CryptoConstructor {
33
29
  constructor() {
34
30
  this.KeyPair = {
package/data.d.ts CHANGED
@@ -44,7 +44,7 @@ export declare namespace Datagram {
44
44
  const version = 1;
45
45
  function create(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable): DatagramConstructor;
46
46
  function isDatagram(obj: any): boolean;
47
- function from(data: Uint8Array): DatagramConstructor;
47
+ function from(data: Uint8Array | Datagram | string): DatagramConstructor;
48
48
  }
49
49
  declare class DatagramConstructor implements Encodable, Datagram {
50
50
  readonly id: string;
package/data.js CHANGED
@@ -70,7 +70,12 @@ var Datagram;
70
70
  }
71
71
  Datagram.isDatagram = isDatagram;
72
72
  function from(data) {
73
- return new DatagramConstructor(data);
73
+ if (typeof data === 'string') {
74
+ const decoded = (0, utils_1.decodeBase64)(data);
75
+ return new DatagramConstructor(decoded);
76
+ }
77
+ else
78
+ return new DatagramConstructor(data);
74
79
  }
75
80
  Datagram.from = from;
76
81
  })(Datagram || (exports.Datagram = Datagram = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freesignal/protocol",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Signal Protocol implementation in javascript",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "author": "Christian Braghette",
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
- const { session: bobsession, cleartext } = (_a = bob.digestMessage(aliceack)) !== null && _a !== void 0 ? _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, (_b = bobsession.encrypt((0, utils_1.decodeUTF8)("Hi Alice!"))) === null || _b === void 0 ? void 0 : _b.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");
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 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
- }
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;
@@ -58,4 +58,59 @@ export interface KeyExchangeDataBundle {
58
58
  readonly signature: string;
59
59
  readonly onetimePreKey: string[];
60
60
  }
61
+ interface UUIDv4 {
62
+ toString(): string;
63
+ toJSON(): string;
64
+ toBuffer(): Uint8Array;
65
+ }
66
+ export interface Crypto {
67
+ hash(message: Uint8Array, algorithm?: Crypto.HashAlgorithms): Uint8Array;
68
+ hmac(key: Uint8Array, message: Uint8Array, length?: number, algorithm?: Crypto.HmacAlgorithms): Uint8Array;
69
+ hkdf(key: Uint8Array, salt: Uint8Array, info?: Uint8Array | string, length?: number): Uint8Array;
70
+ readonly KeyPair: typeof Crypto.KeyPair;
71
+ readonly box: Crypto.box;
72
+ readonly ECDH: Crypto.ECDH;
73
+ readonly EdDSA: Crypto.EdDSA;
74
+ readonly UUID: Crypto.UUID;
75
+ randomBytes(n: number): Uint8Array;
76
+ scalarMult(n: Uint8Array, p: Uint8Array): Uint8Array;
77
+ }
78
+ export declare namespace Crypto {
79
+ type HashAlgorithms = 'sha224' | 'sha256' | 'sha384' | 'sha512';
80
+ type HmacAlgorithms = 'kmac128' | 'kmac256';
81
+ type KeyPair = {
82
+ readonly publicKey: Uint8Array;
83
+ readonly secretKey: Uint8Array;
84
+ };
85
+ namespace KeyPair {
86
+ function isKeyPair(obj: any): boolean;
87
+ }
88
+ interface box {
89
+ readonly keyLength: number;
90
+ readonly nonceLength: number;
91
+ encrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
92
+ decrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | undefined;
93
+ }
94
+ interface ECDH {
95
+ readonly publicKeyLength: number;
96
+ readonly secretKeyLength: number;
97
+ keyPair(secretKey?: Uint8Array): KeyPair;
98
+ sharedKey(publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
99
+ }
100
+ interface EdDSA {
101
+ readonly publicKeyLength: number;
102
+ readonly secretKeyLength: number;
103
+ readonly signatureLength: number;
104
+ readonly seedLength: number;
105
+ keyPair(secretKey?: Uint8Array): KeyPair;
106
+ keyPairFromSeed(seed: Uint8Array): KeyPair;
107
+ sign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
108
+ verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): boolean;
109
+ }
110
+ interface UUID {
111
+ generate(): UUIDv4;
112
+ stringify(arr: Uint8Array, offset?: number): string;
113
+ parse(uuid: string): Uint8Array;
114
+ }
115
+ }
61
116
  export {};
package/types.js CHANGED
@@ -18,7 +18,7 @@
18
18
  * along with this program. If not, see <https://www.gnu.org/licenses/>
19
19
  */
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.Encodable = void 0;
21
+ exports.Crypto = exports.Encodable = void 0;
22
22
  var Encodable;
23
23
  (function (Encodable) {
24
24
  const properties = ['encode', 'toString', 'toJSON'];
@@ -27,3 +27,6 @@ var Encodable;
27
27
  }
28
28
  Encodable.isEncodable = isEncodable;
29
29
  })(Encodable || (exports.Encodable = Encodable = {}));
30
+ var Crypto;
31
+ (function (Crypto) {
32
+ })(Crypto || (exports.Crypto = Crypto = {}));
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 Map();
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
- const signedPreKey = this.bundleStore.get(message.signedPreKeyHash);
106
- const hash = message.signedPreKeyHash.concat(message.onetimePreKeyHash);
107
- const onetimePreKey = this.bundleStore.get(hash);
108
- if (!signedPreKey || !onetimePreKey || !message.identityKey || !message.ephemeralKey)
109
- throw new Error("ACK message malformed");
110
- if (!this.bundleStore.delete(hash))
111
- throw new Error("Bundle store deleting error");
112
- const identityKey = (0, utils_1.decodeBase64)(message.identityKey);
113
- const ephemeralKey = (0, utils_1.decodeBase64)(message.ephemeralKey);
114
- const rootKey = crypto_1.default.hkdf(new Uint8Array([
115
- ...crypto_1.default.scalarMult(signedPreKey.secretKey, identityKey),
116
- ...crypto_1.default.scalarMult(this._identityKey.secretKey, ephemeralKey),
117
- ...crypto_1.default.scalarMult(signedPreKey.secretKey, ephemeralKey),
118
- ...onetimePreKey ? crypto_1.default.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
119
- ]), new Uint8Array(double_ratchet_1.KeySession.rootKeyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.rootKeyLength);
120
- const session = new double_ratchet_1.KeySession({ secretKey: this._identityKey.secretKey, rootKey });
121
- const cleartext = session.decrypt((0, utils_1.decodeBase64)(message.associatedData));
122
- if (!cleartext)
123
- throw new Error("Error decrypting ACK message");
124
- if (!(0, utils_1.verifyUint8Array)(cleartext, (0, utils_1.concatUint8Array)(crypto_1.default.hash(identityKey), crypto_1.default.hash(this._identityKey.publicKey))))
125
- throw new Error("Error verifing Associated Data");
126
- return { session, cleartext };
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
+ }