whatsapp-nodejs 0.0.1-security → 0.0.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.

Potentially problematic release.


This version of whatsapp-nodejs might be problematic. Click here for more details.

Files changed (59) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.js +59 -0
  3. package/.prettierrc +11 -0
  4. package/README.md +51 -3
  5. package/package.json +97 -3
  6. package/proto/ClientHello.proto +167 -0
  7. package/proto/HandshakeMessage.proto +26 -0
  8. package/proto/Message.proto +795 -0
  9. package/proto/SessionStructure.proto +113 -0
  10. package/proto/WhisperTextProtocol.proto +29 -0
  11. package/src/SocketClient.js +241 -0
  12. package/src/SocketManager.js +130 -0
  13. package/src/WASocketClient.js +170 -0
  14. package/src/Whatsapp.js +169 -0
  15. package/src/WhatsappServer.js +17 -0
  16. package/src/bin/decoder.js +1 -0
  17. package/src/bin/dict.js +1 -0
  18. package/src/bin/dict1.js +1 -0
  19. package/src/bin/dict2.js +1 -0
  20. package/src/bin/encoder.js +1 -0
  21. package/src/config.js +71 -0
  22. package/src/db.js +44 -0
  23. package/src/index.js +7 -0
  24. package/src/lib/SocketProxy.js +225 -0
  25. package/src/lib/libsignal-protocol.js +14 -0
  26. package/src/lib/utils.js +123 -0
  27. package/src/logger.js +79 -0
  28. package/src/packet/ProtocolEntity.js +39 -0
  29. package/src/packet/ProtocolTreeNode.js +202 -0
  30. package/src/protobuf/pb.js +32415 -0
  31. package/src/protocol/CipherState.js +48 -0
  32. package/src/protocol/FallbackPatternModifier.js +48 -0
  33. package/src/protocol/ForwarderHandshakeState.js +56 -0
  34. package/src/protocol/HandShake.js +273 -0
  35. package/src/protocol/HandshakePattern.js +62 -0
  36. package/src/protocol/HandshakeState.js +223 -0
  37. package/src/protocol/PKCS7.js +89 -0
  38. package/src/protocol/SwitchableHandshakeState.js +64 -0
  39. package/src/protocol/WASymmetricState.js +116 -0
  40. package/src/protocol/crypto.js +112 -0
  41. package/src/protocol/handshakepatterns/HandshakePattern.js +62 -0
  42. package/src/protocol/handshakepatterns/IKHandshakePattern.js +17 -0
  43. package/src/protocol/handshakepatterns/XXHandshakePattern.js +9 -0
  44. package/src/schema/account.js +65 -0
  45. package/src/schema/axolotl.js +8 -0
  46. package/src/schema/business.js +15 -0
  47. package/src/schema/common.js +26 -0
  48. package/src/schema/dayreport.js +21 -0
  49. package/src/schema/identify.js +49 -0
  50. package/src/schema/message.js +44 -0
  51. package/src/schema/prekey.js +51 -0
  52. package/src/schema/recvmessage.js +33 -0
  53. package/src/schema/senderkey.js +20 -0
  54. package/src/schema/session.js +22 -0
  55. package/src/schema/signedprekeys.js +56 -0
  56. package/test/WASocketClient.test.js +23 -0
  57. package/test/handshake.test.js +38 -0
  58. package/test/logger.test.js +17 -0
  59. package/test/whatsapp.test.js +17 -0
@@ -0,0 +1,48 @@
1
+ const crypto = require('./crypto');
2
+
3
+ class CipherState {
4
+ constructor(name) {
5
+ this.name = name;
6
+ this.key = Buffer.alloc(0); // aes 加解密的 key 字段
7
+ }
8
+
9
+ setKey(key) {
10
+ this.key = key;
11
+ this.setNonce(0);
12
+ }
13
+
14
+ setNonce(nonce) {
15
+ this.nonce = nonce;
16
+ }
17
+
18
+ hasKey() {
19
+ return this.key && this.key.length;
20
+ }
21
+
22
+ rekey() {
23
+ const key = crypto.encryptAES256GCM(
24
+ Buffer.alloc(32).fill(0),
25
+ this.key,
26
+ Buffer.alloc(0),
27
+ 2 ** 64 - 1
28
+ );
29
+ this.setKey(key);
30
+ }
31
+
32
+ encryptAES256GCM(ad, plaintext) {
33
+ if (!this.key.length) return plaintext;
34
+ const result = crypto.encryptAES256GCM(plaintext, this.key, ad, this.nonce);
35
+ this.nonce++;
36
+ return result;
37
+ }
38
+
39
+ decryptAES256GCM(ad, ciphertext) {
40
+ if (!this.key.length) return ciphertext;
41
+ const result = crypto.decryptAES256GCM(ciphertext, this.key, ad, this.nonce);
42
+ this.nonce++;
43
+
44
+ return result;
45
+ }
46
+ }
47
+
48
+ module.exports = CipherState;
@@ -0,0 +1,48 @@
1
+ const HandshakePattern = require('./HandshakePattern');
2
+
3
+ class FallbackPatternModifier {
4
+ VALID_FIRST_MESSAGES = [['e'], ['s'], ['e', 's']];
5
+
6
+ constructor() {
7
+ this.name = 'fallback';
8
+ this._name = 'fallback';
9
+ }
10
+
11
+ _is_modifiable(handsakepattern) {
12
+ const str = handsakepattern.message_patterns[0].join('');
13
+ for (let i = 0; i < this.VALID_FIRST_MESSAGES.length; i++) {
14
+ const element = this.VALID_FIRST_MESSAGES[i].join('');
15
+ if (element === str) return true;
16
+ }
17
+ return false;
18
+ }
19
+
20
+ _get_message_patterns(handshakepattern) {
21
+ return handshakepattern.message_patterns.slice(1);
22
+ }
23
+
24
+ _get_initiator_pre_messages(handshakepattern) {
25
+ return handshakepattern.message_patterns[0];
26
+ }
27
+
28
+ _get_responder_pre_messages() {}
29
+
30
+ _interpret_as_bob(handshakepattern) {
31
+ return true;
32
+ }
33
+
34
+ modify(pattern) {
35
+ if (!this._is_modifiable(pattern)) {
36
+ throw new Error(`pattern ${pattern.name} is not modifiable by ${this.name}`);
37
+ }
38
+ const name = pattern.origin_name + pattern.modifiers.concat(this.name).join('+');
39
+ return new HandshakePattern(
40
+ name,
41
+ this._get_message_patterns(pattern),
42
+ this._get_initiator_pre_messages(pattern),
43
+ this._get_responder_pre_messages(pattern),
44
+ this._interpret_as_bob(pattern)
45
+ );
46
+ }
47
+ }
48
+ module.exports = FallbackPatternModifier;
@@ -0,0 +1,56 @@
1
+ const HandShakeState = require('./HandshakeState');
2
+
3
+ class ForwarderHandshakeState extends HandShakeState {
4
+ constructor(handshakestate) {
5
+ super();
6
+ this._handshakestate = handshakestate;
7
+ }
8
+
9
+ initialize(
10
+ handshake_pattern,
11
+ initiator,
12
+ prologue,
13
+ s = null,
14
+ e = null,
15
+ rs = null,
16
+ re = null,
17
+ psks = null
18
+ ) {
19
+ return this._handshakestate.initialize(
20
+ handshake_pattern,
21
+ initiator,
22
+ prologue,
23
+ s,
24
+ e,
25
+ rs,
26
+ re,
27
+ psks
28
+ );
29
+ }
30
+
31
+ write_message(payload, message_buffer) {
32
+ return this._handshakestate.write_message(payload, message_buffer);
33
+ }
34
+
35
+ read_message(message, payload_buffer) {
36
+ return this._handshakestate.read_message(message, payload_buffer);
37
+ }
38
+
39
+ get re() {
40
+ return this._handshakestate.re;
41
+ }
42
+
43
+ get rs() {
44
+ return this._handshakestate.rs;
45
+ }
46
+
47
+ get s() {
48
+ return this._handshakestate.s;
49
+ }
50
+
51
+ get e() {
52
+ return this._handshakestate.e;
53
+ }
54
+ }
55
+
56
+ module.exports = ForwarderHandshakeState;
@@ -0,0 +1,273 @@
1
+ const CipherState = require('./CipherState');
2
+ const WASymmetricState = require('./WASymmetricState');
3
+ const HandshakeState = require('./HandshakeState');
4
+ const SwitchableHandshakeState = require('./SwitchableHandshakeState');
5
+ const IKHandshakePattern = require('./handshakepatterns/IKHandshakePattern');
6
+ const FallbackPatternModifier = require('./FallbackPatternModifier');
7
+ const XXHandshakePattern = require('./handshakepatterns/XXHandshakePattern');
8
+
9
+ const Decoder = require('../bin/decoder');
10
+ const { ClientHello, HandshakeMessage } = require('../protobuf/pb');
11
+
12
+ class HandShake {
13
+ constructor(waSocketClient) {
14
+ this.waSocketClient = waSocketClient;
15
+
16
+ const versionMajor = 5;
17
+ const versionMinor = 2;
18
+ const waversion = Buffer.from([versionMajor, versionMinor]);
19
+
20
+ this.HEADER = Buffer.concat([Buffer.from('WA'), waversion]);
21
+ this._prologue = Buffer.concat([Buffer.from('WA'), waversion]);
22
+
23
+ this.EDGE_HEADER = Buffer.concat([Buffer.from('ED'), Buffer.from([0, 1])]);
24
+
25
+ const cipherState = new CipherState('AESGCM');
26
+ const waSymmetricState = new WASymmetricState(cipherState);
27
+ const handshakeState = new SwitchableHandshakeState(new HandshakeState(waSymmetricState));
28
+ this.handshakeState = handshakeState;
29
+
30
+ this.isHandShake = false;
31
+
32
+ this.waSocketClient
33
+ .on('data', async data => {
34
+ if (!this.isHandShake) return;
35
+ try {
36
+ const buffer = this.decrypt(data);
37
+ const decoder = new Decoder();
38
+ const node = await decoder.getProtocolTreeNode(buffer);
39
+ if (node && node.tag) {
40
+ this.waSocketClient.emit('node', node);
41
+ }
42
+ const str = node.toString();
43
+ console.info('recv ==>', str);
44
+ } catch (e) {
45
+ console.error('解包失败', e);
46
+ }
47
+ })
48
+ // TODO
49
+ .on('sendencryptmessage', async data => {
50
+ this.waSocketClient.send(this.encrypt(data), true);
51
+ });
52
+ }
53
+
54
+ toBuffer(plaintext) {
55
+ if (typeof plaintext === 'undefined') return Buffer.alloc(0);
56
+ return typeof plaintext === 'string'
57
+ ? Buffer.from(plaintext, 'base64')
58
+ : Buffer.from(plaintext);
59
+ }
60
+
61
+ decrypt(message) {
62
+ return this.recvCipherState.decryptAES256GCM(Buffer.alloc(0), this.toBuffer(message));
63
+ }
64
+
65
+ encrypt(message) {
66
+ return this.sendCipherState.encryptAES256GCM(Buffer.alloc(0), this.toBuffer(message));
67
+ }
68
+
69
+ async start(config, serverStaticPublic) {
70
+ let clientStaticKeypair = Buffer.from(config.clientStaticKeypair, 'base64');
71
+ clientStaticKeypair = {
72
+ private: clientStaticKeypair.slice(0, 32),
73
+ public: clientStaticKeypair.slice(-32),
74
+ };
75
+
76
+ let cipherstatepair;
77
+ if (serverStaticPublic) {
78
+ try {
79
+ console.debug('perform use startHandshakeIK');
80
+ cipherstatepair = await this.startHandshakeIK(
81
+ config,
82
+ clientStaticKeypair,
83
+ serverStaticPublic
84
+ );
85
+ } catch (e) {
86
+ console.error('perform use startHandshakeIK error', e);
87
+ console.debug('perform use switchHandshakeXXFallback');
88
+ cipherstatepair = await this.switchHandshakeXXFallback(
89
+ clientStaticKeypair,
90
+ this.createPayload(config),
91
+ e.serverHello
92
+ );
93
+ }
94
+ } else {
95
+ console.debug('perform use startHandshakeXX');
96
+ cipherstatepair = await this.startHandshakeXX(config, clientStaticKeypair);
97
+ }
98
+ console.info('Handshake success.');
99
+ this.isHandShake = true;
100
+ return cipherstatepair;
101
+ }
102
+
103
+ async switchHandshakeXXFallback(clientStaticKeypair, payload, serverHello) {
104
+ console.debug('switchHandshakeXXFallback');
105
+ this.handshakeState.switch(
106
+ new FallbackPatternModifier().modify(new XXHandshakePattern()),
107
+ true,
108
+ this._prologue,
109
+ clientStaticKeypair
110
+ );
111
+ const payload_buffer = [];
112
+ const message = Buffer.concat([
113
+ Buffer.from(serverHello.ephemeral, 'base64'),
114
+ Buffer.from(serverHello.static, 'base64'),
115
+ Buffer.from(serverHello.payload, 'base64'),
116
+ ]);
117
+ this.handshakeState.read_message(message, payload_buffer);
118
+
119
+ const message_array = [];
120
+ const cipherpair = this.handshakeState.write_message(payload, message_array);
121
+ const message_buffer = Buffer.from(message_array);
122
+
123
+ const clientFinishBuffer = HandshakeMessage.HandshakeMessage.encode(
124
+ HandshakeMessage.HandshakeMessage.create({
125
+ clientFinish: {
126
+ static: message_buffer.slice(0, 48),
127
+ payload: message_buffer.slice(48),
128
+ },
129
+ })
130
+ ).finish();
131
+ this.waSocketClient.send(clientFinishBuffer);
132
+ // this.waSocketClient.setSendLogin(true);
133
+ [this.sendCipherState, this.recvCipherState] = cipherpair;
134
+ // 登陆成功
135
+ return cipherpair;
136
+ }
137
+
138
+ async startHandshakeIK(config, clientStaticKeypair, serverStaticPublic) {
139
+ const ik = new IKHandshakePattern();
140
+ this.handshakeState.initialize(
141
+ ik,
142
+ true,
143
+ this._prologue,
144
+ clientStaticKeypair,
145
+ null,
146
+ serverStaticPublic
147
+ );
148
+
149
+ const clientInfoBuffer = this.createPayload(config);
150
+ const messageArray = [];
151
+ this.handshakeState.write_message(clientInfoBuffer, messageArray);
152
+
153
+ const messageBuffer = Buffer.from(messageArray);
154
+ const ephemeral_public = messageBuffer.slice(0, 32);
155
+ const static_public = messageBuffer.slice(32, 32 + 48);
156
+ const payload = messageBuffer.slice(32 + 48);
157
+ const clientHelloBuffer = HandshakeMessage.HandshakeMessage.encode(
158
+ HandshakeMessage.HandshakeMessage.create({
159
+ clientHello: {
160
+ ephemeral: ephemeral_public,
161
+ static: static_public,
162
+ payload,
163
+ },
164
+ })
165
+ ).finish();
166
+
167
+ this.waSocketClient.send(this.EDGE_HEADER, false);
168
+ this.waSocketClient.send(config.edgeRoutingInfo || 'CAwIBQ==');
169
+ this.waSocketClient.send(this.HEADER, false);
170
+ this.waSocketClient.send(clientHelloBuffer);
171
+
172
+ return new Promise((resolve, reject) => {
173
+ this.waSocketClient.once('data', async buffer => {
174
+ this.isHandShake = true;
175
+ const { serverHello } = HandshakeMessage.HandshakeMessage.decode(buffer).toJSON();
176
+ console.debug('serverHello', serverHello);
177
+ if (serverHello.static && serverHello.static.length) {
178
+ const e = new Error('');
179
+ e.serverHello = serverHello;
180
+ reject(e);
181
+ return;
182
+ }
183
+
184
+ serverHello.static = Buffer.alloc(0);
185
+ if (serverHello.static && serverHello.static.length) {
186
+ serverHello.static = Buffer.from(serverHello.static, 'base64');
187
+ }
188
+
189
+ const message = Buffer.concat([
190
+ Buffer.from(serverHello.ephemeral, 'base64'),
191
+ serverHello.static,
192
+ Buffer.from(serverHello.payload, 'base64'),
193
+ ]);
194
+ const cipherpair = this.handshakeState.read_message(message, []);
195
+ [this.sendCipherState, this.recvCipherState] = cipherpair;
196
+ // 登陆成功
197
+ resolve(cipherpair);
198
+ });
199
+ });
200
+ }
201
+
202
+ generatePushName() {
203
+ const strings = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
204
+ const len = Math.floor(Math.random() * 4 + 4);
205
+ let name = '';
206
+ const stringsLen = strings.length;
207
+ for (let i = 0; i < len; i++) {
208
+ name += strings[Math.floor(Math.random() * stringsLen)];
209
+ }
210
+ return name;
211
+ }
212
+
213
+ createPayload(config) {
214
+ console.debug('client version:', config.version);
215
+ console.debug(
216
+ 'account:',
217
+ config.cc,
218
+ config.mobile,
219
+ config.mcc,
220
+ config.mnc,
221
+ 'platform',
222
+ config.platform
223
+ );
224
+ console.debug('client info:', config.oSVersion, config.manufacturer, config.deviceName);
225
+ const appVersion = config.version.split('.');
226
+ const clientConfig = {
227
+ username: config.mobile,
228
+ passive: false, // 是否接收被动消息
229
+ // passive: config.passive,
230
+ useragent: {
231
+ platform: config.platform || 0,
232
+ appVersion: {
233
+ primary: appVersion[0],
234
+ secondary: appVersion[1],
235
+ tertiary: appVersion[2],
236
+ quaternary: appVersion[3],
237
+ },
238
+ mcc: config.mcc || '000',
239
+ mnc: config.mnc || '000',
240
+ osVersion: config.oSVersion,
241
+ manufacturer: config.manufacturer,
242
+ device: config.deviceName,
243
+ osBuildNumber: config.oSVersion,
244
+ phoneId: config.fdid || '',
245
+ localeLanguageIso_639_1: config.lg || 'en',
246
+ localeCountryIso_3166_1Alpha_2: config.lc || 'US',
247
+ device2: 'unknown',
248
+ // releaseChannel: 0,
249
+ },
250
+ pushName: config.pushName || this.generatePushName(),
251
+ sessionId: Math.floor(Math.random() * 2 ** 32) + 1,
252
+ shortConnect: false,
253
+ connectType: 1,
254
+ // dnsSource: {
255
+ // dnsMethod: 2,
256
+ // appCached: 1,
257
+ // },
258
+ // connectAttemptCount: 1,
259
+ // tag23: 1,
260
+ // tag24: 134,
261
+ };
262
+ if (Number(config.platform) === 10) {
263
+ clientConfig.tag23 = 1;
264
+ clientConfig.tag24 = 5;
265
+ }
266
+
267
+ const ClientConfig = ClientHello.C2S;
268
+ const buffer = ClientConfig.encode(ClientConfig.create(clientConfig)).finish();
269
+ return buffer;
270
+ }
271
+ }
272
+
273
+ module.exports = HandShake;
@@ -0,0 +1,62 @@
1
+ class HandshakePattern {
2
+ REGEX_PATTERN_NAME_MODIFIERS = new RegExp('([A-Z1-9]{1,4})([a-z0-9+]+)*');
3
+
4
+ TEMPLATE_REPR = '{name}:\n{patterns}';
5
+
6
+ TEMPLATE_REPR_PATTERNS_WITH_PRE = '{pre_patterns}\n ...\n{message_patterns}';
7
+
8
+ TEMPLATE_REPR_MESSAGE_SEND = ' -> {tokens}';
9
+
10
+ TEMPLATE_REPR_MESSAGE_RECV = ' <- {tokens}';
11
+
12
+ constructor(
13
+ name,
14
+ message_patterns,
15
+ initiator_pre_messages = null,
16
+ responder_pre_message_pattern = null,
17
+ interpret_as_bob = false
18
+ ) {
19
+ this._name = name; // type: str
20
+ // this._origin_pattern, this._modifiers = this.__class__.parse_handshakepattern(this._name)
21
+ this._origin_pattern = name;
22
+ this._modifiers = [];
23
+ this._message_patterns = message_patterns; // type: tuple[tuple[str]]
24
+ this._initiator_pre_message_pattern = initiator_pre_messages || []; // type: tuple[str]
25
+ this._responder_pre_message_pattern = responder_pre_message_pattern || []; // type: tuple[str]
26
+ this._interpret_as_bob = interpret_as_bob; // type: bool
27
+ }
28
+
29
+ get name() {
30
+ return this._name;
31
+ }
32
+
33
+ get interpret_as_bob() {
34
+ return this._interpret_as_bob;
35
+ }
36
+
37
+ get message_patterns() {
38
+ return this._message_patterns;
39
+ }
40
+
41
+ get initiator_pre_message_pattern() {
42
+ return this._initiator_pre_message_pattern;
43
+ }
44
+
45
+ get responder_pre_message_pattern() {
46
+ return this._responder_pre_message_pattern;
47
+ }
48
+
49
+ get origin_name() {
50
+ return this._origin_pattern;
51
+ }
52
+
53
+ get modifiers() {
54
+ return this._modifiers;
55
+ }
56
+
57
+ // static parse_handshakepattern(handshake_pattern_name) {
58
+ // let matches = handshake_pattern_name.search(this.REGEX_PATTERN_NAME_MODIFIERS)
59
+ // }
60
+ }
61
+
62
+ module.exports = HandshakePattern;
@@ -0,0 +1,223 @@
1
+ const utils = require('../lib/utils');
2
+ const crypto = require('./crypto');
3
+
4
+ class HandshakeState {
5
+ _TEMPLATE_PROTOCOL_NAME = 'Noise_{handshake}_{dh}_{cipher}_{hash}';
6
+
7
+ constructor(symmetricstate) {
8
+ this._symmetricstate = symmetricstate; // type: SymmetricState
9
+ this._s = null; // type: KeyPair config.client_static_keypair
10
+ this._e = null; // type: KeyPair | None 本地生成的一对公私钥
11
+ this._rs = null; // type: PublicKey | None 服务端公钥
12
+ this._re = null; // type: PublicKey | None 本次会话,服务端返回的公钥
13
+ this._initiator = null;
14
+ this._message_patterns = null; // type: list[tuple[str]]
15
+ this._protocol_name = null; // type: str | None
16
+ }
17
+
18
+ get protocol_name() {
19
+ return this._protocol_name;
20
+ }
21
+
22
+ get symmetricstate() {
23
+ return this._symmetricstate;
24
+ }
25
+
26
+ get rs() {
27
+ return this._rs;
28
+ }
29
+
30
+ get re() {
31
+ return this._re;
32
+ }
33
+
34
+ get s() {
35
+ return this._s;
36
+ }
37
+
38
+ get e() {
39
+ return this._e;
40
+ }
41
+
42
+ get dh() {
43
+ return this._dh;
44
+ }
45
+
46
+ initialize(
47
+ handshake_pattern,
48
+ initiator, // xx 时 为 true
49
+ prologue, // Buffer.from('WA',[4,1]) = Buffer.from([0x57,0x41,0x04,0x01])
50
+ s = null,
51
+ e = null,
52
+ rs = null,
53
+ re = null,
54
+ psks = null
55
+ ) {
56
+ // console.log('handshake_pattern', handshake_pattern);
57
+ // Noise_XX_25519_AESGCM_SHA256
58
+ this._protocol_name = this._derive_protocol_name(handshake_pattern.name);
59
+ this._symmetricstate.initialize_symmetric(this._protocol_name);
60
+ this._symmetricstate.mix_hash(prologue);
61
+ this._initiator = initiator;
62
+ this._s = s;
63
+ this._e = e;
64
+ this._rs = rs;
65
+ this._re = re;
66
+ this._psks = psks;
67
+ this._pskmode = handshake_pattern.name.indexOf('psk') !== -1;
68
+ if (
69
+ handshake_pattern.initiator_pre_message_pattern.length ||
70
+ handshake_pattern.responder_pre_message_pattern.length
71
+ ) {
72
+ if (initiator) {
73
+ for (let i = 0; i < handshake_pattern.initiator_pre_message_pattern.length; i++) {
74
+ const token = handshake_pattern.initiator_pre_message_pattern[i];
75
+ if (token === 's') {
76
+ this._symmetricstate.mix_hash(s.public);
77
+ }
78
+ if (token === 'e') {
79
+ this._symmetricstate.mix_hash(e.public);
80
+ if (this._pskmode) {
81
+ this._symmetricstate.mix_key(e.public);
82
+ }
83
+ }
84
+ }
85
+ for (let i = 0; i < handshake_pattern.responder_pre_message_pattern.length; i++) {
86
+ const token = handshake_pattern.responder_pre_message_pattern[i];
87
+ if (token === 's') {
88
+ this._symmetricstate.mix_hash(rs);
89
+ }
90
+ if (token === 'e') {
91
+ this._symmetricstate.mix_hash(re);
92
+ }
93
+ }
94
+ } else {
95
+ for (let i = 0; i < handshake_pattern.initiator_pre_message_pattern.length; i++) {
96
+ const token = handshake_pattern.initiator_pre_message_pattern[i];
97
+ if (token === 's') {
98
+ this._symmetricstate.mix_hash(rs);
99
+ }
100
+ if (token === 'e') {
101
+ this._symmetricstate.mix_hash(re);
102
+ }
103
+ }
104
+ for (let i = 0; i < handshake_pattern.responder_pre_message_pattern.length; i++) {
105
+ const token = handshake_pattern.responder_pre_message_pattern[i];
106
+ if (token === 's') {
107
+ this._symmetricstate.mix_hash(s.public);
108
+ }
109
+ if (token === 'e') {
110
+ this._symmetricstate.mix_hash(e.public);
111
+ if (this._pskmode) {
112
+ this._symmetricstate.mix_key(e.public);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ this._message_patterns = handshake_pattern.message_patterns;
119
+ }
120
+
121
+ _derive_protocol_name(handshake_pattern_name) {
122
+ return `Noise_${handshake_pattern_name}_25519_${this._symmetricstate.ciphername}_${this._symmetricstate.hashname}`;
123
+ }
124
+
125
+ write_message(payload, message_buffer) {
126
+ const message_pattern = this._message_patterns.shift();
127
+ for (let i = 0; i < message_pattern.length; i++) {
128
+ const token = message_pattern[i];
129
+ if (token === 'e') {
130
+ this._e = utils.generateKeyPair();
131
+ this._e.public.map(val => message_buffer.push(val));
132
+ this._symmetricstate.mix_hash(this._e.public);
133
+ if (this._pskmode) {
134
+ this._symmetricstate.mix_key(this._e.public);
135
+ }
136
+ } else if (token === 's') {
137
+ const encrypted = this._symmetricstate.encrypt_and_hash(this._s.public);
138
+ encrypted.map(val => message_buffer.push(val));
139
+ } else if (token === 'ee') {
140
+ this._symmetricstate.mix_key(crypto.dh(this._e, this._re));
141
+ } else if (token === 'es') {
142
+ if (this._initiator) {
143
+ this._symmetricstate.mix_key(crypto.dh(this._e, this._rs));
144
+ } else {
145
+ this._symmetricstate.mix_key(crypto.dh(this._s, this._re));
146
+ }
147
+ } else if (token === 'se') {
148
+ if (this._initiator) {
149
+ this._symmetricstate.mix_key(crypto.dh(this._s, this._re));
150
+ } else {
151
+ this._symmetricstate.mix_key(crypto.dh(this._e, this._rs));
152
+ }
153
+ } else if (token === 'ss') {
154
+ this._symmetricstate.mix_key(crypto.dh(this._s, this._rs));
155
+ } else if (token === 'psk') {
156
+ this._symmetricstate.mix_key_and_hash(this._psks.shift());
157
+ } else {
158
+ throw new Error(`Unsupported token ${token} found in message_pattern`);
159
+ }
160
+ }
161
+ const res = this._symmetricstate.encrypt_and_hash(payload);
162
+ if (res && res.length) {
163
+ res.map(val => message_buffer.push(val));
164
+ }
165
+ if (this._message_patterns.length === 0) {
166
+ return this._symmetricstate.split();
167
+ }
168
+ }
169
+
170
+ read_message(message, payload_buffer) {
171
+ const message_pattern = this._message_patterns.shift();
172
+ let temp;
173
+ for (let i = 0; i < message_pattern.length; i++) {
174
+ const token = message_pattern[i];
175
+ console.debug('read_message', token);
176
+ if (token === 'e') {
177
+ this._re = message.slice(0, 32);
178
+ message = message.slice(32);
179
+ this._symmetricstate.mix_hash(this._re);
180
+ if (this._pskmode) {
181
+ this._symmetricstate.mix_key(this._re);
182
+ }
183
+ } else if (token === 's') {
184
+ if (this._symmetricstate.cipherstate_has_key()) {
185
+ temp = message.slice(0, 32 + 16);
186
+ } else {
187
+ temp = message.slice(0, 32);
188
+ }
189
+ message = message.slice(temp.length);
190
+ this._rs = this._symmetricstate.decrypt_and_hash(temp);
191
+ } else if (token === 'ee') {
192
+ this._symmetricstate.mix_key(crypto.dh(this._e, this._re));
193
+ } else if (token === 'es') {
194
+ if (this._initiator) {
195
+ this._symmetricstate.mix_key(crypto.dh(this._e, this._rs));
196
+ } else {
197
+ this._symmetricstate.mix_key(crypto.dh(this._s, this._re));
198
+ }
199
+ } else if (token === 'se') {
200
+ if (this._initiator) {
201
+ this._symmetricstate.mix_key(crypto.dh(this._s, this._re));
202
+ } else {
203
+ this._symmetricstate.mix_key(crypto.dh(this._e, this._rs));
204
+ }
205
+ } else if (token === 'ss') {
206
+ this._symmetricstate.mix_key(crypto.dh(this._s, this._rs));
207
+ } else if (token === 'psk') {
208
+ this._symmetricstate.mix_key_and_hash(this._psks.shift());
209
+ } else {
210
+ throw new Error(`Unsupported token ${token} found in message_pattern`);
211
+ }
212
+ }
213
+ const res = this._symmetricstate.decrypt_and_hash(message);
214
+ if (res && res.length) {
215
+ res.map(val => payload_buffer.push(val));
216
+ }
217
+ if (this._message_patterns.length === 0) {
218
+ return this._symmetricstate.split();
219
+ }
220
+ }
221
+ }
222
+
223
+ module.exports = HandshakeState;