@electerm/ssh2 0.8.11 → 1.5.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.
@@ -0,0 +1,1602 @@
1
+ // TODO:
2
+ // * make max packet size configurable
3
+ // * if decompression is enabled, use `._packet` in decipher instances as
4
+ // input to (sync) zlib inflater with appropriate offset and length to
5
+ // avoid an additional copy of payload data before inflation
6
+ // * factor decompression status into packet length checks
7
+ 'use strict';
8
+
9
+ const {
10
+ createCipheriv, createDecipheriv, createHmac, randomFillSync, timingSafeEqual
11
+ } = require('crypto');
12
+
13
+ const { readUInt32BE, writeUInt32BE } = require('./utils.js');
14
+
15
+ const FastBuffer = Buffer[Symbol.species];
16
+ const MAX_SEQNO = 2 ** 32 - 1;
17
+ const EMPTY_BUFFER = Buffer.alloc(0);
18
+ const BUF_INT = Buffer.alloc(4);
19
+ const DISCARD_CACHE = new Map();
20
+ const MAX_PACKET_SIZE = 35000;
21
+
22
+ let binding;
23
+ let AESGCMCipher;
24
+ let ChaChaPolyCipher;
25
+ let GenericCipher;
26
+ let AESGCMDecipher;
27
+ let ChaChaPolyDecipher;
28
+ let GenericDecipher;
29
+ try {
30
+ binding = require('./crypto/build/Release/sshcrypto.node');
31
+ ({ AESGCMCipher, ChaChaPolyCipher, GenericCipher,
32
+ AESGCMDecipher, ChaChaPolyDecipher, GenericDecipher } = binding);
33
+ } catch {}
34
+
35
+ const CIPHER_STREAM = 1 << 0;
36
+ const CIPHER_INFO = (() => {
37
+ function info(sslName, blockLen, keyLen, ivLen, authLen, discardLen, flags) {
38
+ return {
39
+ sslName,
40
+ blockLen,
41
+ keyLen,
42
+ ivLen: (ivLen !== 0 || (flags & CIPHER_STREAM)
43
+ ? ivLen
44
+ : blockLen),
45
+ authLen,
46
+ discardLen,
47
+ stream: !!(flags & CIPHER_STREAM),
48
+ };
49
+ }
50
+
51
+ return {
52
+ 'chacha20-poly1305@openssh.com':
53
+ info('chacha20', 8, 64, 0, 16, 0, CIPHER_STREAM),
54
+
55
+ 'aes128-gcm': info('aes-128-gcm', 16, 16, 12, 16, 0, CIPHER_STREAM),
56
+ 'aes256-gcm': info('aes-256-gcm', 16, 32, 12, 16, 0, CIPHER_STREAM),
57
+ 'aes128-gcm@openssh.com':
58
+ info('aes-128-gcm', 16, 16, 12, 16, 0, CIPHER_STREAM),
59
+ 'aes256-gcm@openssh.com':
60
+ info('aes-256-gcm', 16, 32, 12, 16, 0, CIPHER_STREAM),
61
+
62
+ 'aes128-cbc': info('aes-128-cbc', 16, 16, 0, 0, 0, 0),
63
+ 'aes192-cbc': info('aes-192-cbc', 16, 24, 0, 0, 0, 0),
64
+ 'aes256-cbc': info('aes-256-cbc', 16, 32, 0, 0, 0, 0),
65
+ 'rijndael-cbc@lysator.liu.se': info('aes-256-cbc', 16, 32, 0, 0, 0, 0),
66
+ '3des-cbc': info('des-ede3-cbc', 8, 24, 0, 0, 0, 0),
67
+ 'blowfish-cbc': info('bf-cbc', 8, 16, 0, 0, 0, 0),
68
+ 'idea-cbc': info('idea-cbc', 8, 16, 0, 0, 0, 0),
69
+ 'cast128-cbc': info('cast-cbc', 8, 16, 0, 0, 0, 0),
70
+
71
+ 'aes128-ctr': info('aes-128-ctr', 16, 16, 16, 0, 0, CIPHER_STREAM),
72
+ 'aes192-ctr': info('aes-192-ctr', 16, 24, 16, 0, 0, CIPHER_STREAM),
73
+ 'aes256-ctr': info('aes-256-ctr', 16, 32, 16, 0, 0, CIPHER_STREAM),
74
+ '3des-ctr': info('des-ede3', 8, 24, 8, 0, 0, CIPHER_STREAM),
75
+ 'blowfish-ctr': info('bf-ecb', 8, 16, 8, 0, 0, CIPHER_STREAM),
76
+ 'cast128-ctr': info('cast5-ecb', 8, 16, 8, 0, 0, CIPHER_STREAM),
77
+
78
+ /* The "arcfour128" algorithm is the RC4 cipher, as described in
79
+ [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
80
+ generated by the cipher MUST be discarded, and the first byte of the
81
+ first encrypted packet MUST be encrypted using the 1537th byte of
82
+ keystream.
83
+
84
+ -- http://tools.ietf.org/html/rfc4345#section-4 */
85
+ 'arcfour': info('rc4', 8, 16, 0, 0, 1536, CIPHER_STREAM),
86
+ 'arcfour128': info('rc4', 8, 16, 0, 0, 1536, CIPHER_STREAM),
87
+ 'arcfour256': info('rc4', 8, 32, 0, 0, 1536, CIPHER_STREAM),
88
+ 'arcfour512': info('rc4', 8, 64, 0, 0, 1536, CIPHER_STREAM),
89
+ };
90
+ })();
91
+
92
+ const MAC_INFO = (() => {
93
+ function info(sslName, len, actualLen, isETM) {
94
+ return {
95
+ sslName,
96
+ len,
97
+ actualLen,
98
+ isETM,
99
+ };
100
+ }
101
+
102
+ return {
103
+ 'hmac-md5': info('md5', 16, 16, false),
104
+ 'hmac-md5-96': info('md5', 16, 12, false),
105
+ 'hmac-ripemd160': info('ripemd160', 20, 20, false),
106
+ 'hmac-sha1': info('sha1', 20, 20, false),
107
+ 'hmac-sha1-etm@openssh.com': info('sha1', 20, 20, true),
108
+ 'hmac-sha1-96': info('sha1', 20, 12, false),
109
+ 'hmac-sha2-256': info('sha256', 32, 32, false),
110
+ 'hmac-sha2-256-etm@openssh.com': info('sha256', 32, 32, true),
111
+ 'hmac-sha2-256-96': info('sha256', 32, 12, false),
112
+ 'hmac-sha2-512': info('sha512', 64, 64, false),
113
+ 'hmac-sha2-512-etm@openssh.com': info('sha512', 64, 64, true),
114
+ 'hmac-sha2-512-96': info('sha512', 64, 12, false),
115
+ };
116
+ })();
117
+
118
+
119
+ // Should only_be used during the initial handshake
120
+ class NullCipher {
121
+ constructor(seqno, onWrite) {
122
+ this.outSeqno = seqno;
123
+ this._onWrite = onWrite;
124
+ this._dead = false;
125
+ }
126
+ free() {
127
+ this._dead = true;
128
+ }
129
+ allocPacket(payloadLen) {
130
+ let pktLen = 4 + 1 + payloadLen;
131
+ let padLen = 8 - (pktLen & (8 - 1));
132
+ if (padLen < 4)
133
+ padLen += 8;
134
+ pktLen += padLen;
135
+
136
+ const packet = Buffer.allocUnsafe(pktLen);
137
+
138
+ writeUInt32BE(packet, pktLen - 4, 0);
139
+ packet[4] = padLen;
140
+
141
+ randomFillSync(packet, 5 + payloadLen, padLen);
142
+
143
+ return packet;
144
+ }
145
+ encrypt(packet) {
146
+ // `packet` === unencrypted packet
147
+
148
+ if (this._dead)
149
+ return;
150
+
151
+ this._onWrite(packet);
152
+
153
+ this.outSeqno = (this.outSeqno + 1) >>> 0;
154
+ }
155
+ }
156
+
157
+
158
+ const POLY1305_ZEROS = Buffer.alloc(32);
159
+ const POLY1305_OUT_COMPUTE = Buffer.alloc(16);
160
+ let POLY1305_WASM_MODULE;
161
+ let POLY1305_RESULT_MALLOC;
162
+ let poly1305_auth;
163
+ class ChaChaPolyCipherNative {
164
+ constructor(config) {
165
+ const enc = config.outbound;
166
+ this.outSeqno = enc.seqno;
167
+ this._onWrite = enc.onWrite;
168
+ this._encKeyMain = enc.cipherKey.slice(0, 32);
169
+ this._encKeyPktLen = enc.cipherKey.slice(32);
170
+ this._dead = false;
171
+ }
172
+ free() {
173
+ this._dead = true;
174
+ }
175
+ allocPacket(payloadLen) {
176
+ let pktLen = 4 + 1 + payloadLen;
177
+ let padLen = 8 - ((pktLen - 4) & (8 - 1));
178
+ if (padLen < 4)
179
+ padLen += 8;
180
+ pktLen += padLen;
181
+
182
+ const packet = Buffer.allocUnsafe(pktLen);
183
+
184
+ writeUInt32BE(packet, pktLen - 4, 0);
185
+ packet[4] = padLen;
186
+
187
+ randomFillSync(packet, 5 + payloadLen, padLen);
188
+
189
+ return packet;
190
+ }
191
+ encrypt(packet) {
192
+ // `packet` === unencrypted packet
193
+
194
+ if (this._dead)
195
+ return;
196
+
197
+ // Generate Poly1305 key
198
+ POLY1305_OUT_COMPUTE[0] = 0; // Set counter to 0 (little endian)
199
+ writeUInt32BE(POLY1305_OUT_COMPUTE, this.outSeqno, 12);
200
+ const polyKey =
201
+ createCipheriv('chacha20', this._encKeyMain, POLY1305_OUT_COMPUTE)
202
+ .update(POLY1305_ZEROS);
203
+
204
+ // Encrypt packet length
205
+ const pktLenEnc =
206
+ createCipheriv('chacha20', this._encKeyPktLen, POLY1305_OUT_COMPUTE)
207
+ .update(packet.slice(0, 4));
208
+ this._onWrite(pktLenEnc);
209
+
210
+ // Encrypt rest of packet
211
+ POLY1305_OUT_COMPUTE[0] = 1; // Set counter to 1 (little endian)
212
+ const payloadEnc =
213
+ createCipheriv('chacha20', this._encKeyMain, POLY1305_OUT_COMPUTE)
214
+ .update(packet.slice(4));
215
+ this._onWrite(payloadEnc);
216
+
217
+ // Calculate Poly1305 MAC
218
+ poly1305_auth(POLY1305_RESULT_MALLOC,
219
+ pktLenEnc,
220
+ pktLenEnc.length,
221
+ payloadEnc,
222
+ payloadEnc.length,
223
+ polyKey);
224
+ const mac = Buffer.allocUnsafe(16);
225
+ mac.set(
226
+ new Uint8Array(POLY1305_WASM_MODULE.HEAPU8.buffer,
227
+ POLY1305_RESULT_MALLOC,
228
+ 16),
229
+ 0
230
+ );
231
+ this._onWrite(mac);
232
+
233
+ this.outSeqno = (this.outSeqno + 1) >>> 0;
234
+ }
235
+ }
236
+
237
+ class ChaChaPolyCipherBinding {
238
+ constructor(config) {
239
+ const enc = config.outbound;
240
+ this.outSeqno = enc.seqno;
241
+ this._onWrite = enc.onWrite;
242
+ this._instance = new ChaChaPolyCipher(enc.cipherKey);
243
+ this._dead = false;
244
+ }
245
+ free() {
246
+ this._dead = true;
247
+ this._instance.free();
248
+ }
249
+ allocPacket(payloadLen) {
250
+ let pktLen = 4 + 1 + payloadLen;
251
+ let padLen = 8 - ((pktLen - 4) & (8 - 1));
252
+ if (padLen < 4)
253
+ padLen += 8;
254
+ pktLen += padLen;
255
+
256
+ const packet = Buffer.allocUnsafe(pktLen + 16/* MAC */);
257
+
258
+ writeUInt32BE(packet, pktLen - 4, 0);
259
+ packet[4] = padLen;
260
+
261
+ randomFillSync(packet, 5 + payloadLen, padLen);
262
+
263
+ return packet;
264
+ }
265
+ encrypt(packet) {
266
+ // `packet` === unencrypted packet
267
+
268
+ if (this._dead)
269
+ return;
270
+
271
+ // Encrypts in-place
272
+ this._instance.encrypt(packet, this.outSeqno);
273
+
274
+ this._onWrite(packet);
275
+
276
+ this.outSeqno = (this.outSeqno + 1) >>> 0;
277
+ }
278
+ }
279
+
280
+
281
+ class AESGCMCipherNative {
282
+ constructor(config) {
283
+ const enc = config.outbound;
284
+ this.outSeqno = enc.seqno;
285
+ this._onWrite = enc.onWrite;
286
+ this._encSSLName = enc.cipherInfo.sslName;
287
+ this._encKey = enc.cipherKey;
288
+ this._encIV = enc.cipherIV;
289
+ this._dead = false;
290
+ }
291
+ free() {
292
+ this._dead = true;
293
+ }
294
+ allocPacket(payloadLen) {
295
+ let pktLen = 4 + 1 + payloadLen;
296
+ let padLen = 16 - ((pktLen - 4) & (16 - 1));
297
+ if (padLen < 4)
298
+ padLen += 16;
299
+ pktLen += padLen;
300
+
301
+ const packet = Buffer.allocUnsafe(pktLen);
302
+
303
+ writeUInt32BE(packet, pktLen - 4, 0);
304
+ packet[4] = padLen;
305
+
306
+ randomFillSync(packet, 5 + payloadLen, padLen);
307
+
308
+ return packet;
309
+ }
310
+ encrypt(packet) {
311
+ // `packet` === unencrypted packet
312
+
313
+ if (this._dead)
314
+ return;
315
+
316
+ const cipher = createCipheriv(this._encSSLName, this._encKey, this._encIV);
317
+ cipher.setAutoPadding(false);
318
+
319
+ const lenData = packet.slice(0, 4);
320
+ cipher.setAAD(lenData);
321
+ this._onWrite(lenData);
322
+
323
+ // Encrypt pad length, payload, and padding
324
+ const encrypted = cipher.update(packet.slice(4));
325
+ this._onWrite(encrypted);
326
+ const final = cipher.final();
327
+ // XXX: final.length === 0 always?
328
+ if (final.length)
329
+ this._onWrite(final);
330
+
331
+ // Generate MAC
332
+ const tag = cipher.getAuthTag();
333
+ this._onWrite(tag);
334
+
335
+ // Increment counter in IV by 1 for next packet
336
+ ivIncrement(this._encIV);
337
+
338
+ this.outSeqno = (this.outSeqno + 1) >>> 0;
339
+ }
340
+ }
341
+
342
+ class AESGCMCipherBinding {
343
+ constructor(config) {
344
+ const enc = config.outbound;
345
+ this.outSeqno = enc.seqno;
346
+ this._onWrite = enc.onWrite;
347
+ this._instance = new AESGCMCipher(enc.cipherInfo.sslName,
348
+ enc.cipherKey,
349
+ enc.cipherIV);
350
+ this._dead = false;
351
+ }
352
+ free() {
353
+ this._dead = true;
354
+ this._instance.free();
355
+ }
356
+ allocPacket(payloadLen) {
357
+ let pktLen = 4 + 1 + payloadLen;
358
+ let padLen = 16 - ((pktLen - 4) & (16 - 1));
359
+ if (padLen < 4)
360
+ padLen += 16;
361
+ pktLen += padLen;
362
+
363
+ const packet = Buffer.allocUnsafe(pktLen + 16/* authTag */);
364
+
365
+ writeUInt32BE(packet, pktLen - 4, 0);
366
+ packet[4] = padLen;
367
+
368
+ randomFillSync(packet, 5 + payloadLen, padLen);
369
+
370
+ return packet;
371
+ }
372
+ encrypt(packet) {
373
+ // `packet` === unencrypted packet
374
+
375
+ if (this._dead)
376
+ return;
377
+
378
+ // Encrypts in-place
379
+ this._instance.encrypt(packet);
380
+
381
+ this._onWrite(packet);
382
+
383
+ this.outSeqno = (this.outSeqno + 1) >>> 0;
384
+ }
385
+ }
386
+
387
+
388
+ class GenericCipherNative {
389
+ constructor(config) {
390
+ const enc = config.outbound;
391
+ this.outSeqno = enc.seqno;
392
+ this._onWrite = enc.onWrite;
393
+ this._encBlockLen = enc.cipherInfo.blockLen;
394
+ this._cipherInstance = createCipheriv(enc.cipherInfo.sslName,
395
+ enc.cipherKey,
396
+ enc.cipherIV);
397
+ this._macSSLName = enc.macInfo.sslName;
398
+ this._macKey = enc.macKey;
399
+ this._macActualLen = enc.macInfo.actualLen;
400
+ this._macETM = enc.macInfo.isETM;
401
+ this._aadLen = (this._macETM ? 4 : 0);
402
+ this._dead = false;
403
+
404
+ const discardLen = enc.cipherInfo.discardLen;
405
+ if (discardLen) {
406
+ let discard = DISCARD_CACHE.get(discardLen);
407
+ if (discard === undefined) {
408
+ discard = Buffer.alloc(discardLen);
409
+ DISCARD_CACHE.set(discardLen, discard);
410
+ }
411
+ this._cipherInstance.update(discard);
412
+ }
413
+ }
414
+ free() {
415
+ this._dead = true;
416
+ }
417
+ allocPacket(payloadLen) {
418
+ const blockLen = this._encBlockLen;
419
+
420
+ let pktLen = 4 + 1 + payloadLen;
421
+ let padLen = blockLen - ((pktLen - this._aadLen) & (blockLen - 1));
422
+ if (padLen < 4)
423
+ padLen += blockLen;
424
+ pktLen += padLen;
425
+
426
+ const packet = Buffer.allocUnsafe(pktLen);
427
+
428
+ writeUInt32BE(packet, pktLen - 4, 0);
429
+ packet[4] = padLen;
430
+
431
+ randomFillSync(packet, 5 + payloadLen, padLen);
432
+
433
+ return packet;
434
+ }
435
+ encrypt(packet) {
436
+ // `packet` === unencrypted packet
437
+
438
+ if (this._dead)
439
+ return;
440
+
441
+ let mac;
442
+ if (this._macETM) {
443
+ // Encrypt pad length, payload, and padding
444
+ const lenBytes = new Uint8Array(packet.buffer, packet.byteOffset, 4);
445
+ const encrypted = this._cipherInstance.update(
446
+ new Uint8Array(packet.buffer,
447
+ packet.byteOffset + 4,
448
+ packet.length - 4)
449
+ );
450
+
451
+ this._onWrite(lenBytes);
452
+ this._onWrite(encrypted);
453
+
454
+ // TODO: look into storing seqno as 4-byte buffer and incrementing like we
455
+ // do for AES-GCM IVs to avoid having to (re)write all 4 bytes every time
456
+ mac = createHmac(this._macSSLName, this._macKey);
457
+ writeUInt32BE(BUF_INT, this.outSeqno, 0);
458
+ mac.update(BUF_INT);
459
+ mac.update(lenBytes);
460
+ mac.update(encrypted);
461
+ } else {
462
+ // Encrypt length field, pad length, payload, and padding
463
+ const encrypted = this._cipherInstance.update(packet);
464
+ this._onWrite(encrypted);
465
+
466
+ // TODO: look into storing seqno as 4-byte buffer and incrementing like we
467
+ // do for AES-GCM IVs to avoid having to (re)write all 4 bytes every time
468
+ mac = createHmac(this._macSSLName, this._macKey);
469
+ writeUInt32BE(BUF_INT, this.outSeqno, 0);
470
+ mac.update(BUF_INT);
471
+ mac.update(packet);
472
+ }
473
+
474
+ let digest = mac.digest();
475
+ if (digest.length > this._macActualLen)
476
+ digest = digest.slice(0, this._macActualLen);
477
+ this._onWrite(digest);
478
+
479
+ this.outSeqno = (this.outSeqno + 1) >>> 0;
480
+ }
481
+ }
482
+
483
+ class GenericCipherBinding {
484
+ constructor(config) {
485
+ const enc = config.outbound;
486
+ this.outSeqno = enc.seqno;
487
+ this._onWrite = enc.onWrite;
488
+ this._encBlockLen = enc.cipherInfo.blockLen;
489
+ this._macLen = enc.macInfo.len;
490
+ this._macActualLen = enc.macInfo.actualLen;
491
+ this._aadLen = (enc.macInfo.isETM ? 4 : 0);
492
+ this._instance = new GenericCipher(enc.cipherInfo.sslName,
493
+ enc.cipherKey,
494
+ enc.cipherIV,
495
+ enc.macInfo.sslName,
496
+ enc.macKey,
497
+ enc.macInfo.isETM);
498
+ this._dead = false;
499
+ }
500
+ free() {
501
+ this._dead = true;
502
+ this._instance.free();
503
+ }
504
+ allocPacket(payloadLen) {
505
+ const blockLen = this._encBlockLen;
506
+
507
+ let pktLen = 4 + 1 + payloadLen;
508
+ let padLen = blockLen - ((pktLen - this._aadLen) & (blockLen - 1));
509
+ if (padLen < 4)
510
+ padLen += blockLen;
511
+ pktLen += padLen;
512
+
513
+ const packet = Buffer.allocUnsafe(pktLen + this._macLen);
514
+
515
+ writeUInt32BE(packet, pktLen - 4, 0);
516
+ packet[4] = padLen;
517
+
518
+ randomFillSync(packet, 5 + payloadLen, padLen);
519
+
520
+ return packet;
521
+ }
522
+ encrypt(packet) {
523
+ // `packet` === unencrypted packet
524
+
525
+ if (this._dead)
526
+ return;
527
+
528
+ // Encrypts in-place
529
+ this._instance.encrypt(packet, this.outSeqno);
530
+
531
+ if (this._macActualLen < this._macLen) {
532
+ packet = new FastBuffer(packet.buffer,
533
+ packet.byteOffset,
534
+ (packet.length
535
+ - (this._macLen - this._macActualLen)));
536
+ }
537
+ this._onWrite(packet);
538
+
539
+ this.outSeqno = (this.outSeqno + 1) >>> 0;
540
+ }
541
+ }
542
+
543
+
544
+ class NullDecipher {
545
+ constructor(seqno, onPayload) {
546
+ this.inSeqno = seqno;
547
+ this._onPayload = onPayload;
548
+ this._len = 0;
549
+ this._lenBytes = 0;
550
+ this._packet = null;
551
+ this._packetPos = 0;
552
+ }
553
+ free() {}
554
+ decrypt(data, p, dataLen) {
555
+ while (p < dataLen) {
556
+ // Read packet length
557
+ if (this._lenBytes < 4) {
558
+ let nb = Math.min(4 - this._lenBytes, dataLen - p);
559
+
560
+ this._lenBytes += nb;
561
+ while (nb--)
562
+ this._len = (this._len << 8) + data[p++];
563
+
564
+ if (this._lenBytes < 4)
565
+ return;
566
+
567
+ if (this._len > MAX_PACKET_SIZE
568
+ || this._len < 8
569
+ || (4 + this._len & 7) !== 0) {
570
+ throw new Error('Bad packet length');
571
+ }
572
+ if (p >= dataLen)
573
+ return;
574
+ }
575
+
576
+ // Read padding length, payload, and padding
577
+ if (this._packetPos < this._len) {
578
+ const nb = Math.min(this._len - this._packetPos, dataLen - p);
579
+ let chunk;
580
+ if (p !== 0 || nb !== dataLen)
581
+ chunk = new Uint8Array(data.buffer, data.byteOffset + p, nb);
582
+ else
583
+ chunk = data;
584
+ if (nb === this._len) {
585
+ this._packet = chunk;
586
+ } else {
587
+ if (!this._packet)
588
+ this._packet = Buffer.allocUnsafe(this._len);
589
+ this._packet.set(chunk, this._packetPos);
590
+ }
591
+ p += nb;
592
+ this._packetPos += nb;
593
+ if (this._packetPos < this._len)
594
+ return;
595
+ }
596
+
597
+ const payload = (!this._packet
598
+ ? EMPTY_BUFFER
599
+ : new FastBuffer(this._packet.buffer,
600
+ this._packet.byteOffset + 1,
601
+ this._packet.length
602
+ - this._packet[0] - 1));
603
+
604
+ // Prepare for next packet
605
+ this.inSeqno = (this.inSeqno + 1) >>> 0;
606
+ this._len = 0;
607
+ this._lenBytes = 0;
608
+ this._packet = null;
609
+ this._packetPos = 0;
610
+
611
+ {
612
+ const ret = this._onPayload(payload);
613
+ if (ret !== undefined)
614
+ return (ret === false ? p : ret);
615
+ }
616
+ }
617
+ }
618
+ }
619
+
620
+ class ChaChaPolyDecipherNative {
621
+ constructor(config) {
622
+ const dec = config.inbound;
623
+ this.inSeqno = dec.seqno;
624
+ this._onPayload = dec.onPayload;
625
+ this._decKeyMain = dec.decipherKey.slice(0, 32);
626
+ this._decKeyPktLen = dec.decipherKey.slice(32);
627
+ this._len = 0;
628
+ this._lenBuf = Buffer.alloc(4);
629
+ this._lenPos = 0;
630
+ this._packet = null;
631
+ this._pktLen = 0;
632
+ this._mac = Buffer.allocUnsafe(16);
633
+ this._calcMac = Buffer.allocUnsafe(16);
634
+ this._macPos = 0;
635
+ }
636
+ free() {}
637
+ decrypt(data, p, dataLen) {
638
+ // `data` === encrypted data
639
+
640
+ while (p < dataLen) {
641
+ // Read packet length
642
+ if (this._lenPos < 4) {
643
+ let nb = Math.min(4 - this._lenPos, dataLen - p);
644
+ while (nb--)
645
+ this._lenBuf[this._lenPos++] = data[p++];
646
+ if (this._lenPos < 4)
647
+ return;
648
+
649
+ POLY1305_OUT_COMPUTE[0] = 0; // Set counter to 0 (little endian)
650
+ writeUInt32BE(POLY1305_OUT_COMPUTE, this.inSeqno, 12);
651
+
652
+ const decLenBytes =
653
+ createDecipheriv('chacha20', this._decKeyPktLen, POLY1305_OUT_COMPUTE)
654
+ .update(this._lenBuf);
655
+ this._len = readUInt32BE(decLenBytes, 0);
656
+
657
+ if (this._len > MAX_PACKET_SIZE
658
+ || this._len < 8
659
+ || (this._len & 7) !== 0) {
660
+ throw new Error('Bad packet length');
661
+ }
662
+ }
663
+
664
+ // Read padding length, payload, and padding
665
+ if (this._pktLen < this._len) {
666
+ if (p >= dataLen)
667
+ return;
668
+ const nb = Math.min(this._len - this._pktLen, dataLen - p);
669
+ let encrypted;
670
+ if (p !== 0 || nb !== dataLen)
671
+ encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
672
+ else
673
+ encrypted = data;
674
+ if (nb === this._len) {
675
+ this._packet = encrypted;
676
+ } else {
677
+ if (!this._packet)
678
+ this._packet = Buffer.allocUnsafe(this._len);
679
+ this._packet.set(encrypted, this._pktLen);
680
+ }
681
+ p += nb;
682
+ this._pktLen += nb;
683
+ if (this._pktLen < this._len || p >= dataLen)
684
+ return;
685
+ }
686
+
687
+ // Read Poly1305 MAC
688
+ {
689
+ const nb = Math.min(16 - this._macPos, dataLen - p);
690
+ // TODO: avoid copying if entire MAC is in current chunk
691
+ if (p !== 0 || nb !== dataLen) {
692
+ this._mac.set(
693
+ new Uint8Array(data.buffer, data.byteOffset + p, nb),
694
+ this._macPos
695
+ );
696
+ } else {
697
+ this._mac.set(data, this._macPos);
698
+ }
699
+ p += nb;
700
+ this._macPos += nb;
701
+ if (this._macPos < 16)
702
+ return;
703
+ }
704
+
705
+ // Generate Poly1305 key
706
+ POLY1305_OUT_COMPUTE[0] = 0; // Set counter to 0 (little endian)
707
+ writeUInt32BE(POLY1305_OUT_COMPUTE, this.inSeqno, 12);
708
+ const polyKey =
709
+ createCipheriv('chacha20', this._decKeyMain, POLY1305_OUT_COMPUTE)
710
+ .update(POLY1305_ZEROS);
711
+
712
+ // Calculate and compare Poly1305 MACs
713
+ poly1305_auth(POLY1305_RESULT_MALLOC,
714
+ this._lenBuf,
715
+ 4,
716
+ this._packet,
717
+ this._packet.length,
718
+ polyKey);
719
+
720
+ this._calcMac.set(
721
+ new Uint8Array(POLY1305_WASM_MODULE.HEAPU8.buffer,
722
+ POLY1305_RESULT_MALLOC,
723
+ 16),
724
+ 0
725
+ );
726
+ if (!timingSafeEqual(this._calcMac, this._mac))
727
+ throw new Error('Invalid MAC');
728
+
729
+ // Decrypt packet
730
+ POLY1305_OUT_COMPUTE[0] = 1; // Set counter to 1 (little endian)
731
+ const packet =
732
+ createDecipheriv('chacha20', this._decKeyMain, POLY1305_OUT_COMPUTE)
733
+ .update(this._packet);
734
+
735
+ const payload = new FastBuffer(packet.buffer,
736
+ packet.byteOffset + 1,
737
+ packet.length - packet[0] - 1);
738
+
739
+ // Prepare for next packet
740
+ this.inSeqno = (this.inSeqno + 1) >>> 0;
741
+ this._len = 0;
742
+ this._lenPos = 0;
743
+ this._packet = null;
744
+ this._pktLen = 0;
745
+ this._macPos = 0;
746
+
747
+ {
748
+ const ret = this._onPayload(payload);
749
+ if (ret !== undefined)
750
+ return (ret === false ? p : ret);
751
+ }
752
+ }
753
+ }
754
+ }
755
+
756
+ class ChaChaPolyDecipherBinding {
757
+ constructor(config) {
758
+ const dec = config.inbound;
759
+ this.inSeqno = dec.seqno;
760
+ this._onPayload = dec.onPayload;
761
+ this._instance = new ChaChaPolyDecipher(dec.decipherKey);
762
+ this._len = 0;
763
+ this._lenBuf = Buffer.alloc(4);
764
+ this._lenPos = 0;
765
+ this._packet = null;
766
+ this._pktLen = 0;
767
+ this._mac = Buffer.allocUnsafe(16);
768
+ this._macPos = 0;
769
+ }
770
+ free() {
771
+ this._instance.free();
772
+ }
773
+ decrypt(data, p, dataLen) {
774
+ // `data` === encrypted data
775
+
776
+ while (p < dataLen) {
777
+ // Read packet length
778
+ if (this._lenPos < 4) {
779
+ let nb = Math.min(4 - this._lenPos, dataLen - p);
780
+ while (nb--)
781
+ this._lenBuf[this._lenPos++] = data[p++];
782
+ if (this._lenPos < 4)
783
+ return;
784
+
785
+ this._len = this._instance.decryptLen(this._lenBuf, this.inSeqno);
786
+
787
+ if (this._len > MAX_PACKET_SIZE
788
+ || this._len < 8
789
+ || (this._len & 7) !== 0) {
790
+ throw new Error('Bad packet length');
791
+ }
792
+
793
+ if (p >= dataLen)
794
+ return;
795
+ }
796
+
797
+ // Read padding length, payload, and padding
798
+ if (this._pktLen < this._len) {
799
+ const nb = Math.min(this._len - this._pktLen, dataLen - p);
800
+ let encrypted;
801
+ if (p !== 0 || nb !== dataLen)
802
+ encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
803
+ else
804
+ encrypted = data;
805
+ if (nb === this._len) {
806
+ this._packet = encrypted;
807
+ } else {
808
+ if (!this._packet)
809
+ this._packet = Buffer.allocUnsafe(this._len);
810
+ this._packet.set(encrypted, this._pktLen);
811
+ }
812
+ p += nb;
813
+ this._pktLen += nb;
814
+ if (this._pktLen < this._len || p >= dataLen)
815
+ return;
816
+ }
817
+
818
+ // Read Poly1305 MAC
819
+ {
820
+ const nb = Math.min(16 - this._macPos, dataLen - p);
821
+ // TODO: avoid copying if entire MAC is in current chunk
822
+ if (p !== 0 || nb !== dataLen) {
823
+ this._mac.set(
824
+ new Uint8Array(data.buffer, data.byteOffset + p, nb),
825
+ this._macPos
826
+ );
827
+ } else {
828
+ this._mac.set(data, this._macPos);
829
+ }
830
+ p += nb;
831
+ this._macPos += nb;
832
+ if (this._macPos < 16)
833
+ return;
834
+ }
835
+
836
+ this._instance.decrypt(this._packet, this._mac, this.inSeqno);
837
+
838
+ const payload = new FastBuffer(this._packet.buffer,
839
+ this._packet.byteOffset + 1,
840
+ this._packet.length - this._packet[0] - 1);
841
+
842
+ // Prepare for next packet
843
+ this.inSeqno = (this.inSeqno + 1) >>> 0;
844
+ this._len = 0;
845
+ this._lenPos = 0;
846
+ this._packet = null;
847
+ this._pktLen = 0;
848
+ this._macPos = 0;
849
+
850
+ {
851
+ const ret = this._onPayload(payload);
852
+ if (ret !== undefined)
853
+ return (ret === false ? p : ret);
854
+ }
855
+ }
856
+ }
857
+ }
858
+
859
+ class AESGCMDecipherNative {
860
+ constructor(config) {
861
+ const dec = config.inbound;
862
+ this.inSeqno = dec.seqno;
863
+ this._onPayload = dec.onPayload;
864
+ this._decipherInstance = null;
865
+ this._decipherSSLName = dec.decipherInfo.sslName;
866
+ this._decipherKey = dec.decipherKey;
867
+ this._decipherIV = dec.decipherIV;
868
+ this._len = 0;
869
+ this._lenBytes = 0;
870
+ this._packet = null;
871
+ this._packetPos = 0;
872
+ this._pktLen = 0;
873
+ this._tag = Buffer.allocUnsafe(16);
874
+ this._tagPos = 0;
875
+ }
876
+ free() {}
877
+ decrypt(data, p, dataLen) {
878
+ // `data` === encrypted data
879
+
880
+ while (p < dataLen) {
881
+ // Read packet length (unencrypted, but AAD)
882
+ if (this._lenBytes < 4) {
883
+ let nb = Math.min(4 - this._lenBytes, dataLen - p);
884
+ this._lenBytes += nb;
885
+ while (nb--)
886
+ this._len = (this._len << 8) + data[p++];
887
+ if (this._lenBytes < 4)
888
+ return;
889
+
890
+ if ((this._len + 20) > MAX_PACKET_SIZE
891
+ || this._len < 16
892
+ || (this._len & 15) !== 0) {
893
+ throw new Error('Bad packet length');
894
+ }
895
+
896
+ this._decipherInstance = createDecipheriv(
897
+ this._decipherSSLName,
898
+ this._decipherKey,
899
+ this._decipherIV
900
+ );
901
+ this._decipherInstance.setAutoPadding(false);
902
+ this._decipherInstance.setAAD(intToBytes(this._len));
903
+ }
904
+
905
+ // Read padding length, payload, and padding
906
+ if (this._pktLen < this._len) {
907
+ if (p >= dataLen)
908
+ return;
909
+ const nb = Math.min(this._len - this._pktLen, dataLen - p);
910
+ let decrypted;
911
+ if (p !== 0 || nb !== dataLen) {
912
+ decrypted = this._decipherInstance.update(
913
+ new Uint8Array(data.buffer, data.byteOffset + p, nb)
914
+ );
915
+ } else {
916
+ decrypted = this._decipherInstance.update(data);
917
+ }
918
+ if (decrypted.length) {
919
+ if (nb === this._len) {
920
+ this._packet = decrypted;
921
+ } else {
922
+ if (!this._packet)
923
+ this._packet = Buffer.allocUnsafe(this._len);
924
+ this._packet.set(decrypted, this._packetPos);
925
+ }
926
+ this._packetPos += decrypted.length;
927
+ }
928
+ p += nb;
929
+ this._pktLen += nb;
930
+ if (this._pktLen < this._len || p >= dataLen)
931
+ return;
932
+ }
933
+
934
+ // Read authentication tag
935
+ {
936
+ const nb = Math.min(16 - this._tagPos, dataLen - p);
937
+ if (p !== 0 || nb !== dataLen) {
938
+ this._tag.set(
939
+ new Uint8Array(data.buffer, data.byteOffset + p, nb),
940
+ this._tagPos
941
+ );
942
+ } else {
943
+ this._tag.set(data, this._tagPos);
944
+ }
945
+ p += nb;
946
+ this._tagPos += nb;
947
+ if (this._tagPos < 16)
948
+ return;
949
+ }
950
+
951
+ {
952
+ // Verify authentication tag
953
+ this._decipherInstance.setAuthTag(this._tag);
954
+
955
+ const decrypted = this._decipherInstance.final();
956
+
957
+ // XXX: this should never output any data since stream ciphers always
958
+ // return data from .update() and block ciphers must end on a multiple
959
+ // of the block length, which would have caused an exception to be
960
+ // thrown if the total input was not...
961
+ if (decrypted.length) {
962
+ if (this._packet)
963
+ this._packet.set(decrypted, this._packetPos);
964
+ else
965
+ this._packet = decrypted;
966
+ }
967
+ }
968
+
969
+ const payload = (!this._packet
970
+ ? EMPTY_BUFFER
971
+ : new FastBuffer(this._packet.buffer,
972
+ this._packet.byteOffset + 1,
973
+ this._packet.length
974
+ - this._packet[0] - 1));
975
+
976
+ // Prepare for next packet
977
+ this.inSeqno = (this.inSeqno + 1) >>> 0;
978
+ ivIncrement(this._decipherIV);
979
+ this._len = 0;
980
+ this._lenBytes = 0;
981
+ this._packet = null;
982
+ this._packetPos = 0;
983
+ this._pktLen = 0;
984
+ this._tagPos = 0;
985
+
986
+ {
987
+ const ret = this._onPayload(payload);
988
+ if (ret !== undefined)
989
+ return (ret === false ? p : ret);
990
+ }
991
+ }
992
+ }
993
+ }
994
+
995
+ class AESGCMDecipherBinding {
996
+ constructor(config) {
997
+ const dec = config.inbound;
998
+ this.inSeqno = dec.seqno;
999
+ this._onPayload = dec.onPayload;
1000
+ this._instance = new AESGCMDecipher(dec.decipherInfo.sslName,
1001
+ dec.decipherKey,
1002
+ dec.decipherIV);
1003
+ this._len = 0;
1004
+ this._lenBytes = 0;
1005
+ this._packet = null;
1006
+ this._pktLen = 0;
1007
+ this._tag = Buffer.allocUnsafe(16);
1008
+ this._tagPos = 0;
1009
+ }
1010
+ free() {}
1011
+ decrypt(data, p, dataLen) {
1012
+ // `data` === encrypted data
1013
+
1014
+ while (p < dataLen) {
1015
+ // Read packet length (unencrypted, but AAD)
1016
+ if (this._lenBytes < 4) {
1017
+ let nb = Math.min(4 - this._lenBytes, dataLen - p);
1018
+ this._lenBytes += nb;
1019
+ while (nb--)
1020
+ this._len = (this._len << 8) + data[p++];
1021
+ if (this._lenBytes < 4)
1022
+ return;
1023
+
1024
+ if ((this._len + 20) > MAX_PACKET_SIZE
1025
+ || this._len < 16
1026
+ || (this._len & 15) !== 0) {
1027
+ throw new Error(`Bad packet length: ${this._len}`);
1028
+ }
1029
+ }
1030
+
1031
+ // Read padding length, payload, and padding
1032
+ if (this._pktLen < this._len) {
1033
+ if (p >= dataLen)
1034
+ return;
1035
+ const nb = Math.min(this._len - this._pktLen, dataLen - p);
1036
+ let encrypted;
1037
+ if (p !== 0 || nb !== dataLen)
1038
+ encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
1039
+ else
1040
+ encrypted = data;
1041
+ if (nb === this._len) {
1042
+ this._packet = encrypted;
1043
+ } else {
1044
+ if (!this._packet)
1045
+ this._packet = Buffer.allocUnsafe(this._len);
1046
+ this._packet.set(encrypted, this._pktLen);
1047
+ }
1048
+ p += nb;
1049
+ this._pktLen += nb;
1050
+ if (this._pktLen < this._len || p >= dataLen)
1051
+ return;
1052
+ }
1053
+
1054
+ // Read authentication tag
1055
+ {
1056
+ const nb = Math.min(16 - this._tagPos, dataLen - p);
1057
+ if (p !== 0 || nb !== dataLen) {
1058
+ this._tag.set(
1059
+ new Uint8Array(data.buffer, data.byteOffset + p, nb),
1060
+ this._tagPos
1061
+ );
1062
+ } else {
1063
+ this._tag.set(data, this._tagPos);
1064
+ }
1065
+ p += nb;
1066
+ this._tagPos += nb;
1067
+ if (this._tagPos < 16)
1068
+ return;
1069
+ }
1070
+
1071
+ this._instance.decrypt(this._packet, this._len, this._tag);
1072
+
1073
+ const payload = new FastBuffer(this._packet.buffer,
1074
+ this._packet.byteOffset + 1,
1075
+ this._packet.length - this._packet[0] - 1);
1076
+
1077
+ // Prepare for next packet
1078
+ this.inSeqno = (this.inSeqno + 1) >>> 0;
1079
+ this._len = 0;
1080
+ this._lenBytes = 0;
1081
+ this._packet = null;
1082
+ this._pktLen = 0;
1083
+ this._tagPos = 0;
1084
+
1085
+ {
1086
+ const ret = this._onPayload(payload);
1087
+ if (ret !== undefined)
1088
+ return (ret === false ? p : ret);
1089
+ }
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ // TODO: test incremental .update()s vs. copying to _packet and doing a single
1095
+ // .update() after entire packet read -- a single .update() would allow
1096
+ // verifying MAC before decrypting for ETM MACs
1097
+ class GenericDecipherNative {
1098
+ constructor(config) {
1099
+ const dec = config.inbound;
1100
+ this.inSeqno = dec.seqno;
1101
+ this._onPayload = dec.onPayload;
1102
+ this._decipherInstance = createDecipheriv(dec.decipherInfo.sslName,
1103
+ dec.decipherKey,
1104
+ dec.decipherIV);
1105
+ this._decipherInstance.setAutoPadding(false);
1106
+ this._block = Buffer.allocUnsafe(
1107
+ dec.macInfo.isETM ? 4 : dec.decipherInfo.blockLen
1108
+ );
1109
+ this._blockSize = dec.decipherInfo.blockLen;
1110
+ this._blockPos = 0;
1111
+ this._len = 0;
1112
+ this._packet = null;
1113
+ this._packetPos = 0;
1114
+ this._pktLen = 0;
1115
+ this._mac = Buffer.allocUnsafe(dec.macInfo.actualLen);
1116
+ this._macPos = 0;
1117
+ this._macSSLName = dec.macInfo.sslName;
1118
+ this._macKey = dec.macKey;
1119
+ this._macActualLen = dec.macInfo.actualLen;
1120
+ this._macETM = dec.macInfo.isETM;
1121
+ this._macInstance = null;
1122
+
1123
+ const discardLen = dec.decipherInfo.discardLen;
1124
+ if (discardLen) {
1125
+ let discard = DISCARD_CACHE.get(discardLen);
1126
+ if (discard === undefined) {
1127
+ discard = Buffer.alloc(discardLen);
1128
+ DISCARD_CACHE.set(discardLen, discard);
1129
+ }
1130
+ this._decipherInstance.update(discard);
1131
+ }
1132
+ }
1133
+ free() {}
1134
+ decrypt(data, p, dataLen) {
1135
+ // `data` === encrypted data
1136
+
1137
+ while (p < dataLen) {
1138
+ // Read first encrypted block
1139
+ if (this._blockPos < this._block.length) {
1140
+ const nb = Math.min(this._block.length - this._blockPos, dataLen - p);
1141
+ if (p !== 0 || nb !== dataLen || nb < data.length) {
1142
+ this._block.set(
1143
+ new Uint8Array(data.buffer, data.byteOffset + p, nb),
1144
+ this._blockPos
1145
+ );
1146
+ } else {
1147
+ this._block.set(data, this._blockPos);
1148
+ }
1149
+
1150
+ p += nb;
1151
+ this._blockPos += nb;
1152
+ if (this._blockPos < this._block.length)
1153
+ return;
1154
+
1155
+ let decrypted;
1156
+ let need;
1157
+ if (this._macETM) {
1158
+ this._len = need = readUInt32BE(this._block, 0);
1159
+ } else {
1160
+ // Decrypt first block to get packet length
1161
+ decrypted = this._decipherInstance.update(this._block);
1162
+ this._len = readUInt32BE(decrypted, 0);
1163
+ need = 4 + this._len - this._blockSize;
1164
+ }
1165
+
1166
+ if (this._len > MAX_PACKET_SIZE
1167
+ || this._len < 5
1168
+ || (need & (this._blockSize - 1)) !== 0) {
1169
+ throw new Error('Bad packet length');
1170
+ }
1171
+
1172
+ // Create MAC up front to calculate in parallel with decryption
1173
+ this._macInstance = createHmac(this._macSSLName, this._macKey);
1174
+
1175
+ writeUInt32BE(BUF_INT, this.inSeqno, 0);
1176
+ this._macInstance.update(BUF_INT);
1177
+ if (this._macETM) {
1178
+ this._macInstance.update(this._block);
1179
+ } else {
1180
+ this._macInstance.update(new Uint8Array(decrypted.buffer,
1181
+ decrypted.byteOffset,
1182
+ 4));
1183
+ this._pktLen = decrypted.length - 4;
1184
+ this._packetPos = this._pktLen;
1185
+ this._packet = Buffer.allocUnsafe(this._len);
1186
+ this._packet.set(
1187
+ new Uint8Array(decrypted.buffer,
1188
+ decrypted.byteOffset + 4,
1189
+ this._packetPos),
1190
+ 0
1191
+ );
1192
+ }
1193
+
1194
+ if (p >= dataLen)
1195
+ return;
1196
+ }
1197
+
1198
+ // Read padding length, payload, and padding
1199
+ if (this._pktLen < this._len) {
1200
+ const nb = Math.min(this._len - this._pktLen, dataLen - p);
1201
+ let encrypted;
1202
+ if (p !== 0 || nb !== dataLen)
1203
+ encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
1204
+ else
1205
+ encrypted = data;
1206
+ if (this._macETM)
1207
+ this._macInstance.update(encrypted);
1208
+ const decrypted = this._decipherInstance.update(encrypted);
1209
+ if (decrypted.length) {
1210
+ if (nb === this._len) {
1211
+ this._packet = decrypted;
1212
+ } else {
1213
+ if (!this._packet)
1214
+ this._packet = Buffer.allocUnsafe(this._len);
1215
+ this._packet.set(decrypted, this._packetPos);
1216
+ }
1217
+ this._packetPos += decrypted.length;
1218
+ }
1219
+ p += nb;
1220
+ this._pktLen += nb;
1221
+ if (this._pktLen < this._len || p >= dataLen)
1222
+ return;
1223
+ }
1224
+
1225
+ // Read MAC
1226
+ {
1227
+ const nb = Math.min(this._macActualLen - this._macPos, dataLen - p);
1228
+ if (p !== 0 || nb !== dataLen) {
1229
+ this._mac.set(
1230
+ new Uint8Array(data.buffer, data.byteOffset + p, nb),
1231
+ this._macPos
1232
+ );
1233
+ } else {
1234
+ this._mac.set(data, this._macPos);
1235
+ }
1236
+ p += nb;
1237
+ this._macPos += nb;
1238
+ if (this._macPos < this._macActualLen)
1239
+ return;
1240
+ }
1241
+
1242
+ // Verify MAC
1243
+ if (!this._macETM)
1244
+ this._macInstance.update(this._packet);
1245
+ let calculated = this._macInstance.digest();
1246
+ if (this._macActualLen < calculated.length) {
1247
+ calculated = new Uint8Array(calculated.buffer,
1248
+ calculated.byteOffset,
1249
+ this._macActualLen);
1250
+ }
1251
+ if (!timingSafeEquals(calculated, this._mac))
1252
+ throw new Error('Invalid MAC');
1253
+
1254
+ const payload = new FastBuffer(this._packet.buffer,
1255
+ this._packet.byteOffset + 1,
1256
+ this._packet.length - this._packet[0] - 1);
1257
+
1258
+ // Prepare for next packet
1259
+ this.inSeqno = (this.inSeqno + 1) >>> 0;
1260
+ this._blockPos = 0;
1261
+ this._len = 0;
1262
+ this._packet = null;
1263
+ this._packetPos = 0;
1264
+ this._pktLen = 0;
1265
+ this._macPos = 0;
1266
+ this._macInstance = null;
1267
+
1268
+ {
1269
+ const ret = this._onPayload(payload);
1270
+ if (ret !== undefined)
1271
+ return (ret === false ? p : ret);
1272
+ }
1273
+ }
1274
+ }
1275
+ }
1276
+
1277
+ class GenericDecipherBinding {
1278
+ constructor(config) {
1279
+ const dec = config.inbound;
1280
+ this.inSeqno = dec.seqno;
1281
+ this._onPayload = dec.onPayload;
1282
+ this._instance = new GenericDecipher(dec.decipherInfo.sslName,
1283
+ dec.decipherKey,
1284
+ dec.decipherIV,
1285
+ dec.macInfo.sslName,
1286
+ dec.macKey,
1287
+ dec.macInfo.isETM,
1288
+ dec.macInfo.actualLen);
1289
+ this._block = Buffer.allocUnsafe(
1290
+ dec.macInfo.isETM || dec.decipherInfo.stream
1291
+ ? 4
1292
+ : dec.decipherInfo.blockLen
1293
+ );
1294
+ this._blockPos = 0;
1295
+ this._len = 0;
1296
+ this._packet = null;
1297
+ this._pktLen = 0;
1298
+ this._mac = Buffer.allocUnsafe(dec.macInfo.actualLen);
1299
+ this._macPos = 0;
1300
+ this._macActualLen = dec.macInfo.actualLen;
1301
+ this._macETM = dec.macInfo.isETM;
1302
+ }
1303
+ free() {
1304
+ this._instance.free();
1305
+ }
1306
+ decrypt(data, p, dataLen) {
1307
+ // `data` === encrypted data
1308
+
1309
+ while (p < dataLen) {
1310
+ // Read first encrypted block
1311
+ if (this._blockPos < this._block.length) {
1312
+ const nb = Math.min(this._block.length - this._blockPos, dataLen - p);
1313
+ if (p !== 0 || nb !== dataLen || nb < data.length) {
1314
+ this._block.set(
1315
+ new Uint8Array(data.buffer, data.byteOffset + p, nb),
1316
+ this._blockPos
1317
+ );
1318
+ } else {
1319
+ this._block.set(data, this._blockPos);
1320
+ }
1321
+
1322
+ p += nb;
1323
+ this._blockPos += nb;
1324
+ if (this._blockPos < this._block.length)
1325
+ return;
1326
+
1327
+ let need;
1328
+ if (this._macETM) {
1329
+ this._len = need = readUInt32BE(this._block, 0);
1330
+ } else {
1331
+ // Decrypt first block to get packet length
1332
+ this._instance.decryptBlock(this._block);
1333
+ this._len = readUInt32BE(this._block, 0);
1334
+ need = 4 + this._len - this._block.length;
1335
+ }
1336
+
1337
+ if (this._len > MAX_PACKET_SIZE
1338
+ || this._len < 5
1339
+ || (need & (this._block.length - 1)) !== 0) {
1340
+ throw new Error('Bad packet length');
1341
+ }
1342
+
1343
+ if (!this._macETM) {
1344
+ this._pktLen = (this._block.length - 4);
1345
+ if (this._pktLen) {
1346
+ this._packet = Buffer.allocUnsafe(this._len);
1347
+ this._packet.set(
1348
+ new Uint8Array(this._block.buffer,
1349
+ this._block.byteOffset + 4,
1350
+ this._pktLen),
1351
+ 0
1352
+ );
1353
+ }
1354
+ }
1355
+
1356
+ if (p >= dataLen)
1357
+ return;
1358
+ }
1359
+
1360
+ // Read padding length, payload, and padding
1361
+ if (this._pktLen < this._len) {
1362
+ const nb = Math.min(this._len - this._pktLen, dataLen - p);
1363
+ let encrypted;
1364
+ if (p !== 0 || nb !== dataLen)
1365
+ encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
1366
+ else
1367
+ encrypted = data;
1368
+ if (nb === this._len) {
1369
+ this._packet = encrypted;
1370
+ } else {
1371
+ if (!this._packet)
1372
+ this._packet = Buffer.allocUnsafe(this._len);
1373
+ this._packet.set(encrypted, this._pktLen);
1374
+ }
1375
+ p += nb;
1376
+ this._pktLen += nb;
1377
+ if (this._pktLen < this._len || p >= dataLen)
1378
+ return;
1379
+ }
1380
+
1381
+ // Read MAC
1382
+ {
1383
+ const nb = Math.min(this._macActualLen - this._macPos, dataLen - p);
1384
+ if (p !== 0 || nb !== dataLen) {
1385
+ this._mac.set(
1386
+ new Uint8Array(data.buffer, data.byteOffset + p, nb),
1387
+ this._macPos
1388
+ );
1389
+ } else {
1390
+ this._mac.set(data, this._macPos);
1391
+ }
1392
+ p += nb;
1393
+ this._macPos += nb;
1394
+ if (this._macPos < this._macActualLen)
1395
+ return;
1396
+ }
1397
+
1398
+ // Decrypt and verify MAC
1399
+ this._instance.decrypt(this._packet,
1400
+ this.inSeqno,
1401
+ this._block,
1402
+ this._mac);
1403
+
1404
+ const payload = new FastBuffer(this._packet.buffer,
1405
+ this._packet.byteOffset + 1,
1406
+ this._packet.length - this._packet[0] - 1);
1407
+
1408
+ // Prepare for next packet
1409
+ this.inSeqno = (this.inSeqno + 1) >>> 0;
1410
+ this._blockPos = 0;
1411
+ this._len = 0;
1412
+ this._packet = null;
1413
+ this._pktLen = 0;
1414
+ this._macPos = 0;
1415
+ this._macInstance = null;
1416
+
1417
+ {
1418
+ const ret = this._onPayload(payload);
1419
+ if (ret !== undefined)
1420
+ return (ret === false ? p : ret);
1421
+ }
1422
+ }
1423
+ }
1424
+ }
1425
+
1426
+ // Increments unsigned, big endian counter (last 8 bytes) of AES-GCM IV
1427
+ function ivIncrement(iv) {
1428
+ // eslint-disable-next-line no-unused-expressions
1429
+ ++iv[11] >>> 8
1430
+ && ++iv[10] >>> 8
1431
+ && ++iv[9] >>> 8
1432
+ && ++iv[8] >>> 8
1433
+ && ++iv[7] >>> 8
1434
+ && ++iv[6] >>> 8
1435
+ && ++iv[5] >>> 8
1436
+ && ++iv[4] >>> 8;
1437
+ }
1438
+
1439
+ const intToBytes = (() => {
1440
+ const ret = Buffer.alloc(4);
1441
+ return (n) => {
1442
+ ret[0] = (n >>> 24);
1443
+ ret[1] = (n >>> 16);
1444
+ ret[2] = (n >>> 8);
1445
+ ret[3] = n;
1446
+ return ret;
1447
+ };
1448
+ })();
1449
+
1450
+ function timingSafeEquals(a, b) {
1451
+ if (a.length !== b.length) {
1452
+ timingSafeEqual(a, a);
1453
+ return false;
1454
+ }
1455
+ return timingSafeEqual(a, b);
1456
+ }
1457
+
1458
+ function createCipher(config) {
1459
+ if (typeof config !== 'object' || config === null)
1460
+ throw new Error('Invalid config');
1461
+
1462
+ if (typeof config.outbound !== 'object' || config.outbound === null)
1463
+ throw new Error('Invalid outbound');
1464
+
1465
+ const outbound = config.outbound;
1466
+
1467
+ if (typeof outbound.onWrite !== 'function')
1468
+ throw new Error('Invalid outbound.onWrite');
1469
+
1470
+ if (typeof outbound.cipherInfo !== 'object' || outbound.cipherInfo === null)
1471
+ throw new Error('Invalid outbound.cipherInfo');
1472
+
1473
+ if (!Buffer.isBuffer(outbound.cipherKey)
1474
+ || outbound.cipherKey.length !== outbound.cipherInfo.keyLen) {
1475
+ throw new Error('Invalid outbound.cipherKey');
1476
+ }
1477
+
1478
+ if (outbound.cipherInfo.ivLen
1479
+ && (!Buffer.isBuffer(outbound.cipherIV)
1480
+ || outbound.cipherIV.length !== outbound.cipherInfo.ivLen)) {
1481
+ throw new Error('Invalid outbound.cipherIV');
1482
+ }
1483
+
1484
+ if (typeof outbound.seqno !== 'number'
1485
+ || outbound.seqno < 0
1486
+ || outbound.seqno > MAX_SEQNO) {
1487
+ throw new Error('Invalid outbound.seqno');
1488
+ }
1489
+
1490
+ const forceNative = !!outbound.forceNative;
1491
+
1492
+ switch (outbound.cipherInfo.sslName) {
1493
+ case 'aes-128-gcm':
1494
+ case 'aes-256-gcm':
1495
+ return (AESGCMCipher && !forceNative
1496
+ ? new AESGCMCipherBinding(config)
1497
+ : new AESGCMCipherNative(config));
1498
+ case 'chacha20':
1499
+ return (ChaChaPolyCipher && !forceNative
1500
+ ? new ChaChaPolyCipherBinding(config)
1501
+ : new ChaChaPolyCipherNative(config));
1502
+ default: {
1503
+ if (typeof outbound.macInfo !== 'object' || outbound.macInfo === null)
1504
+ throw new Error('Invalid outbound.macInfo');
1505
+ if (!Buffer.isBuffer(outbound.macKey)
1506
+ || outbound.macKey.length !== outbound.macInfo.len) {
1507
+ throw new Error('Invalid outbound.macKey');
1508
+ }
1509
+ return (GenericCipher && !forceNative
1510
+ ? new GenericCipherBinding(config)
1511
+ : new GenericCipherNative(config));
1512
+ }
1513
+ }
1514
+ }
1515
+
1516
+ function createDecipher(config) {
1517
+ if (typeof config !== 'object' || config === null)
1518
+ throw new Error('Invalid config');
1519
+
1520
+ if (typeof config.inbound !== 'object' || config.inbound === null)
1521
+ throw new Error('Invalid inbound');
1522
+
1523
+ const inbound = config.inbound;
1524
+
1525
+ if (typeof inbound.onPayload !== 'function')
1526
+ throw new Error('Invalid inbound.onPayload');
1527
+
1528
+ if (typeof inbound.decipherInfo !== 'object'
1529
+ || inbound.decipherInfo === null) {
1530
+ throw new Error('Invalid inbound.decipherInfo');
1531
+ }
1532
+
1533
+ if (!Buffer.isBuffer(inbound.decipherKey)
1534
+ || inbound.decipherKey.length !== inbound.decipherInfo.keyLen) {
1535
+ throw new Error('Invalid inbound.decipherKey');
1536
+ }
1537
+
1538
+ if (inbound.decipherInfo.ivLen
1539
+ && (!Buffer.isBuffer(inbound.decipherIV)
1540
+ || inbound.decipherIV.length !== inbound.decipherInfo.ivLen)) {
1541
+ throw new Error('Invalid inbound.decipherIV');
1542
+ }
1543
+
1544
+ if (typeof inbound.seqno !== 'number'
1545
+ || inbound.seqno < 0
1546
+ || inbound.seqno > MAX_SEQNO) {
1547
+ throw new Error('Invalid inbound.seqno');
1548
+ }
1549
+
1550
+ const forceNative = !!inbound.forceNative;
1551
+
1552
+ switch (inbound.decipherInfo.sslName) {
1553
+ case 'aes-128-gcm':
1554
+ case 'aes-256-gcm':
1555
+ return (AESGCMDecipher && !forceNative
1556
+ ? new AESGCMDecipherBinding(config)
1557
+ : new AESGCMDecipherNative(config));
1558
+ case 'chacha20':
1559
+ return (ChaChaPolyDecipher && !forceNative
1560
+ ? new ChaChaPolyDecipherBinding(config)
1561
+ : new ChaChaPolyDecipherNative(config));
1562
+ default: {
1563
+ if (typeof inbound.macInfo !== 'object' || inbound.macInfo === null)
1564
+ throw new Error('Invalid inbound.macInfo');
1565
+ if (!Buffer.isBuffer(inbound.macKey)
1566
+ || inbound.macKey.length !== inbound.macInfo.len) {
1567
+ throw new Error('Invalid inbound.macKey');
1568
+ }
1569
+ return (GenericDecipher && !forceNative
1570
+ ? new GenericDecipherBinding(config)
1571
+ : new GenericDecipherNative(config));
1572
+ }
1573
+ }
1574
+ }
1575
+
1576
+ module.exports = {
1577
+ CIPHER_INFO,
1578
+ MAC_INFO,
1579
+ bindingAvailable: !!binding,
1580
+ init: (() => {
1581
+ // eslint-disable-next-line no-async-promise-executor
1582
+ return new Promise(async (resolve, reject) => {
1583
+ try {
1584
+ POLY1305_WASM_MODULE = await require('./crypto/poly1305.js')();
1585
+ POLY1305_RESULT_MALLOC = POLY1305_WASM_MODULE._malloc(16);
1586
+ poly1305_auth = POLY1305_WASM_MODULE.cwrap(
1587
+ 'poly1305_auth',
1588
+ null,
1589
+ ['number', 'array', 'number', 'array', 'number', 'array']
1590
+ );
1591
+ } catch (ex) {
1592
+ return reject(ex);
1593
+ }
1594
+ resolve();
1595
+ });
1596
+ })(),
1597
+
1598
+ NullCipher,
1599
+ createCipher,
1600
+ NullDecipher,
1601
+ createDecipher,
1602
+ };