@electerm/ssh2 1.5.0 → 1.9.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.
package/install.js CHANGED
@@ -2,19 +2,26 @@
2
2
 
3
3
  const { spawnSync } = require('child_process');
4
4
 
5
+ const forceFailOnNonZero = (process.env.CI_CHECK_FAIL === 'ssh2');
6
+
5
7
  // Attempt to build the bundled optional binding
6
- const result = spawnSync('node-gyp', [
8
+ const args = [
7
9
  `--target=${process.version}`,
8
- 'rebuild'
9
- ], {
10
+ `--real_openssl_major=${/^\d+/.exec(process.versions.openssl)[0]}`,
11
+ 'rebuild',
12
+ ];
13
+ const result = spawnSync('node-gyp', args, {
10
14
  cwd: 'lib/protocol/crypto',
11
15
  encoding: 'utf8',
12
16
  shell: true,
13
17
  stdio: 'inherit',
14
18
  windowsHide: true,
15
19
  });
16
- if (result.error || result.status !== 0)
20
+ if (result.error || result.status !== 0) {
17
21
  console.log('Failed to build optional crypto binding');
18
- else
22
+ if (forceFailOnNonZero)
23
+ process.exit(1);
24
+ } else {
19
25
  console.log('Succeeded in building optional crypto binding');
26
+ }
20
27
  process.exit(0);
package/lib/Channel.js CHANGED
@@ -214,6 +214,7 @@ class Channel extends DuplexStream {
214
214
  destroy() {
215
215
  this.end();
216
216
  this.close();
217
+ return this;
217
218
  }
218
219
 
219
220
  // Session type-specific methods =============================================
@@ -2593,14 +2593,13 @@ const CLIENT_HANDLERS = {
2593
2593
  */
2594
2594
  const errorCode = bufferParser.readUInt32BE();
2595
2595
  const errorMsg = bufferParser.readString(true);
2596
- const lang = bufferParser.skipString();
2597
2596
  bufferParser.clear();
2598
2597
 
2599
- if (lang === undefined) {
2600
- if (reqID !== undefined)
2601
- delete sftp._requests[reqID];
2602
- return doFatalSFTPError(sftp, 'Malformed STATUS packet');
2603
- }
2598
+ // Note: we avoid checking that the error message and language tag are in
2599
+ // the packet because there are some broken implementations that incorrectly
2600
+ // omit them. The language tag in general was never really used amongst ssh
2601
+ // implementations, so in the case of a missing error message we just
2602
+ // default to something sensible.
2604
2603
 
2605
2604
  if (sftp._debug) {
2606
2605
  const jsonMsg = JSON.stringify(errorMsg);
@@ -7,7 +7,7 @@ try {
7
7
  cpuInfo = require('cpu-features')();
8
8
  } catch {}
9
9
 
10
- const { bindingAvailable } = require('./crypto.js');
10
+ const { bindingAvailable, CIPHER_INFO, MAC_INFO } = require('./crypto.js');
11
11
 
12
12
  const eddsaSupported = (() => {
13
13
  if (typeof crypto.sign === 'function'
@@ -76,7 +76,11 @@ const SUPPORTED_SERVER_HOST_KEY = DEFAULT_SERVER_HOST_KEY.concat([
76
76
  ]);
77
77
 
78
78
 
79
- const DEFAULT_CIPHER = [
79
+ const canUseCipher = (() => {
80
+ const ciphers = crypto.getCiphers();
81
+ return (name) => ciphers.includes(CIPHER_INFO[name].sslName);
82
+ })();
83
+ let DEFAULT_CIPHER = [
80
84
  // http://tools.ietf.org/html/rfc5647
81
85
  'aes128-gcm@openssh.com',
82
86
  'aes256-gcm@openssh.com',
@@ -99,12 +103,15 @@ if (cpuInfo && cpuInfo.flags && !cpuInfo.flags.aes) {
99
103
  } else {
100
104
  DEFAULT_CIPHER.push('chacha20-poly1305@openssh.com');
101
105
  }
106
+ DEFAULT_CIPHER = DEFAULT_CIPHER.filter(canUseCipher);
102
107
  const SUPPORTED_CIPHER = DEFAULT_CIPHER.concat([
103
108
  'aes256-cbc',
104
109
  'aes192-cbc',
105
110
  'aes128-cbc',
106
111
  'blowfish-cbc',
107
112
  '3des-cbc',
113
+ 'aes128-gcm',
114
+ 'aes256-gcm',
108
115
 
109
116
  // http://tools.ietf.org/html/rfc4345#section-4:
110
117
  'arcfour256',
@@ -112,9 +119,13 @@ const SUPPORTED_CIPHER = DEFAULT_CIPHER.concat([
112
119
 
113
120
  'cast128-cbc',
114
121
  'arcfour',
115
- ]);
122
+ ].filter(canUseCipher));
116
123
 
117
124
 
125
+ const canUseMAC = (() => {
126
+ const hashes = crypto.getHashes();
127
+ return (name) => hashes.includes(MAC_INFO[name].sslName);
128
+ })();
118
129
  const DEFAULT_MAC = [
119
130
  'hmac-sha2-256-etm@openssh.com',
120
131
  'hmac-sha2-512-etm@openssh.com',
@@ -122,7 +133,7 @@ const DEFAULT_MAC = [
122
133
  'hmac-sha2-256',
123
134
  'hmac-sha2-512',
124
135
  'hmac-sha1',
125
- ];
136
+ ].filter(canUseMAC);
126
137
  const SUPPORTED_MAC = DEFAULT_MAC.concat([
127
138
  'hmac-md5',
128
139
  'hmac-sha2-256-96', // first 96 bits of HMAC-SHA256
@@ -130,7 +141,7 @@ const SUPPORTED_MAC = DEFAULT_MAC.concat([
130
141
  'hmac-ripemd160',
131
142
  'hmac-sha1-96', // first 96 bits of HMAC-SHA1
132
143
  'hmac-md5-96', // first 96 bits of HMAC-MD5
133
- ]);
144
+ ].filter(canUseMAC));
134
145
 
135
146
  const DEFAULT_COMPRESSION = [
136
147
  'none',
@@ -1,4 +1,7 @@
1
1
  {
2
+ 'variables': {
3
+ 'real_openssl_major%': '0',
4
+ },
2
5
  'targets': [
3
6
  {
4
7
  'target_name': 'sshcrypto',
@@ -9,6 +12,12 @@
9
12
  'src/binding.cc'
10
13
  ],
11
14
  'cflags': [ '-O3' ],
15
+
16
+ # Needed for OpenSSL 3.x/node.js v17.x+
17
+ 'defines': [
18
+ 'OPENSSL_API_COMPAT=0x10100000L',
19
+ 'REAL_OPENSSL_MAJOR=<(real_openssl_major)',
20
+ ],
12
21
  },
13
22
  ],
14
23
  }
@@ -1,4 +1,3 @@
1
- // TODO: switch from obsolete EVP_* APIs in CCP
2
1
  #include <stdio.h>
3
2
  #include <string.h>
4
3
  #include <assert.h>
@@ -7,10 +6,34 @@
7
6
  #include <node_buffer.h>
8
7
  #include <nan.h>
9
8
 
9
+ #if NODE_MAJOR_VERSION >= 17
10
+ # include <openssl/configuration.h>
11
+ #endif
12
+
10
13
  #include <openssl/err.h>
11
14
  #include <openssl/evp.h>
12
15
  #include <openssl/hmac.h>
13
16
 
17
+ #ifndef _WIN32
18
+ # include <dlfcn.h>
19
+ #endif
20
+
21
+ typedef int (*ctx_iv_len_func)(const EVP_CIPHER_CTX*);
22
+ typedef int (*ctx_key_len_func)(const EVP_CIPHER_CTX*);
23
+ typedef int (*ctx_get_block_size_func)(const EVP_CIPHER_CTX*);
24
+ typedef int (*cipher_flags_func)(const EVP_CIPHER*);
25
+ ctx_iv_len_func ctx_iv_len = nullptr;
26
+ ctx_key_len_func ctx_key_len = nullptr;
27
+ ctx_get_block_size_func ctx_get_block_size = nullptr;
28
+ cipher_flags_func cipher_flags = nullptr;
29
+
30
+ #if REAL_OPENSSL_MAJOR < 3
31
+ # undef EVP_DigestSignUpdate
32
+ # define EVP_DigestSignUpdate EVP_DigestUpdate
33
+ # undef EVP_PKEY_OP_SIGNCTX
34
+ # define EVP_PKEY_OP_SIGNCTX (1 << 6)
35
+ #endif
36
+
14
37
  using namespace node;
15
38
  using namespace v8;
16
39
  using namespace std;
@@ -62,8 +85,14 @@ class ChaChaPolyCipher : public ObjectWrap {
62
85
  explicit ChaChaPolyCipher()
63
86
  : ctx_main_(nullptr),
64
87
  ctx_pktlen_(nullptr),
88
+ #if REAL_OPENSSL_MAJOR >= 3
89
+ mac_(nullptr),
90
+ mac_ctx_(nullptr) {}
91
+ #else
65
92
  md_ctx_(nullptr),
66
- polykey_(nullptr) {}
93
+ polykey_(nullptr),
94
+ polykey_ctx_(nullptr) {}
95
+ #endif
67
96
 
68
97
  ~ChaChaPolyCipher() {
69
98
  clear();
@@ -71,15 +100,23 @@ class ChaChaPolyCipher : public ObjectWrap {
71
100
 
72
101
  void clear() {
73
102
  if (ctx_pktlen_) {
74
- EVP_CIPHER_CTX_cleanup(ctx_pktlen_);
75
103
  EVP_CIPHER_CTX_free(ctx_pktlen_);
76
104
  ctx_pktlen_ = nullptr;
77
105
  }
78
106
  if (ctx_main_) {
79
- EVP_CIPHER_CTX_cleanup(ctx_main_);
80
107
  EVP_CIPHER_CTX_free(ctx_main_);
81
108
  ctx_main_ = nullptr;
82
109
  }
110
+ #if REAL_OPENSSL_MAJOR >= 3
111
+ if (mac_ctx_) {
112
+ EVP_MAC_CTX_free(mac_ctx_);
113
+ mac_ctx_ = nullptr;
114
+ }
115
+ if (mac_) {
116
+ EVP_MAC_free(mac_);
117
+ mac_ = nullptr;
118
+ }
119
+ #else
83
120
  if (polykey_) {
84
121
  EVP_PKEY_free(polykey_);
85
122
  polykey_ = nullptr;
@@ -90,6 +127,7 @@ class ChaChaPolyCipher : public ObjectWrap {
90
127
  }
91
128
  // `polykey_ctx_` is not explicitly freed as it is freed implicitly when
92
129
  // `md_ctx_` is freed
130
+ #endif
93
131
  }
94
132
 
95
133
  ErrorType init(unsigned char* keys, size_t keys_len) {
@@ -108,7 +146,14 @@ class ChaChaPolyCipher : public ObjectWrap {
108
146
 
109
147
  if ((ctx_pktlen_ = EVP_CIPHER_CTX_new()) == nullptr
110
148
  || (ctx_main_ = EVP_CIPHER_CTX_new()) == nullptr
149
+ #if REAL_OPENSSL_MAJOR >= 3
150
+ || (mac_ = EVP_MAC_fetch(nullptr,
151
+ "POLY1305",
152
+ "provider=default")) == nullptr
153
+ || (mac_ctx_ = EVP_MAC_CTX_new(mac_)) == nullptr
154
+ #else
111
155
  || (md_ctx_ = EVP_MD_CTX_new()) == nullptr
156
+ #endif
112
157
  || EVP_EncryptInit_ex(ctx_pktlen_,
113
158
  cipher,
114
159
  nullptr,
@@ -122,7 +167,7 @@ class ChaChaPolyCipher : public ObjectWrap {
122
167
  r = kErrOpenSSL;
123
168
  goto out;
124
169
  }
125
- if (EVP_CIPHER_CTX_iv_length(ctx_pktlen_) != 16) {
170
+ if (ctx_iv_len(ctx_pktlen_) != 16) {
126
171
  r = kErrBadIVLen;
127
172
  goto out;
128
173
  }
@@ -206,6 +251,14 @@ out:
206
251
  }
207
252
 
208
253
  // Poly1305 over ciphertext
254
+ #if REAL_OPENSSL_MAJOR >= 3
255
+ if (EVP_MAC_init(mac_ctx_, polykey, sizeof(polykey), nullptr) != 1
256
+ || EVP_MAC_update(mac_ctx_, packet, data_len) != 1
257
+ || EVP_MAC_final(mac_ctx_, packet + data_len, &sig_len, sig_len) != 1) {
258
+ r = kErrOpenSSL;
259
+ goto out;
260
+ }
261
+ #else
209
262
  if (polykey_) {
210
263
  if (EVP_PKEY_CTX_ctrl(polykey_ctx_,
211
264
  -1,
@@ -245,9 +298,10 @@ out:
245
298
  r = kErrOpenSSL;
246
299
  goto out;
247
300
  }
301
+ #endif
248
302
 
249
- out:
250
- return r;
303
+ out:
304
+ return r;
251
305
  }
252
306
 
253
307
  static NAN_METHOD(New) {
@@ -328,9 +382,14 @@ out:
328
382
 
329
383
  EVP_CIPHER_CTX* ctx_main_;
330
384
  EVP_CIPHER_CTX* ctx_pktlen_;
385
+ #if REAL_OPENSSL_MAJOR >= 3
386
+ EVP_MAC* mac_;
387
+ EVP_MAC_CTX* mac_ctx_;
388
+ #else
331
389
  EVP_MD_CTX* md_ctx_;
332
390
  EVP_PKEY* polykey_;
333
391
  EVP_PKEY_CTX* polykey_ctx_;
392
+ #endif
334
393
  };
335
394
 
336
395
  class AESGCMCipher : public ObjectWrap {
@@ -359,7 +418,6 @@ class AESGCMCipher : public ObjectWrap {
359
418
 
360
419
  void clear() {
361
420
  if (ctx_) {
362
- EVP_CIPHER_CTX_cleanup(ctx_);
363
421
  EVP_CIPHER_CTX_free(ctx_);
364
422
  ctx_ = nullptr;
365
423
  }
@@ -394,12 +452,12 @@ class AESGCMCipher : public ObjectWrap {
394
452
  goto out;
395
453
  }
396
454
 
397
- //~ if (iv_len != static_cast<size_t>(EVP_CIPHER_CTX_iv_length(ctx_))) {
455
+ //~ if (iv_len != static_cast<size_t>(ctx_iv_len(ctx_))) {
398
456
  //~ r = kErrBadIVLen;
399
457
  //~ goto out;
400
458
  //~ }
401
459
 
402
- if (key_len != static_cast<size_t>(EVP_CIPHER_CTX_key_length(ctx_))) {
460
+ if (key_len != static_cast<size_t>(ctx_key_len(ctx_))) {
403
461
  if (!EVP_CIPHER_CTX_set_key_length(ctx_, key_len)) {
404
462
  r = kErrBadKeyLen;
405
463
  goto out;
@@ -606,7 +664,6 @@ class GenericCipher : public ObjectWrap {
606
664
 
607
665
  void clear() {
608
666
  if (ctx_) {
609
- EVP_CIPHER_CTX_cleanup(ctx_);
610
667
  EVP_CIPHER_CTX_free(ctx_);
611
668
  ctx_ = nullptr;
612
669
  }
@@ -640,12 +697,12 @@ class GenericCipher : public ObjectWrap {
640
697
  goto out;
641
698
  }
642
699
 
643
- if (iv_len != static_cast<size_t>(EVP_CIPHER_CTX_iv_length(ctx_))) {
700
+ if (iv_len != static_cast<size_t>(ctx_iv_len(ctx_))) {
644
701
  r = kErrBadIVLen;
645
702
  goto out;
646
703
  }
647
704
 
648
- if (key_len != static_cast<size_t>(EVP_CIPHER_CTX_key_length(ctx_))) {
705
+ if (key_len != static_cast<size_t>(ctx_key_len(ctx_))) {
649
706
  if (!EVP_CIPHER_CTX_set_key_length(ctx_, key_len)) {
650
707
  r = kErrBadKeyLen;
651
708
  goto out;
@@ -930,8 +987,14 @@ class ChaChaPolyDecipher : public ObjectWrap {
930
987
  explicit ChaChaPolyDecipher()
931
988
  : ctx_main_(nullptr),
932
989
  ctx_pktlen_(nullptr),
990
+ #if REAL_OPENSSL_MAJOR >= 3
991
+ mac_(nullptr),
992
+ mac_ctx_(nullptr) {}
993
+ #else
933
994
  md_ctx_(nullptr),
934
- polykey_(nullptr) {}
995
+ polykey_(nullptr),
996
+ polykey_ctx_(nullptr) {}
997
+ #endif
935
998
 
936
999
  ~ChaChaPolyDecipher() {
937
1000
  clear();
@@ -939,15 +1002,23 @@ class ChaChaPolyDecipher : public ObjectWrap {
939
1002
 
940
1003
  void clear() {
941
1004
  if (ctx_pktlen_) {
942
- EVP_CIPHER_CTX_cleanup(ctx_pktlen_);
943
1005
  EVP_CIPHER_CTX_free(ctx_pktlen_);
944
1006
  ctx_pktlen_ = nullptr;
945
1007
  }
946
1008
  if (ctx_main_) {
947
- EVP_CIPHER_CTX_cleanup(ctx_main_);
948
1009
  EVP_CIPHER_CTX_free(ctx_main_);
949
1010
  ctx_main_ = nullptr;
950
1011
  }
1012
+ #if REAL_OPENSSL_MAJOR >= 3
1013
+ if (mac_ctx_) {
1014
+ EVP_MAC_CTX_free(mac_ctx_);
1015
+ mac_ctx_ = nullptr;
1016
+ }
1017
+ if (mac_) {
1018
+ EVP_MAC_free(mac_);
1019
+ mac_ = nullptr;
1020
+ }
1021
+ #else
951
1022
  if (polykey_) {
952
1023
  EVP_PKEY_free(polykey_);
953
1024
  polykey_ = nullptr;
@@ -958,6 +1029,7 @@ class ChaChaPolyDecipher : public ObjectWrap {
958
1029
  }
959
1030
  // `polykey_ctx_` is not explicitly freed as it is freed implicitly when
960
1031
  // `md_ctx_` is freed
1032
+ #endif
961
1033
  }
962
1034
 
963
1035
  ErrorType init(unsigned char* keys, size_t keys_len) {
@@ -976,7 +1048,14 @@ class ChaChaPolyDecipher : public ObjectWrap {
976
1048
 
977
1049
  if ((ctx_pktlen_ = EVP_CIPHER_CTX_new()) == nullptr
978
1050
  || (ctx_main_ = EVP_CIPHER_CTX_new()) == nullptr
1051
+ #if REAL_OPENSSL_MAJOR >= 3
1052
+ || (mac_ = EVP_MAC_fetch(nullptr,
1053
+ "POLY1305",
1054
+ "provider=default")) == nullptr
1055
+ || (mac_ctx_ = EVP_MAC_CTX_new(mac_)) == nullptr
1056
+ #else
979
1057
  || (md_ctx_ = EVP_MD_CTX_new()) == nullptr
1058
+ #endif
980
1059
  || EVP_DecryptInit_ex(ctx_pktlen_,
981
1060
  cipher,
982
1061
  nullptr,
@@ -990,7 +1069,7 @@ class ChaChaPolyDecipher : public ObjectWrap {
990
1069
  r = kErrOpenSSL;
991
1070
  goto out;
992
1071
  }
993
- if (EVP_CIPHER_CTX_iv_length(ctx_pktlen_) != 16) {
1072
+ if (ctx_iv_len(ctx_pktlen_) != 16) {
994
1073
  r = kErrBadIVLen;
995
1074
  goto out;
996
1075
  }
@@ -1083,6 +1162,15 @@ out:
1083
1162
  }
1084
1163
 
1085
1164
  // Poly1305 over ciphertext
1165
+ #if REAL_OPENSSL_MAJOR >= 3
1166
+ if (EVP_MAC_init(mac_ctx_, polykey, sizeof(polykey), nullptr) != 1
1167
+ || EVP_MAC_update(mac_ctx_, length_bytes, sizeof(length_bytes)) != 1
1168
+ || EVP_MAC_update(mac_ctx_, packet, packet_len) != 1
1169
+ || EVP_MAC_final(mac_ctx_, calc_mac, &sig_len, sig_len) != 1) {
1170
+ r = kErrOpenSSL;
1171
+ goto out;
1172
+ }
1173
+ #else
1086
1174
  if (polykey_) {
1087
1175
  if (EVP_PKEY_CTX_ctrl(polykey_ctx_,
1088
1176
  -1,
@@ -1128,6 +1216,7 @@ out:
1128
1216
  r = kErrOpenSSL;
1129
1217
  goto out;
1130
1218
  }
1219
+ #endif
1131
1220
 
1132
1221
  // Compare MACs
1133
1222
  if (CRYPTO_memcmp(mac, calc_mac, sizeof(calc_mac))) {
@@ -1289,9 +1378,14 @@ out:
1289
1378
  unsigned char length_bytes[4];
1290
1379
  EVP_CIPHER_CTX* ctx_main_;
1291
1380
  EVP_CIPHER_CTX* ctx_pktlen_;
1381
+ #if REAL_OPENSSL_MAJOR >= 3
1382
+ EVP_MAC* mac_;
1383
+ EVP_MAC_CTX* mac_ctx_;
1384
+ #else
1292
1385
  EVP_MD_CTX* md_ctx_;
1293
1386
  EVP_PKEY* polykey_;
1294
1387
  EVP_PKEY_CTX* polykey_ctx_;
1388
+ #endif
1295
1389
  };
1296
1390
 
1297
1391
  class AESGCMDecipher : public ObjectWrap {
@@ -1320,7 +1414,6 @@ class AESGCMDecipher : public ObjectWrap {
1320
1414
 
1321
1415
  void clear() {
1322
1416
  if (ctx_) {
1323
- EVP_CIPHER_CTX_cleanup(ctx_);
1324
1417
  EVP_CIPHER_CTX_free(ctx_);
1325
1418
  ctx_ = nullptr;
1326
1419
  }
@@ -1355,12 +1448,12 @@ class AESGCMDecipher : public ObjectWrap {
1355
1448
  goto out;
1356
1449
  }
1357
1450
 
1358
- //~ if (iv_len != static_cast<size_t>(EVP_CIPHER_CTX_iv_length(ctx_))) {
1451
+ //~ if (iv_len != static_cast<size_t>(ctx_iv_len(ctx_))) {
1359
1452
  //~ r = kErrBadIVLen;
1360
1453
  //~ goto out;
1361
1454
  //~ }
1362
1455
 
1363
- if (key_len != static_cast<size_t>(EVP_CIPHER_CTX_key_length(ctx_))) {
1456
+ if (key_len != static_cast<size_t>(ctx_key_len(ctx_))) {
1364
1457
  if (!EVP_CIPHER_CTX_set_key_length(ctx_, key_len)) {
1365
1458
  r = kErrBadKeyLen;
1366
1459
  goto out;
@@ -1578,7 +1671,6 @@ class GenericDecipher : public ObjectWrap {
1578
1671
 
1579
1672
  void clear() {
1580
1673
  if (ctx_) {
1581
- EVP_CIPHER_CTX_cleanup(ctx_);
1582
1674
  EVP_CIPHER_CTX_free(ctx_);
1583
1675
  ctx_ = nullptr;
1584
1676
  }
@@ -1613,12 +1705,12 @@ class GenericDecipher : public ObjectWrap {
1613
1705
  goto out;
1614
1706
  }
1615
1707
 
1616
- if (iv_len != static_cast<size_t>(EVP_CIPHER_CTX_iv_length(ctx_))) {
1708
+ if (iv_len != static_cast<size_t>(ctx_iv_len(ctx_))) {
1617
1709
  r = kErrBadIVLen;
1618
1710
  goto out;
1619
1711
  }
1620
1712
 
1621
- if (key_len != static_cast<size_t>(EVP_CIPHER_CTX_key_length(ctx_))) {
1713
+ if (key_len != static_cast<size_t>(ctx_key_len(ctx_))) {
1622
1714
  if (!EVP_CIPHER_CTX_set_key_length(ctx_, key_len)) {
1623
1715
  r = kErrBadKeyLen;
1624
1716
  goto out;
@@ -1673,7 +1765,11 @@ class GenericDecipher : public ObjectWrap {
1673
1765
  hmac_len_ = HMAC_size(ctx_hmac_);
1674
1766
  hmac_actual_len_ = hmac_actual_len;
1675
1767
  is_etm_ = is_etm;
1768
+ #if REAL_OPENSSL_MAJOR >= 3
1676
1769
  switch (EVP_CIPHER_CTX_mode(ctx_)) {
1770
+ #else
1771
+ switch (cipher_flags(EVP_CIPHER_CTX_cipher(ctx_)) & EVP_CIPH_MODE) {
1772
+ #endif
1677
1773
  case EVP_CIPH_STREAM_CIPHER:
1678
1774
  case EVP_CIPH_CTR_MODE:
1679
1775
  is_stream_ = 1;
@@ -1681,7 +1777,7 @@ class GenericDecipher : public ObjectWrap {
1681
1777
  default:
1682
1778
  is_stream_ = 0;
1683
1779
  }
1684
- block_size_ = EVP_CIPHER_CTX_block_size(ctx_);
1780
+ block_size_ = ctx_get_block_size(ctx_);
1685
1781
 
1686
1782
  out:
1687
1783
  return r;
@@ -1991,6 +2087,55 @@ out:
1991
2087
 
1992
2088
 
1993
2089
  NAN_MODULE_INIT(init) {
2090
+ // These are needed because node-gyp (as of this writing) does not use the
2091
+ // proper (OpenSSL) system headers when node was built against a shared
2092
+ // version of OpenSSL. Usually this isn't an issue because OSes that build
2093
+ // node in this way typically use the same version of OpenSSL as was bundled
2094
+ // with node for a particular node version for the best compatibility. However
2095
+ // with the inclusion of OpenSSL 3.x in node v17.x, some OSes are still
2096
+ // linking with a shared OpenSSL 1.x, which can cause both compilation and
2097
+ // runtime errors because of changes in OpenSSL's code.
2098
+ //
2099
+ // For that reason, we need to make sure we need to resolve some specific
2100
+ // symbols at runtime to workaround these buggy situations.
2101
+ #ifdef _WIN32
2102
+ # define load_sym(name) GetProcAddress(GetModuleHandle(NULL), name)
2103
+ #else
2104
+ # define load_sym(name) dlsym(RTLD_DEFAULT, name)
2105
+ #endif
2106
+ ctx_iv_len = reinterpret_cast<ctx_iv_len_func>(
2107
+ load_sym("EVP_CIPHER_CTX_get_iv_length")
2108
+ );
2109
+ if (!ctx_iv_len) {
2110
+ ctx_iv_len = reinterpret_cast<ctx_iv_len_func>(
2111
+ load_sym("EVP_CIPHER_CTX_iv_length")
2112
+ );
2113
+ }
2114
+ ctx_key_len = reinterpret_cast<ctx_key_len_func>(
2115
+ load_sym("EVP_CIPHER_CTX_get_key_length")
2116
+ );
2117
+ if (!ctx_key_len) {
2118
+ ctx_key_len = reinterpret_cast<ctx_key_len_func>(
2119
+ load_sym("EVP_CIPHER_CTX_key_length")
2120
+ );
2121
+ }
2122
+ cipher_flags = reinterpret_cast<cipher_flags_func>(
2123
+ load_sym("EVP_CIPHER_get_flags")
2124
+ );
2125
+ if (!cipher_flags) {
2126
+ cipher_flags = reinterpret_cast<cipher_flags_func>(
2127
+ load_sym("EVP_CIPHER_flags")
2128
+ );
2129
+ }
2130
+ ctx_get_block_size = reinterpret_cast<ctx_get_block_size_func>(
2131
+ load_sym("EVP_CIPHER_CTX_get_block_size")
2132
+ );
2133
+ if (!ctx_get_block_size) {
2134
+ ctx_get_block_size = reinterpret_cast<ctx_get_block_size_func>(
2135
+ load_sym("EVP_CIPHER_CTX_block_size")
2136
+ );
2137
+ }
2138
+
1994
2139
  ChaChaPolyCipher::Init(target);
1995
2140
  AESGCMCipher::Init(target);
1996
2141
  GenericCipher::Init(target);
@@ -595,6 +595,8 @@ OpenSSH_Private.prototype = BaseKey;
595
595
  } else {
596
596
  ret = [];
597
597
  }
598
+ if (ret instanceof Error)
599
+ return ret;
598
600
  // This will need to change if/when OpenSSH ever starts storing multiple
599
601
  // keys in their key files
600
602
  return ret[0];
@@ -171,7 +171,7 @@ module.exports = {
171
171
  doFatalError: (protocol, msg, level, reason) => {
172
172
  let err;
173
173
  if (DISCONNECT_REASON === undefined)
174
- ({ DISCONNECT_REASON } = require('./utils.js'));
174
+ ({ DISCONNECT_REASON } = require('./constants.js'));
175
175
  if (msg instanceof Error) {
176
176
  // doFatalError(protocol, err[, reason])
177
177
  err = msg;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/ssh2",
3
- "version": "1.5.0",
3
+ "version": "1.9.0",
4
4
  "author": "Brian White <mscdex@mscdex.net>",
5
5
  "description": "SSH2 client and server modules written in pure JavaScript for node.js",
6
6
  "main": "./lib/index.js",
@@ -15,10 +15,6 @@
15
15
  "@mscdex/eslint-config": "^1.0.0",
16
16
  "eslint": "^7.0.0"
17
17
  },
18
- "peerDependencies": {
19
- "cpu-features": "0.0.2",
20
- "nan": "^2.15.0"
21
- },
22
18
  "scripts": {
23
19
  "install": "node install.js",
24
20
  "rebuild": "node install.js",