@freesignal/protocol 0.5.5 → 0.6.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.
@@ -16,7 +16,9 @@
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 { IdentityKey, UserId } from "./types";
19
20
  export interface ExportedKeySession {
21
+ identityKey: string;
20
22
  secretKey: string;
21
23
  rootKey?: string;
22
24
  sendingChain?: ExportedKeyChain;
@@ -41,18 +43,20 @@ export declare class KeySession {
41
43
  static readonly info: string;
42
44
  static readonly maxCount = 65536;
43
45
  readonly id: string;
46
+ readonly identityKey: IdentityKey;
44
47
  private keyPair;
45
48
  private rootKey?;
46
49
  private sendingChain?;
47
50
  private receivingChain?;
48
51
  private nextHeaderKey?;
49
52
  private previousKeys;
50
- constructor(opts?: {
53
+ constructor(identityKey: IdentityKey, opts?: {
51
54
  id?: string;
52
55
  secretKey?: Uint8Array;
53
56
  remoteKey?: Uint8Array;
54
57
  rootKey?: Uint8Array;
55
58
  });
59
+ get userId(): UserId;
56
60
  private getChain;
57
61
  getHeaderKeys(): {
58
62
  readonly sending?: Uint8Array;
@@ -24,15 +24,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.KeySession = void 0;
25
25
  const crypto_1 = __importDefault(require("@freesignal/crypto"));
26
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.
30
31
  */
31
32
  class KeySession {
32
33
  //headerKey?: Uint8Array, nextHeaderKey?: Uint8Array,
33
- constructor(opts = {}) {
34
+ constructor(identityKey, opts = {}) {
34
35
  var _a;
35
36
  this.previousKeys = new KeyMap();
37
+ this.identityKey = identityKey;
36
38
  this.id = (_a = opts.id) !== null && _a !== void 0 ? _a : crypto_1.default.UUID.generate().toString();
37
39
  this.keyPair = crypto_1.default.ECDH.keyPair(opts.secretKey);
38
40
  if (opts.rootKey)
@@ -43,6 +45,9 @@ class KeySession {
43
45
  this.sendingChain = this.getChain(opts.remoteKey); //, opts.headerKey);
44
46
  }
45
47
  }
48
+ get userId() {
49
+ return this.identityKey.userId;
50
+ }
46
51
  getChain(remoteKey, headerKey, previousCount) {
47
52
  const sharedKey = crypto_1.default.ECDH.scalarMult(this.keyPair.secretKey, remoteKey);
48
53
  if (!this.rootKey)
@@ -106,6 +111,7 @@ class KeySession {
106
111
  toJSON() {
107
112
  var _a, _b;
108
113
  return {
114
+ identityKey: this.identityKey.toString(),
109
115
  secretKey: (0, utils_1.decodeBase64)(this.keyPair.secretKey),
110
116
  rootKey: this.rootKey ? (0, utils_1.decodeBase64)(this.rootKey) : undefined,
111
117
  sendingChain: (_a = this.sendingChain) === null || _a === void 0 ? void 0 : _a.toJSON(),
@@ -120,7 +126,7 @@ class KeySession {
120
126
  * @returns session with the state parsed.
121
127
  */
122
128
  static from(data) {
123
- const session = new KeySession({ secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: data.rootKey ? (0, utils_1.encodeBase64)(data.rootKey) : undefined });
129
+ const session = new KeySession(types_1.IdentityKey.from(data.identityKey), { secretKey: (0, utils_1.encodeBase64)(data.secretKey), rootKey: data.rootKey ? (0, utils_1.encodeBase64)(data.rootKey) : undefined });
124
130
  session.sendingChain = data.sendingChain ? KeyChain.from(data.sendingChain) : undefined;
125
131
  session.receivingChain = data.receivingChain ? KeyChain.from(data.receivingChain) : undefined;
126
132
  session.previousKeys = new KeyMap(data.previousKeys);
package/dist/node.d.ts CHANGED
@@ -33,7 +33,6 @@ export declare class BootstrapRequest {
33
33
  export declare class FreeSignalNode {
34
34
  protected readonly privateIdentityKey: PrivateIdentityKey;
35
35
  protected readonly sessions: SessionMap;
36
- protected readonly users: LocalStorage<string, IdentityKey>;
37
36
  protected readonly bundles: LocalStorage<string, KeyExchangeDataBundle>;
38
37
  protected readonly keyExchange: KeyExchange;
39
38
  protected readonly discovers: Set<string>;
@@ -41,7 +40,6 @@ export declare class FreeSignalNode {
41
40
  constructor(storage: Database<{
42
41
  sessions: LocalStorage<string, ExportedKeySession>;
43
42
  keyExchange: LocalStorage<string, Crypto.KeyPair>;
44
- users: LocalStorage<string, IdentityKey>;
45
43
  bundles: LocalStorage<string, KeyExchangeDataBundle>;
46
44
  bootstraps: LocalStorage<string, BootstrapRequest>;
47
45
  }>, privateIdentityKey?: PrivateIdentityKey);
@@ -51,6 +49,7 @@ export declare class FreeSignalNode {
51
49
  getRequest(userId: string): Promise<KeyExchangeData | undefined>;
52
50
  protected encrypt(receiverId: string | UserId, protocol: Protocols, data: Uint8Array): Promise<Datagram>;
53
51
  packHandshake(data: KeyExchangeData): Promise<Datagram>;
52
+ packHandshake(receiverId: string | UserId): Promise<Datagram>;
54
53
  packData<T>(receiverId: string | UserId, data: T): Promise<Datagram>;
55
54
  packRelay(receiverId: string | UserId, data: Datagram): Promise<Datagram>;
56
55
  packDiscover(receiverId: string | UserId, discoverId: string | UserId): Promise<Datagram>;
package/dist/node.js CHANGED
@@ -37,6 +37,9 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
37
37
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
38
38
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
39
39
  };
40
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41
+ return (mod && mod.__esModule) ? mod : { "default": mod };
42
+ };
40
43
  var _BootstrapRequest_status;
41
44
  Object.defineProperty(exports, "__esModule", { value: true });
42
45
  exports.FreeSignalNode = exports.BootstrapRequest = void 0;
@@ -45,6 +48,7 @@ const x3dh_1 = require("./x3dh");
45
48
  const double_ratchet_1 = require("./double-ratchet");
46
49
  const _1 = require(".");
47
50
  const utils_1 = require("@freesignal/utils");
51
+ const crypto_1 = __importDefault(require("@freesignal/crypto"));
48
52
  class BootstrapRequest {
49
53
  constructor(senderId, data) {
50
54
  this.senderId = senderId;
@@ -81,7 +85,6 @@ class FreeSignalNode {
81
85
  this.privateIdentityKey = privateIdentityKey !== null && privateIdentityKey !== void 0 ? privateIdentityKey : (0, _1.createIdentity)();
82
86
  this.sessions = new SessionMap(storage.sessions);
83
87
  this.keyExchange = new x3dh_1.KeyExchange(storage.keyExchange, this.privateIdentityKey);
84
- this.users = storage.users;
85
88
  this.bundles = storage.bundles;
86
89
  this.bootstraps = storage.bootstraps;
87
90
  }
@@ -111,10 +114,16 @@ class FreeSignalNode {
111
114
  }
112
115
  packHandshake(data) {
113
116
  return __awaiter(this, void 0, void 0, function* () {
114
- const { session, message, identityKey } = yield this.keyExchange.digestData(data, (0, utils_1.encodeData)(yield this.keyExchange.generateBundle()));
115
- const remoteId = types_1.UserId.fromKey(identityKey);
116
- yield this.users.set(remoteId.toString(), identityKey);
117
- yield this.sessions.set(remoteId.toString(), session);
117
+ var _a;
118
+ if (typeof data === 'string' || data instanceof types_1.UserId) {
119
+ const userId = data.toString();
120
+ const identityKey = (_a = (yield this.sessions.get(userId))) === null || _a === void 0 ? void 0 : _a.identityKey;
121
+ if (!identityKey)
122
+ throw new Error("Missing user");
123
+ return yield this.encrypt(userId, types_1.Protocols.HANDSHAKE, crypto_1.default.ECDH.scalarMult(identityKey.exchangeKey, this.privateIdentityKey.exchangeKey));
124
+ }
125
+ const { session, message } = yield this.keyExchange.digestData(data, (0, utils_1.encodeData)(yield this.keyExchange.generateBundle()));
126
+ yield this.sessions.set(session.userId.toString(), session);
118
127
  return new types_1.Datagram(this.userId.toString(), types_1.UserId.fromKey(data.identityKey).toString(), types_1.Protocols.HANDSHAKE, (0, utils_1.encodeData)(message)).sign(this.privateIdentityKey.signatureKey);
119
128
  });
120
129
  }
@@ -145,10 +154,11 @@ class FreeSignalNode {
145
154
  }
146
155
  decrypt(datagram) {
147
156
  return __awaiter(this, void 0, void 0, function* () {
148
- const signatureKey = yield this.users.get(datagram.sender);
149
- if (!signatureKey)
157
+ var _a;
158
+ const identityKey = (_a = (yield this.sessions.get(datagram.sender))) === null || _a === void 0 ? void 0 : _a.identityKey;
159
+ if (!identityKey)
150
160
  throw new Error("User IdentityKey not found");
151
- if (!types_1.Datagram.verify(datagram, signatureKey.signatureKey))
161
+ if (!types_1.Datagram.verify(datagram, identityKey.signatureKey))
152
162
  throw new Error("Signature not verified");
153
163
  const session = yield this.sessions.get(datagram.sender);
154
164
  if (!session)
@@ -168,7 +178,7 @@ class FreeSignalNode {
168
178
  */
169
179
  open(datagram) {
170
180
  return __awaiter(this, void 0, void 0, function* () {
171
- var _a;
181
+ var _a, _b;
172
182
  if (datagram instanceof Uint8Array)
173
183
  datagram = types_1.Datagram.from(datagram);
174
184
  let out = {
@@ -178,14 +188,24 @@ class FreeSignalNode {
178
188
  case types_1.Protocols.HANDSHAKE:
179
189
  if (!datagram.payload)
180
190
  throw new Error("Missing payload");
191
+ if (yield this.sessions.has(datagram.sender)) {
192
+ const payload = yield this.decrypt(datagram);
193
+ const identityKey = (_a = (yield this.sessions.get(datagram.sender))) === null || _a === void 0 ? void 0 : _a.identityKey;
194
+ if (!identityKey)
195
+ throw new Error("Missing user");
196
+ if (!(0, utils_1.compareBytes)(payload, crypto_1.default.ECDH.scalarMult(identityKey.exchangeKey, this.privateIdentityKey.exchangeKey)))
197
+ throw new Error("Error validating handshake data");
198
+ return out;
199
+ }
181
200
  const data = (0, utils_1.decodeData)(datagram.payload);
182
201
  if (!types_1.Datagram.verify(datagram, types_1.IdentityKey.from(data.identityKey).signatureKey))
183
202
  throw new Error("Signature not verified");
184
- const { session, identityKey, associatedData } = yield this.keyExchange.digestMessage(data);
185
- const userId = types_1.UserId.fromKey(identityKey);
186
- yield this.users.set(userId.toString(), identityKey);
187
- yield this.sessions.set(userId.toString(), session);
188
- yield this.bundles.set(userId.toString(), (0, utils_1.decodeData)(associatedData));
203
+ const { session, associatedData } = yield this.keyExchange.digestMessage(data);
204
+ yield this.sessions.set(session.userId.toString(), session);
205
+ yield this.bundles.set(session.userId.toString(), (0, utils_1.decodeData)(associatedData));
206
+ out.datagram = yield this.packHandshake(session.userId);
207
+ if (!out.datagram)
208
+ throw new Error("Error during handshake");
189
209
  return out;
190
210
  case types_1.Protocols.MESSAGE:
191
211
  out.payload = yield this.decrypt(datagram);
@@ -195,7 +215,7 @@ class FreeSignalNode {
195
215
  return out;
196
216
  case types_1.Protocols.DISCOVER:
197
217
  const message = (0, utils_1.decodeData)(yield this.decrypt(datagram));
198
- if (message.type === types_1.DiscoverType.REQUEST && message.discoverId && !(yield this.users.has(message.discoverId))) {
218
+ if (message.type === types_1.DiscoverType.REQUEST && message.discoverId && !(yield this.sessions.has(message.discoverId))) {
199
219
  let data;
200
220
  if (message.discoverId === this.userId.toString()) {
201
221
  data = yield this.keyExchange.generateData();
@@ -237,7 +257,7 @@ class FreeSignalNode {
237
257
  this.onRequest(request);
238
258
  }
239
259
  ;
240
- const bootstrap = yield ((_a = (yield this.bootstraps.get(datagram.sender))) === null || _a === void 0 ? void 0 : _a.get());
260
+ const bootstrap = yield ((_b = (yield this.bootstraps.get(datagram.sender))) === null || _b === void 0 ? void 0 : _b.get());
241
261
  if (bootstrap)
242
262
  out.datagram = yield this.packHandshake(bootstrap);
243
263
  return out;
package/dist/types.d.ts CHANGED
@@ -29,30 +29,37 @@ export declare class UserId implements Encodable {
29
29
  static fromKey(identityKey: string | Uint8Array | IdentityKey): UserId;
30
30
  static from(userId: string | Uint8Array | UserId): UserId;
31
31
  }
32
- export interface IdentityKey extends Encodable {
32
+ export declare class IdentityKey implements Encodable {
33
+ static readonly keyLength: number;
34
+ static readonly version = 1;
35
+ private static readonly info;
33
36
  readonly info: number;
34
37
  readonly signatureKey: Uint8Array;
35
38
  readonly exchangeKey: Uint8Array;
39
+ constructor(identityKey: IdentityKey | Uint8Array | string);
40
+ get userId(): UserId;
41
+ toBytes(): Uint8Array;
42
+ toString(): string;
43
+ toJSON(): string;
44
+ static from(identityKey: IdentityKey | Uint8Array | string): IdentityKey;
45
+ static from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): IdentityKey;
36
46
  }
37
- export declare namespace IdentityKey {
38
- const keyLength: number;
39
- const version = 1;
40
- function isIdentityKeys(obj: any): boolean;
41
- function from(identityKey: IdentityKey | Uint8Array | string): IdentityKey;
42
- function from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): IdentityKey;
43
- }
44
- export interface PrivateIdentityKey {
47
+ export declare class PrivateIdentityKey implements Encodable {
48
+ static readonly keyLength: number;
49
+ static readonly version = 1;
50
+ private static readonly info;
45
51
  readonly info: number;
46
52
  readonly signatureKey: Uint8Array;
47
53
  readonly exchangeKey: Uint8Array;
48
54
  readonly identityKey: IdentityKey;
49
- }
50
- export declare namespace PrivateIdentityKey {
51
- const keyLength: number;
52
- const version = 1;
53
- function isIdentityKeys(obj: any): boolean;
54
- function from(identityKey: PrivateIdentityKey | Uint8Array | string): PrivateIdentityKey;
55
- function from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): PrivateIdentityKey;
55
+ constructor(privateIdentityKey: PrivateIdentityKey | Uint8Array | string);
56
+ get userId(): string;
57
+ toBytes(): Uint8Array;
58
+ toString(): string;
59
+ toJSON(): string;
60
+ static isIdentityKeys(obj: any): boolean;
61
+ static from(identityKey: PrivateIdentityKey | Uint8Array | string): PrivateIdentityKey;
62
+ static from(signatureKey: Uint8Array | string, exchangeKey: Uint8Array | string): PrivateIdentityKey;
56
63
  }
57
64
  export declare enum DiscoverType {
58
65
  REQUEST = 0,
@@ -92,7 +99,7 @@ export interface SignedDatagram extends Datagram {
92
99
  signature: string;
93
100
  }
94
101
  export declare class DatagramHeader implements Encodable {
95
- private static offset;
102
+ static readonly headerLength: number;
96
103
  readonly id: string;
97
104
  readonly version: number;
98
105
  readonly sender: string;
@@ -113,7 +120,6 @@ export declare class Datagram implements Encodable, DatagramHeader {
113
120
  private _createdAt;
114
121
  private _payload?;
115
122
  private _signature?;
116
- private static headerOffset;
117
123
  constructor(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable);
118
124
  get id(): string;
119
125
  get version(): number;
package/dist/types.js CHANGED
@@ -86,8 +86,8 @@ class UserId {
86
86
  static fromKey(identityKey) {
87
87
  if (typeof identityKey === 'string')
88
88
  identityKey = (0, utils_1.encodeBase64)(identityKey);
89
- else if (IdentityKey.isIdentityKeys(identityKey))
90
- identityKey = identityKey.toBytes();
89
+ else if (identityKey instanceof IdentityKey)
90
+ identityKey = (identityKey).toBytes();
91
91
  return new UserId(crypto_1.default.hkdf(identityKey, new Uint8Array(32).fill(0), "/freesignal/userid"));
92
92
  }
93
93
  static from(userId) {
@@ -97,112 +97,101 @@ class UserId {
97
97
  }
98
98
  }
99
99
  exports.UserId = UserId;
100
- var IdentityKey;
101
- (function (IdentityKey) {
102
- IdentityKey.keyLength = crypto_1.default.EdDSA.publicKeyLength + crypto_1.default.ECDH.publicKeyLength + 1;
103
- const info = 0x70;
104
- IdentityKey.version = 1;
105
- class IdentityKeyConstructor {
106
- constructor(identityKey) {
107
- if (identityKey instanceof IdentityKeyConstructor) {
108
- this.info = identityKey.info;
109
- this.signatureKey = identityKey.signatureKey;
110
- this.exchangeKey = identityKey.exchangeKey;
111
- }
112
- else {
113
- if (typeof identityKey === 'string')
114
- identityKey = (0, utils_1.encodeBase64)(identityKey);
115
- if (!isIdentityKeys(identityKey))
116
- throw new Error("Invalid key length");
117
- this.info = identityKey[0];
118
- this.signatureKey = identityKey.subarray(1, crypto_1.default.EdDSA.publicKeyLength + 1);
119
- this.exchangeKey = identityKey.subarray(crypto_1.default.EdDSA.publicKeyLength + 1, IdentityKey.keyLength);
120
- }
100
+ class IdentityKey {
101
+ constructor(identityKey) {
102
+ if (identityKey instanceof IdentityKey) {
103
+ this.info = identityKey.info;
104
+ this.signatureKey = identityKey.signatureKey;
105
+ this.exchangeKey = identityKey.exchangeKey;
121
106
  }
122
- get userId() {
123
- return UserId.fromKey(this.toBytes()).toString();
124
- }
125
- toBytes() {
126
- return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
127
- }
128
- toString() {
129
- return (0, utils_1.decodeBase64)(this.toBytes());
130
- }
131
- toJSON() {
132
- return this.toString();
107
+ else {
108
+ if (typeof identityKey === 'string')
109
+ identityKey = (0, utils_1.encodeBase64)(identityKey);
110
+ if (identityKey.length !== IdentityKey.keyLength)
111
+ throw new Error("Invalid key length");
112
+ this.info = identityKey[0];
113
+ this.signatureKey = identityKey.subarray(1, crypto_1.default.EdDSA.publicKeyLength + 1);
114
+ this.exchangeKey = identityKey.subarray(crypto_1.default.EdDSA.publicKeyLength + 1, IdentityKey.keyLength);
133
115
  }
134
116
  }
135
- function isIdentityKeys(obj) {
136
- return (obj instanceof Uint8Array && obj.length === IdentityKey.keyLength) || obj instanceof IdentityKeyConstructor;
117
+ get userId() {
118
+ return UserId.fromKey(this.toBytes());
137
119
  }
138
- IdentityKey.isIdentityKeys = isIdentityKeys;
139
- function from(...keys) {
120
+ toBytes() {
121
+ return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
122
+ }
123
+ toString() {
124
+ return (0, utils_1.decodeBase64)(this.toBytes());
125
+ }
126
+ toJSON() {
127
+ return this.toString();
128
+ }
129
+ static from(...keys) {
140
130
  keys = keys.map(key => {
141
- if (key instanceof IdentityKeyConstructor)
131
+ if (key instanceof IdentityKey)
142
132
  return key.toBytes();
143
133
  else if (typeof key === 'string')
144
134
  return (0, utils_1.encodeBase64)(key);
145
135
  else
146
136
  return key;
147
137
  });
148
- return new IdentityKeyConstructor(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(info + IdentityKey.version, 1), ...keys) : keys[0]);
149
- }
150
- IdentityKey.from = from;
151
- })(IdentityKey || (exports.IdentityKey = IdentityKey = {}));
152
- var PrivateIdentityKey;
153
- (function (PrivateIdentityKey) {
154
- PrivateIdentityKey.keyLength = crypto_1.default.EdDSA.secretKeyLength + crypto_1.default.ECDH.secretKeyLength + 1;
155
- const info = 0x4E;
156
- PrivateIdentityKey.version = 1;
157
- class PrivateIdentityKeyConstructor {
158
- constructor(privateIdentityKey) {
159
- if (privateIdentityKey instanceof PrivateIdentityKeyConstructor) {
160
- this.info = privateIdentityKey.info;
161
- this.signatureKey = privateIdentityKey.signatureKey;
162
- this.exchangeKey = privateIdentityKey.exchangeKey;
163
- this.identityKey = privateIdentityKey.identityKey;
164
- }
165
- else {
166
- if (typeof privateIdentityKey === 'string')
167
- privateIdentityKey = (0, utils_1.encodeBase64)(privateIdentityKey);
168
- if (!isIdentityKeys(privateIdentityKey))
169
- throw new Error("Invalid key length");
170
- this.info = privateIdentityKey[0];
171
- this.signatureKey = privateIdentityKey.subarray(1, crypto_1.default.EdDSA.secretKeyLength + 1);
172
- this.exchangeKey = privateIdentityKey.subarray(crypto_1.default.EdDSA.secretKeyLength + 1, PrivateIdentityKey.keyLength);
173
- this.identityKey = IdentityKey.from(crypto_1.default.EdDSA.keyPair(this.signatureKey).publicKey, crypto_1.default.ECDH.keyPair(this.exchangeKey).publicKey);
174
- }
175
- }
176
- get userId() {
177
- return UserId.fromKey(this.identityKey.toBytes()).toString();
178
- }
179
- toBytes() {
180
- return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
181
- }
182
- toString() {
183
- return (0, utils_1.decodeBase64)(this.toBytes());
138
+ return new IdentityKey(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(IdentityKey.info + IdentityKey.version, 1), ...keys) : keys[0]);
139
+ }
140
+ }
141
+ exports.IdentityKey = IdentityKey;
142
+ IdentityKey.keyLength = crypto_1.default.EdDSA.publicKeyLength + crypto_1.default.ECDH.publicKeyLength + 1;
143
+ IdentityKey.version = 1;
144
+ IdentityKey.info = 0x70;
145
+ class PrivateIdentityKey {
146
+ constructor(privateIdentityKey) {
147
+ if (privateIdentityKey instanceof PrivateIdentityKey) {
148
+ this.info = privateIdentityKey.info;
149
+ this.signatureKey = privateIdentityKey.signatureKey;
150
+ this.exchangeKey = privateIdentityKey.exchangeKey;
151
+ this.identityKey = privateIdentityKey.identityKey;
184
152
  }
185
- toJSON() {
186
- return this.toString();
153
+ else {
154
+ if (typeof privateIdentityKey === 'string')
155
+ privateIdentityKey = (0, utils_1.encodeBase64)(privateIdentityKey);
156
+ if (!PrivateIdentityKey.isIdentityKeys(privateIdentityKey))
157
+ throw new Error("Invalid key length");
158
+ this.info = privateIdentityKey[0];
159
+ this.signatureKey = privateIdentityKey.subarray(1, crypto_1.default.EdDSA.secretKeyLength + 1);
160
+ this.exchangeKey = privateIdentityKey.subarray(crypto_1.default.EdDSA.secretKeyLength + 1, PrivateIdentityKey.keyLength);
161
+ this.identityKey = IdentityKey.from(crypto_1.default.EdDSA.keyPair(this.signatureKey).publicKey, crypto_1.default.ECDH.keyPair(this.exchangeKey).publicKey);
187
162
  }
188
163
  }
189
- function isIdentityKeys(obj) {
190
- return (obj instanceof Uint8Array && obj.length === PrivateIdentityKey.keyLength) || obj instanceof PrivateIdentityKeyConstructor;
164
+ get userId() {
165
+ return UserId.fromKey(this.identityKey.toBytes()).toString();
191
166
  }
192
- PrivateIdentityKey.isIdentityKeys = isIdentityKeys;
193
- function from(...keys) {
167
+ toBytes() {
168
+ return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.info, 1), this.signatureKey, this.exchangeKey);
169
+ }
170
+ toString() {
171
+ return (0, utils_1.decodeBase64)(this.toBytes());
172
+ }
173
+ toJSON() {
174
+ return this.toString();
175
+ }
176
+ static isIdentityKeys(obj) {
177
+ return (obj instanceof Uint8Array && obj.length === PrivateIdentityKey.keyLength) || obj instanceof PrivateIdentityKey;
178
+ }
179
+ static from(...keys) {
194
180
  keys = keys.map(key => {
195
- if (key instanceof PrivateIdentityKeyConstructor)
181
+ if (key instanceof PrivateIdentityKey)
196
182
  return key.toBytes();
197
183
  else if (typeof key === 'string')
198
184
  return (0, utils_1.encodeBase64)(key);
199
185
  else
200
186
  return key;
201
187
  });
202
- return new PrivateIdentityKeyConstructor(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(info + PrivateIdentityKey.version, 1), ...keys) : keys[0]);
188
+ return new PrivateIdentityKey(keys.length === 2 ? (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(PrivateIdentityKey.info + PrivateIdentityKey.version, 1), ...keys) : keys[0]);
203
189
  }
204
- PrivateIdentityKey.from = from;
205
- })(PrivateIdentityKey || (exports.PrivateIdentityKey = PrivateIdentityKey = {}));
190
+ }
191
+ exports.PrivateIdentityKey = PrivateIdentityKey;
192
+ PrivateIdentityKey.keyLength = crypto_1.default.EdDSA.secretKeyLength + crypto_1.default.ECDH.secretKeyLength + 1;
193
+ PrivateIdentityKey.version = 1;
194
+ PrivateIdentityKey.info = 0x4E;
206
195
  var DiscoverType;
207
196
  (function (DiscoverType) {
208
197
  DiscoverType[DiscoverType["REQUEST"] = 0] = "REQUEST";
@@ -247,7 +236,7 @@ class DatagramHeader {
247
236
  this.id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
248
237
  this.createdAt = (0, utils_1.bytesToNumber)(data.subarray(18, 26));
249
238
  this.sender = (0, utils_1.decodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength));
250
- this.receiver = (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, DatagramHeader.offset));
239
+ this.receiver = (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, DatagramHeader.headerLength));
251
240
  }
252
241
  toBytes() {
253
242
  return (0, utils_1.concatBytes)((0, utils_1.numberToBytes)(this.version, 1), Protocols.encode(this.protocol, 1), crypto_1.default.UUID.parse(this.id), (0, utils_1.numberToBytes)(this.createdAt, 8), (0, utils_1.encodeBase64)(this.sender), (0, utils_1.encodeBase64)(this.receiver));
@@ -259,7 +248,7 @@ class DatagramHeader {
259
248
  }
260
249
  }
261
250
  exports.DatagramHeader = DatagramHeader;
262
- DatagramHeader.offset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
251
+ DatagramHeader.headerLength = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
263
252
  class Datagram {
264
253
  constructor(sender, receiver, protocol, payload) {
265
254
  this._id = crypto_1.default.UUID.generate().toString();
@@ -295,7 +284,7 @@ class Datagram {
295
284
  return data.subarray(0, data.length - (this._signature ? crypto_1.default.EdDSA.signatureLength : 0));
296
285
  }
297
286
  get header() {
298
- return this.toBytes().slice(0, Datagram.headerOffset);
287
+ return this.toBytes().slice(0, DatagramHeader.headerLength);
299
288
  }
300
289
  toBytes() {
301
290
  var _a, _b, _c;
@@ -335,7 +324,7 @@ class Datagram {
335
324
  if (typeof data === 'string')
336
325
  data = (0, utils_1.encodeBase64)(data);
337
326
  if (data instanceof Uint8Array) {
338
- const datagram = new Datagram((0, utils_1.decodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength)), (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, Datagram.headerOffset)), Protocols.decode(data.subarray(1, 2)), data.subarray(Datagram.headerOffset, data.length - (data[0] & 128 ? crypto_1.default.EdDSA.signatureLength : 0)));
327
+ const datagram = new Datagram((0, utils_1.decodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength)), (0, utils_1.decodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, DatagramHeader.headerLength)), Protocols.decode(data.subarray(1, 2)), data.subarray(DatagramHeader.headerLength, data.length - (data[0] & 128 ? crypto_1.default.EdDSA.signatureLength : 0)));
339
328
  datagram._version = data[0] & 127;
340
329
  datagram._id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
341
330
  datagram._createdAt = (0, utils_1.bytesToNumber)(data.subarray(18, 26));
@@ -357,7 +346,6 @@ class Datagram {
357
346
  }
358
347
  exports.Datagram = Datagram;
359
348
  Datagram.version = 1;
360
- Datagram.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
361
349
  class EncryptionHeader {
362
350
  constructor(keys, nonce) {
363
351
  this.nonce = nonce;
package/dist/x3dh.d.ts CHANGED
@@ -38,11 +38,9 @@ export declare class KeyExchange {
38
38
  digestData(message: KeyExchangeData, associatedData?: Uint8Array): Promise<{
39
39
  session: KeySession;
40
40
  message: KeyExchangeSynMessage;
41
- identityKey: IdentityKey;
42
41
  }>;
43
42
  digestMessage(message: KeyExchangeSynMessage): Promise<{
44
43
  session: KeySession;
45
- identityKey: IdentityKey;
46
44
  associatedData: Uint8Array;
47
45
  }>;
48
46
  }
package/dist/x3dh.js CHANGED
@@ -99,7 +99,7 @@ class KeyExchange {
99
99
  ...onetimePreKey ? crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
100
100
  ]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 2);
101
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
+ const session = new double_ratchet_1.KeySession(identityKey, { remoteKey: identityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
103
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()));
104
104
  if (!encrypted)
105
105
  throw new Error("Decryption error");
@@ -112,8 +112,7 @@ class KeyExchange {
112
112
  signedPreKeyHash: (0, utils_1.decodeBase64)(signedPreKeyHash),
113
113
  onetimePreKeyHash: (0, utils_1.decodeBase64)(onetimePreKeyHash),
114
114
  associatedData: (0, utils_1.decodeBase64)(encrypted.toBytes())
115
- },
116
- identityKey
115
+ }
117
116
  };
118
117
  });
119
118
  }
@@ -135,7 +134,7 @@ class KeyExchange {
135
134
  ...onetimePreKey ? crypto_1.default.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
136
135
  ]), new Uint8Array(double_ratchet_1.KeySession.keyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.keyLength * 2);
137
136
  //, 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
+ const session = new double_ratchet_1.KeySession(identityKey, { secretKey: this.privateIdentityKey.exchangeKey, rootKey: derivedKey.subarray(0, double_ratchet_1.KeySession.keyLength) });
139
138
  const data = (0, types_1.decryptData)(session, (0, utils_1.encodeBase64)(message.associatedData));
140
139
  if (!data)
141
140
  throw new Error("Error decrypting ACK message");
@@ -143,7 +142,6 @@ class KeyExchange {
143
142
  throw new Error("Error verifing Associated Data");
144
143
  return {
145
144
  session,
146
- identityKey,
147
145
  associatedData: data.subarray(64)
148
146
  };
149
147
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freesignal/protocol",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "description": "Signal Protocol implementation in javascript",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "author": "Christian Braghette",