casper-baileys 1.0.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.
Files changed (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +193 -0
  3. package/WAProto/GenerateStatics.sh +2 -0
  4. package/WAProto/WAProto.proto +4633 -0
  5. package/WAProto/index.js +165059 -0
  6. package/engine-requirements.js +10 -0
  7. package/lib/Defaults/baileys-version.json +3 -0
  8. package/lib/Defaults/index.js +105 -0
  9. package/lib/Signal/Group/ciphertext-message.js +15 -0
  10. package/lib/Signal/Group/group-session-builder.js +64 -0
  11. package/lib/Signal/Group/group_cipher.js +96 -0
  12. package/lib/Signal/Group/index.js +57 -0
  13. package/lib/Signal/Group/keyhelper.js +55 -0
  14. package/lib/Signal/Group/queue-job.js +57 -0
  15. package/lib/Signal/Group/sender-chain-key.js +34 -0
  16. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  17. package/lib/Signal/Group/sender-key-message.js +69 -0
  18. package/lib/Signal/Group/sender-key-name.js +51 -0
  19. package/lib/Signal/Group/sender-key-record.js +53 -0
  20. package/lib/Signal/Group/sender-key-state.js +99 -0
  21. package/lib/Signal/Group/sender-message-key.js +29 -0
  22. package/lib/Signal/libsignal/WhisperTextProtocol.js +933 -0
  23. package/lib/Signal/libsignal/base_key_type.js +7 -0
  24. package/lib/Signal/libsignal/chain_type.js +6 -0
  25. package/lib/Signal/libsignal/crypto.js +98 -0
  26. package/lib/Signal/libsignal/curve.js +142 -0
  27. package/lib/Signal/libsignal/errors.js +33 -0
  28. package/lib/Signal/libsignal/index.js +9 -0
  29. package/lib/Signal/libsignal/keyhelper.js +45 -0
  30. package/lib/Signal/libsignal/numeric_fingerprint.js +72 -0
  31. package/lib/Signal/libsignal/protobufs.js +10 -0
  32. package/lib/Signal/libsignal/protocol_address.js +40 -0
  33. package/lib/Signal/libsignal/queue_job.js +69 -0
  34. package/lib/Signal/libsignal/session_builder.js +160 -0
  35. package/lib/Signal/libsignal/session_cipher.js +320 -0
  36. package/lib/Signal/libsignal/session_record.js +302 -0
  37. package/lib/Signal/libsignal.js +174 -0
  38. package/lib/Socket/Client/index.js +18 -0
  39. package/lib/Socket/Client/types.js +13 -0
  40. package/lib/Socket/Client/websocket.js +62 -0
  41. package/lib/Socket/business.js +260 -0
  42. package/lib/Socket/chats.js +880 -0
  43. package/lib/Socket/gcstatus.js +237 -0
  44. package/lib/Socket/groups.js +340 -0
  45. package/lib/Socket/index.js +10 -0
  46. package/lib/Socket/messages-recv.js +1079 -0
  47. package/lib/Socket/messages-send.js +1083 -0
  48. package/lib/Socket/mex.js +46 -0
  49. package/lib/Socket/newsletter.js +233 -0
  50. package/lib/Socket/socket.js +617 -0
  51. package/lib/Socket/usync.js +65 -0
  52. package/lib/Types/Auth.js +2 -0
  53. package/lib/Types/Call.js +2 -0
  54. package/lib/Types/Chat.js +10 -0
  55. package/lib/Types/Contact.js +2 -0
  56. package/lib/Types/Events.js +2 -0
  57. package/lib/Types/GroupMetadata.js +2 -0
  58. package/lib/Types/Label.js +27 -0
  59. package/lib/Types/LabelAssociation.js +9 -0
  60. package/lib/Types/Message.js +7 -0
  61. package/lib/Types/Newsletter.js +33 -0
  62. package/lib/Types/Product.js +2 -0
  63. package/lib/Types/Signal.js +2 -0
  64. package/lib/Types/Socket.js +2 -0
  65. package/lib/Types/State.js +2 -0
  66. package/lib/Types/USync.js +2 -0
  67. package/lib/Types/index.js +42 -0
  68. package/lib/Utils/auth-utils.js +199 -0
  69. package/lib/Utils/baileys-event-stream.js +63 -0
  70. package/lib/Utils/business.js +240 -0
  71. package/lib/Utils/chat-utils.js +741 -0
  72. package/lib/Utils/crypto.js +187 -0
  73. package/lib/Utils/decode-wa-message.js +284 -0
  74. package/lib/Utils/event-buffer.js +516 -0
  75. package/lib/Utils/generics.js +400 -0
  76. package/lib/Utils/history.js +100 -0
  77. package/lib/Utils/index.js +34 -0
  78. package/lib/Utils/lid-mapping.js +88 -0
  79. package/lib/Utils/link-preview.js +122 -0
  80. package/lib/Utils/logger.js +7 -0
  81. package/lib/Utils/lt-hash.js +51 -0
  82. package/lib/Utils/make-mutex.js +44 -0
  83. package/lib/Utils/messages-media.js +706 -0
  84. package/lib/Utils/messages.js +872 -0
  85. package/lib/Utils/noise-handler.js +150 -0
  86. package/lib/Utils/process-message.js +381 -0
  87. package/lib/Utils/signal.js +155 -0
  88. package/lib/Utils/use-multi-file-auth-state.js +124 -0
  89. package/lib/Utils/validate-connection.js +170 -0
  90. package/lib/WABinary/constants.js +1303 -0
  91. package/lib/WABinary/decode.js +266 -0
  92. package/lib/WABinary/encode.js +252 -0
  93. package/lib/WABinary/generic-utils.js +110 -0
  94. package/lib/WABinary/index.js +21 -0
  95. package/lib/WABinary/jid-utils.js +66 -0
  96. package/lib/WABinary/types.js +2 -0
  97. package/lib/WAM/BinaryInfo.js +13 -0
  98. package/lib/WAM/constants.js +15243 -0
  99. package/lib/WAM/encode.js +153 -0
  100. package/lib/WAM/index.js +19 -0
  101. package/lib/WAUSync/Protocols/USyncContactProtocol.js +32 -0
  102. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
  103. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
  104. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +42 -0
  105. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +53 -0
  106. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
  107. package/lib/WAUSync/Protocols/index.js +20 -0
  108. package/lib/WAUSync/USyncQuery.js +93 -0
  109. package/lib/WAUSync/USyncUser.js +26 -0
  110. package/lib/WAUSync/index.js +19 -0
  111. package/lib/index.js +30 -0
  112. package/package.json +39 -0
  113. package/test.js +7 -0
@@ -0,0 +1,160 @@
1
+
2
+ 'use strict';
3
+
4
+ // PATCHED by CASPER-XD ULTRA - Suppressed noisy session closing warnings
5
+
6
+ const BaseKeyType = require('./base_key_type');
7
+ const ChainType = require('./chain_type');
8
+ const SessionRecord = require('./session_record');
9
+ const crypto = require('./crypto');
10
+ const curve = require('./curve');
11
+ const errors = require('./errors');
12
+ const queueJob = require('./queue_job');
13
+
14
+
15
+ class SessionBuilder {
16
+
17
+ constructor(storage, protocolAddress) {
18
+ this.addr = protocolAddress;
19
+ this.storage = storage;
20
+ }
21
+
22
+ async initOutgoing(device) {
23
+ const fqAddr = this.addr.toString();
24
+ return await queueJob(fqAddr, async () => {
25
+ if (!await this.storage.isTrustedIdentity(this.addr.id, device.identityKey)) {
26
+ throw new errors.UntrustedIdentityKeyError(this.addr.id, device.identityKey);
27
+ }
28
+ curve.verifySignature(device.identityKey, device.signedPreKey.publicKey,
29
+ device.signedPreKey.signature, true);
30
+ const baseKey = curve.generateKeyPair();
31
+ const devicePreKey = device.preKey && device.preKey.publicKey;
32
+ const session = await this.initSession(true, baseKey, undefined, device.identityKey,
33
+ devicePreKey, device.signedPreKey.publicKey,
34
+ device.registrationId);
35
+ session.pendingPreKey = {
36
+ signedKeyId: device.signedPreKey.keyId,
37
+ baseKey: baseKey.pubKey
38
+ };
39
+ if (device.preKey) {
40
+ session.pendingPreKey.preKeyId = device.preKey.keyId;
41
+ }
42
+ let record = await this.storage.loadSession(fqAddr);
43
+ if (!record) {
44
+ record = new SessionRecord();
45
+ } else {
46
+ const openSession = record.getOpenSession();
47
+ if (openSession) {
48
+ record.closeSession(openSession);
49
+ }
50
+ }
51
+ record.setSession(session);
52
+ await this.storage.storeSession(fqAddr, record);
53
+ });
54
+ }
55
+
56
+ async initIncoming(record, message) {
57
+ const fqAddr = this.addr.toString();
58
+ if (!await this.storage.isTrustedIdentity(fqAddr, message.identityKey)) {
59
+ throw new errors.UntrustedIdentityKeyError(this.addr.id, message.identityKey);
60
+ }
61
+ if (record.getSession(message.baseKey)) {
62
+ return;
63
+ }
64
+ const preKeyPair = await this.storage.loadPreKey(message.preKeyId);
65
+ if (message.preKeyId && !preKeyPair) {
66
+ throw new errors.PreKeyError('Invalid PreKey ID');
67
+ }
68
+ const signedPreKeyPair = await this.storage.loadSignedPreKey(message.signedPreKeyId);
69
+ if (!signedPreKeyPair) {
70
+ throw new errors.PreKeyError("Missing SignedPreKey");
71
+ }
72
+ const existingOpenSession = record.getOpenSession();
73
+ if (existingOpenSession) {
74
+ record.closeSession(existingOpenSession);
75
+ }
76
+ record.setSession(await this.initSession(false, preKeyPair, signedPreKeyPair,
77
+ message.identityKey, message.baseKey,
78
+ undefined, message.registrationId));
79
+ return message.preKeyId;
80
+ }
81
+
82
+ async initSession(isInitiator, ourEphemeralKey, ourSignedKey, theirIdentityPubKey,
83
+ theirEphemeralPubKey, theirSignedPubKey, registrationId) {
84
+ if (isInitiator) {
85
+ if (ourSignedKey) {
86
+ throw new Error("Invalid call to initSession");
87
+ }
88
+ ourSignedKey = ourEphemeralKey;
89
+ } else {
90
+ if (theirSignedPubKey) {
91
+ throw new Error("Invalid call to initSession");
92
+ }
93
+ theirSignedPubKey = theirEphemeralPubKey;
94
+ }
95
+ let sharedSecret;
96
+ if (!ourEphemeralKey || !theirEphemeralPubKey) {
97
+ sharedSecret = new Uint8Array(32 * 4);
98
+ } else {
99
+ sharedSecret = new Uint8Array(32 * 5);
100
+ }
101
+ for (var i = 0; i < 32; i++) {
102
+ sharedSecret[i] = 0xff;
103
+ }
104
+ const ourIdentityKey = await this.storage.getOurIdentity();
105
+ const a1 = curve.calculateAgreement(theirSignedPubKey, ourIdentityKey.privKey);
106
+ const a2 = curve.calculateAgreement(theirIdentityPubKey, ourSignedKey.privKey);
107
+ const a3 = curve.calculateAgreement(theirSignedPubKey, ourSignedKey.privKey);
108
+ if (isInitiator) {
109
+ sharedSecret.set(new Uint8Array(a1), 32);
110
+ sharedSecret.set(new Uint8Array(a2), 32 * 2);
111
+ } else {
112
+ sharedSecret.set(new Uint8Array(a1), 32 * 2);
113
+ sharedSecret.set(new Uint8Array(a2), 32);
114
+ }
115
+ sharedSecret.set(new Uint8Array(a3), 32 * 3);
116
+ if (ourEphemeralKey && theirEphemeralPubKey) {
117
+ const a4 = curve.calculateAgreement(theirEphemeralPubKey, ourEphemeralKey.privKey);
118
+ sharedSecret.set(new Uint8Array(a4), 32 * 4);
119
+ }
120
+ const masterKey = crypto.deriveSecrets(Buffer.from(sharedSecret), Buffer.alloc(32),
121
+ Buffer.from("WhisperText"));
122
+ const session = SessionRecord.createEntry();
123
+ session.registrationId = registrationId;
124
+ session.currentRatchet = {
125
+ rootKey: masterKey[0],
126
+ ephemeralKeyPair: isInitiator ? curve.generateKeyPair() : ourSignedKey,
127
+ lastRemoteEphemeralKey: theirSignedPubKey,
128
+ previousCounter: 0
129
+ };
130
+ session.indexInfo = {
131
+ created: Date.now(),
132
+ used: Date.now(),
133
+ remoteIdentityKey: theirIdentityPubKey,
134
+ baseKey: isInitiator ? ourEphemeralKey.pubKey : theirEphemeralPubKey,
135
+ baseKeyType: isInitiator ? BaseKeyType.OURS : BaseKeyType.THEIRS,
136
+ closed: -1
137
+ };
138
+ if (isInitiator) {
139
+ this.calculateSendingRatchet(session, theirSignedPubKey);
140
+ }
141
+ return session;
142
+ }
143
+
144
+ calculateSendingRatchet(session, remoteKey) {
145
+ const ratchet = session.currentRatchet;
146
+ const sharedSecret = curve.calculateAgreement(remoteKey, ratchet.ephemeralKeyPair.privKey);
147
+ const masterKey = crypto.deriveSecrets(sharedSecret, ratchet.rootKey, Buffer.from("WhisperRatchet"));
148
+ session.addChain(ratchet.ephemeralKeyPair.pubKey, {
149
+ messageKeys: {},
150
+ chainKey: {
151
+ counter: -1,
152
+ key: masterKey[1]
153
+ },
154
+ chainType: ChainType.SENDING
155
+ });
156
+ ratchet.rootKey = masterKey[0];
157
+ }
158
+ }
159
+
160
+ module.exports = SessionBuilder;
@@ -0,0 +1,320 @@
1
+ // vim: ts=4:sw=4:expandtab
2
+ // PATCHED by CASPER-XD ULTRA - Suppressed noisy session error logging
3
+
4
+ const ChainType = require('./chain_type');
5
+ const ProtocolAddress = require('./protocol_address');
6
+ const SessionBuilder = require('./session_builder');
7
+ const SessionRecord = require('./session_record');
8
+ const crypto = require('./crypto');
9
+ const curve = require('./curve');
10
+ const errors = require('./errors');
11
+ const protobufs = require('./protobufs');
12
+ const queueJob = require('./queue_job');
13
+
14
+ const VERSION = 3;
15
+
16
+ function assertBuffer(value) {
17
+ if (!(value instanceof Buffer)) {
18
+ throw TypeError(`Expected Buffer instead of: ${value.constructor.name}`);
19
+ }
20
+ return value;
21
+ }
22
+
23
+
24
+ class SessionCipher {
25
+
26
+ constructor(storage, protocolAddress) {
27
+ if (!(protocolAddress instanceof ProtocolAddress)) {
28
+ throw new TypeError("protocolAddress must be a ProtocolAddress");
29
+ }
30
+ this.addr = protocolAddress;
31
+ this.storage = storage;
32
+ }
33
+
34
+ _encodeTupleByte(number1, number2) {
35
+ if (number1 > 15 || number2 > 15) {
36
+ throw TypeError("Numbers must be 4 bits or less");
37
+ }
38
+ return (number1 << 4) | number2;
39
+ }
40
+
41
+ _decodeTupleByte(byte) {
42
+ return [byte >> 4, byte & 0xf];
43
+ }
44
+
45
+ toString() {
46
+ return `<SessionCipher(${this.addr.toString()})>`;
47
+ }
48
+
49
+ async getRecord() {
50
+ const record = await this.storage.loadSession(this.addr.toString());
51
+ if (record && !(record instanceof SessionRecord)) {
52
+ throw new TypeError('SessionRecord type expected from loadSession');
53
+ }
54
+ return record;
55
+ }
56
+
57
+ async storeRecord(record) {
58
+ record.removeOldSessions();
59
+ await this.storage.storeSession(this.addr.toString(), record);
60
+ }
61
+
62
+ async queueJob(awaitable) {
63
+ return await queueJob(this.addr.toString(), awaitable);
64
+ }
65
+
66
+ async encrypt(data) {
67
+ assertBuffer(data);
68
+ const ourIdentityKey = await this.storage.getOurIdentity();
69
+ return await this.queueJob(async () => {
70
+ const record = await this.getRecord();
71
+ if (!record) {
72
+ throw new errors.SessionError("No sessions");
73
+ }
74
+ const session = record.getOpenSession();
75
+ if (!session) {
76
+ throw new errors.SessionError("No open session");
77
+ }
78
+ const remoteIdentityKey = session.indexInfo.remoteIdentityKey;
79
+ if (!await this.storage.isTrustedIdentity(this.addr.id, remoteIdentityKey)) {
80
+ throw new errors.UntrustedIdentityKeyError(this.addr.id, remoteIdentityKey);
81
+ }
82
+ const chain = session.getChain(session.currentRatchet.ephemeralKeyPair.pubKey);
83
+ if (chain.chainType === ChainType.RECEIVING) {
84
+ throw new Error("Tried to encrypt on a receiving chain");
85
+ }
86
+ this.fillMessageKeys(chain, chain.chainKey.counter + 1);
87
+ const keys = crypto.deriveSecrets(chain.messageKeys[chain.chainKey.counter],
88
+ Buffer.alloc(32), Buffer.from("WhisperMessageKeys"));
89
+ delete chain.messageKeys[chain.chainKey.counter];
90
+ const msg = protobufs.WhisperMessage.create();
91
+ msg.ephemeralKey = session.currentRatchet.ephemeralKeyPair.pubKey;
92
+ msg.counter = chain.chainKey.counter;
93
+ msg.previousCounter = session.currentRatchet.previousCounter;
94
+ msg.ciphertext = crypto.encrypt(keys[0], data, keys[2].slice(0, 16));
95
+ const msgBuf = protobufs.WhisperMessage.encode(msg).finish();
96
+ const macInput = Buffer.alloc(msgBuf.byteLength + (33 * 2) + 1);
97
+ macInput.set(ourIdentityKey.pubKey);
98
+ macInput.set(session.indexInfo.remoteIdentityKey, 33);
99
+ macInput[33 * 2] = this._encodeTupleByte(VERSION, VERSION);
100
+ macInput.set(msgBuf, (33 * 2) + 1);
101
+ const mac = crypto.calculateMAC(keys[1], macInput);
102
+ const result = Buffer.alloc(msgBuf.byteLength + 9);
103
+ result[0] = this._encodeTupleByte(VERSION, VERSION);
104
+ result.set(msgBuf, 1);
105
+ result.set(mac.slice(0, 8), msgBuf.byteLength + 1);
106
+ await this.storeRecord(record);
107
+ let type, body;
108
+ if (session.pendingPreKey) {
109
+ type = 3; // prekey bundle
110
+ const preKeyMsg = protobufs.PreKeyWhisperMessage.create({
111
+ identityKey: ourIdentityKey.pubKey,
112
+ registrationId: await this.storage.getOurRegistrationId(),
113
+ baseKey: session.pendingPreKey.baseKey,
114
+ signedPreKeyId: session.pendingPreKey.signedKeyId,
115
+ message: result
116
+ });
117
+ if (session.pendingPreKey.preKeyId) {
118
+ preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
119
+ }
120
+ body = Buffer.concat([
121
+ Buffer.from([this._encodeTupleByte(VERSION, VERSION)]),
122
+ Buffer.from(
123
+ protobufs.PreKeyWhisperMessage.encode(preKeyMsg).finish()
124
+ )
125
+ ]);
126
+ } else {
127
+ type = 1; // normal
128
+ body = result;
129
+ }
130
+ return {
131
+ type,
132
+ body,
133
+ registrationId: session.registrationId
134
+ };
135
+ });
136
+ }
137
+
138
+ async decryptWithSessions(data, sessions) {
139
+ if (!sessions.length) {
140
+ throw new errors.SessionError("No sessions available");
141
+ }
142
+ const errs = [];
143
+ for (const session of sessions) {
144
+ let plaintext;
145
+ try {
146
+ plaintext = await this.doDecryptWhisperMessage(data, session);
147
+ session.indexInfo.used = Date.now();
148
+ return {
149
+ session,
150
+ plaintext
151
+ };
152
+ } catch(e) {
153
+ errs.push(e);
154
+ }
155
+ }
156
+ throw new errors.SessionError("No matching sessions found for message");
157
+ }
158
+
159
+ async decryptWhisperMessage(data) {
160
+ assertBuffer(data);
161
+ return await this.queueJob(async () => {
162
+ const record = await this.getRecord();
163
+ if (!record) {
164
+ throw new errors.SessionError("No session record");
165
+ }
166
+ const result = await this.decryptWithSessions(data, record.getSessions());
167
+ const remoteIdentityKey = result.session.indexInfo.remoteIdentityKey;
168
+ if (!await this.storage.isTrustedIdentity(this.addr.id, remoteIdentityKey)) {
169
+ throw new errors.UntrustedIdentityKeyError(this.addr.id, remoteIdentityKey);
170
+ }
171
+ if (record.isClosed(result.session)) {
172
+ }
173
+ await this.storeRecord(record);
174
+ return result.plaintext;
175
+ });
176
+ }
177
+
178
+ async decryptPreKeyWhisperMessage(data) {
179
+ assertBuffer(data);
180
+ const versions = this._decodeTupleByte(data[0]);
181
+ if (versions[1] > 3 || versions[0] < 3) { // min version > 3 or max version < 3
182
+ throw new Error("Incompatible version number on PreKeyWhisperMessage");
183
+ }
184
+ return await this.queueJob(async () => {
185
+ let record = await this.getRecord();
186
+ const preKeyProto = protobufs.PreKeyWhisperMessage.decode(data.slice(1));
187
+ if (!record) {
188
+ if (preKeyProto.registrationId == null) {
189
+ throw new Error("No registrationId");
190
+ }
191
+ record = new SessionRecord();
192
+ }
193
+ const builder = new SessionBuilder(this.storage, this.addr);
194
+ const preKeyId = await builder.initIncoming(record, preKeyProto);
195
+ const session = record.getSession(preKeyProto.baseKey);
196
+ const plaintext = await this.doDecryptWhisperMessage(preKeyProto.message, session);
197
+ await this.storeRecord(record);
198
+ if (preKeyId) {
199
+ await this.storage.removePreKey(preKeyId);
200
+ }
201
+ return plaintext;
202
+ });
203
+ }
204
+
205
+ async doDecryptWhisperMessage(messageBuffer, session) {
206
+ assertBuffer(messageBuffer);
207
+ if (!session) {
208
+ throw new TypeError("session required");
209
+ }
210
+ const versions = this._decodeTupleByte(messageBuffer[0]);
211
+ if (versions[1] > 3 || versions[0] < 3) { // min version > 3 or max version < 3
212
+ throw new Error("Incompatible version number on WhisperMessage");
213
+ }
214
+ const messageProto = messageBuffer.slice(1, -8);
215
+ const message = protobufs.WhisperMessage.decode(messageProto);
216
+ this.maybeStepRatchet(session, message.ephemeralKey, message.previousCounter);
217
+ const chain = session.getChain(message.ephemeralKey);
218
+ if (chain.chainType === ChainType.SENDING) {
219
+ throw new Error("Tried to decrypt on a sending chain");
220
+ }
221
+ this.fillMessageKeys(chain, message.counter);
222
+ if (!chain.messageKeys.hasOwnProperty(message.counter)) {
223
+ throw new errors.MessageCounterError('Key used already or never filled');
224
+ }
225
+ const messageKey = chain.messageKeys[message.counter];
226
+ delete chain.messageKeys[message.counter];
227
+ const keys = crypto.deriveSecrets(messageKey, Buffer.alloc(32),
228
+ Buffer.from("WhisperMessageKeys"));
229
+ const ourIdentityKey = await this.storage.getOurIdentity();
230
+ const macInput = Buffer.alloc(messageProto.byteLength + (33 * 2) + 1);
231
+ macInput.set(session.indexInfo.remoteIdentityKey);
232
+ macInput.set(ourIdentityKey.pubKey, 33);
233
+ macInput[33 * 2] = this._encodeTupleByte(VERSION, VERSION);
234
+ macInput.set(messageProto, (33 * 2) + 1);
235
+ crypto.verifyMAC(macInput, keys[1], messageBuffer.slice(-8), 8);
236
+ const plaintext = crypto.decrypt(keys[0], message.ciphertext, keys[2].slice(0, 16));
237
+ delete session.pendingPreKey;
238
+ return plaintext;
239
+ }
240
+
241
+ fillMessageKeys(chain, counter) {
242
+ if (chain.chainKey.counter >= counter) {
243
+ return;
244
+ }
245
+ if (counter - chain.chainKey.counter > 2000) {
246
+ throw new errors.SessionError('Over 2000 messages into the future!');
247
+ }
248
+ if (chain.chainKey.key === undefined) {
249
+ throw new errors.SessionError('Chain closed');
250
+ }
251
+ const key = chain.chainKey.key;
252
+ chain.messageKeys[chain.chainKey.counter + 1] = crypto.calculateMAC(key, Buffer.from([1]));
253
+ chain.chainKey.key = crypto.calculateMAC(key, Buffer.from([2]));
254
+ chain.chainKey.counter += 1;
255
+ return this.fillMessageKeys(chain, counter);
256
+ }
257
+
258
+ maybeStepRatchet(session, remoteKey, previousCounter) {
259
+ if (session.getChain(remoteKey)) {
260
+ return;
261
+ }
262
+ const ratchet = session.currentRatchet;
263
+ let previousRatchet = session.getChain(ratchet.lastRemoteEphemeralKey);
264
+ if (previousRatchet) {
265
+ this.fillMessageKeys(previousRatchet, previousCounter);
266
+ delete previousRatchet.chainKey.key; // Close
267
+ }
268
+ this.calculateRatchet(session, remoteKey, false);
269
+ const prevCounter = session.getChain(ratchet.ephemeralKeyPair.pubKey);
270
+ if (prevCounter) {
271
+ ratchet.previousCounter = prevCounter.chainKey.counter;
272
+ session.deleteChain(ratchet.ephemeralKeyPair.pubKey);
273
+ }
274
+ ratchet.ephemeralKeyPair = curve.generateKeyPair();
275
+ this.calculateRatchet(session, remoteKey, true);
276
+ ratchet.lastRemoteEphemeralKey = remoteKey;
277
+ }
278
+
279
+ calculateRatchet(session, remoteKey, sending) {
280
+ let ratchet = session.currentRatchet;
281
+ const sharedSecret = curve.calculateAgreement(remoteKey, ratchet.ephemeralKeyPair.privKey);
282
+ const masterKey = crypto.deriveSecrets(sharedSecret, ratchet.rootKey,
283
+ Buffer.from("WhisperRatchet"), /*chunks*/ 2);
284
+ const chainKey = sending ? ratchet.ephemeralKeyPair.pubKey : remoteKey;
285
+ session.addChain(chainKey, {
286
+ messageKeys: {},
287
+ chainKey: {
288
+ counter: -1,
289
+ key: masterKey[1]
290
+ },
291
+ chainType: sending ? ChainType.SENDING : ChainType.RECEIVING
292
+ });
293
+ ratchet.rootKey = masterKey[0];
294
+ }
295
+
296
+ async hasOpenSession() {
297
+ return await this.queueJob(async () => {
298
+ const record = await this.getRecord();
299
+ if (!record) {
300
+ return false;
301
+ }
302
+ return record.haveOpenSession();
303
+ });
304
+ }
305
+
306
+ async closeOpenSession() {
307
+ return await this.queueJob(async () => {
308
+ const record = await this.getRecord();
309
+ if (record) {
310
+ const openSession = record.getOpenSession();
311
+ if (openSession) {
312
+ record.closeSession(openSession);
313
+ await this.storeRecord(record);
314
+ }
315
+ }
316
+ });
317
+ }
318
+ }
319
+
320
+ module.exports = SessionCipher;