@electerm/ssh2 1.11.2 → 1.16.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,194 @@
1
+ # This file is generated by gyp; do not edit.
2
+
3
+ TOOLSET := target
4
+ TARGET := sshcrypto
5
+ DEFS_Debug := \
6
+ '-DNODE_GYP_MODULE_NAME=sshcrypto' \
7
+ '-DUSING_UV_SHARED=1' \
8
+ '-DUSING_V8_SHARED=1' \
9
+ '-DV8_DEPRECATION_WARNINGS=1' \
10
+ '-DV8_DEPRECATION_WARNINGS' \
11
+ '-DV8_IMMINENT_DEPRECATION_WARNINGS' \
12
+ '-D_GLIBCXX_USE_CXX11_ABI=1' \
13
+ '-D_DARWIN_USE_64_BIT_INODE=1' \
14
+ '-D_LARGEFILE_SOURCE' \
15
+ '-D_FILE_OFFSET_BITS=64' \
16
+ '-DOPENSSL_NO_PINSHARED' \
17
+ '-DOPENSSL_THREADS' \
18
+ '-DOPENSSL_API_COMPAT=0x10100000L' \
19
+ '-DREAL_OPENSSL_MAJOR=3' \
20
+ '-DBUILDING_NODE_EXTENSION' \
21
+ '-DDEBUG' \
22
+ '-D_DEBUG' \
23
+ '-DV8_ENABLE_CHECKS'
24
+
25
+ # Flags passed to all source files.
26
+ CFLAGS_Debug := \
27
+ -O0 \
28
+ -gdwarf-2 \
29
+ -mmacosx-version-min=10.15 \
30
+ -arch x86_64 \
31
+ -Wall \
32
+ -Wendif-labels \
33
+ -W \
34
+ -Wno-unused-parameter
35
+
36
+ # Flags passed to only C files.
37
+ CFLAGS_C_Debug := \
38
+ -fno-strict-aliasing
39
+
40
+ # Flags passed to only C++ files.
41
+ CFLAGS_CC_Debug := \
42
+ -std=gnu++17 \
43
+ -stdlib=libc++ \
44
+ -fno-rtti \
45
+ -fno-exceptions \
46
+ -fno-strict-aliasing
47
+
48
+ # Flags passed to only ObjC files.
49
+ CFLAGS_OBJC_Debug :=
50
+
51
+ # Flags passed to only ObjC++ files.
52
+ CFLAGS_OBJCC_Debug :=
53
+
54
+ INCS_Debug := \
55
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/include/node \
56
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/src \
57
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/config \
58
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/openssl/include \
59
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/uv/include \
60
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/zlib \
61
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/v8/include \
62
+ -I$(srcdir)/../../../node_modules/nan
63
+
64
+ DEFS_Release := \
65
+ '-DNODE_GYP_MODULE_NAME=sshcrypto' \
66
+ '-DUSING_UV_SHARED=1' \
67
+ '-DUSING_V8_SHARED=1' \
68
+ '-DV8_DEPRECATION_WARNINGS=1' \
69
+ '-DV8_DEPRECATION_WARNINGS' \
70
+ '-DV8_IMMINENT_DEPRECATION_WARNINGS' \
71
+ '-D_GLIBCXX_USE_CXX11_ABI=1' \
72
+ '-D_DARWIN_USE_64_BIT_INODE=1' \
73
+ '-D_LARGEFILE_SOURCE' \
74
+ '-D_FILE_OFFSET_BITS=64' \
75
+ '-DOPENSSL_NO_PINSHARED' \
76
+ '-DOPENSSL_THREADS' \
77
+ '-DOPENSSL_API_COMPAT=0x10100000L' \
78
+ '-DREAL_OPENSSL_MAJOR=3' \
79
+ '-DBUILDING_NODE_EXTENSION'
80
+
81
+ # Flags passed to all source files.
82
+ CFLAGS_Release := \
83
+ -O3 \
84
+ -gdwarf-2 \
85
+ -mmacosx-version-min=10.15 \
86
+ -arch x86_64 \
87
+ -Wall \
88
+ -Wendif-labels \
89
+ -W \
90
+ -Wno-unused-parameter
91
+
92
+ # Flags passed to only C files.
93
+ CFLAGS_C_Release := \
94
+ -fno-strict-aliasing
95
+
96
+ # Flags passed to only C++ files.
97
+ CFLAGS_CC_Release := \
98
+ -std=gnu++17 \
99
+ -stdlib=libc++ \
100
+ -fno-rtti \
101
+ -fno-exceptions \
102
+ -fno-strict-aliasing
103
+
104
+ # Flags passed to only ObjC files.
105
+ CFLAGS_OBJC_Release :=
106
+
107
+ # Flags passed to only ObjC++ files.
108
+ CFLAGS_OBJCC_Release :=
109
+
110
+ INCS_Release := \
111
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/include/node \
112
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/src \
113
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/config \
114
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/openssl/include \
115
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/uv/include \
116
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/zlib \
117
+ -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/v8/include \
118
+ -I$(srcdir)/../../../node_modules/nan
119
+
120
+ OBJS := \
121
+ $(obj).target/$(TARGET)/src/binding.o
122
+
123
+ # Add to the list of files we specially track dependencies for.
124
+ all_deps += $(OBJS)
125
+
126
+ # CFLAGS et al overrides must be target-local.
127
+ # See "Target-specific Variable Values" in the GNU Make manual.
128
+ $(OBJS): TOOLSET := $(TOOLSET)
129
+ $(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
130
+ $(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
131
+ $(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE))
132
+ $(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE))
133
+
134
+ # Suffix rules, putting all outputs into $(obj).
135
+
136
+ $(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
137
+ @$(call do_cmd,cxx,1)
138
+
139
+ # Try building from generated source, too.
140
+
141
+ $(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
142
+ @$(call do_cmd,cxx,1)
143
+
144
+ $(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
145
+ @$(call do_cmd,cxx,1)
146
+
147
+ # End of this set of suffix rules
148
+ ### Rules for final target.
149
+ LDFLAGS_Debug := \
150
+ -undefined dynamic_lookup \
151
+ -Wl,-search_paths_first \
152
+ -mmacosx-version-min=10.15 \
153
+ -arch x86_64 \
154
+ -L$(builddir) \
155
+ -stdlib=libc++
156
+
157
+ LIBTOOLFLAGS_Debug := \
158
+ -undefined dynamic_lookup \
159
+ -Wl,-search_paths_first
160
+
161
+ LDFLAGS_Release := \
162
+ -undefined dynamic_lookup \
163
+ -Wl,-search_paths_first \
164
+ -mmacosx-version-min=10.15 \
165
+ -arch x86_64 \
166
+ -L$(builddir) \
167
+ -stdlib=libc++
168
+
169
+ LIBTOOLFLAGS_Release := \
170
+ -undefined dynamic_lookup \
171
+ -Wl,-search_paths_first
172
+
173
+ LIBS :=
174
+
175
+ $(builddir)/sshcrypto.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
176
+ $(builddir)/sshcrypto.node: LIBS := $(LIBS)
177
+ $(builddir)/sshcrypto.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE))
178
+ $(builddir)/sshcrypto.node: TOOLSET := $(TOOLSET)
179
+ $(builddir)/sshcrypto.node: $(OBJS) FORCE_DO_CMD
180
+ $(call do_cmd,solink_module)
181
+
182
+ all_deps += $(builddir)/sshcrypto.node
183
+ # Add target alias
184
+ .PHONY: sshcrypto
185
+ sshcrypto: $(builddir)/sshcrypto.node
186
+
187
+ # Short alias for building this executable.
188
+ .PHONY: sshcrypto.node
189
+ sshcrypto.node: $(builddir)/sshcrypto.node
190
+
191
+ # Add executable to "all" target.
192
+ .PHONY: all
193
+ all: $(builddir)/sshcrypto.node
194
+
@@ -146,6 +146,48 @@ module.exports = {
146
146
  const handler = self._handlers.SERVICE_ACCEPT;
147
147
  handler && handler(self, name);
148
148
  },
149
+ [MESSAGE.EXT_INFO]: (self, payload) => {
150
+ /*
151
+ byte SSH_MSG_EXT_INFO
152
+ uint32 nr-extensions
153
+ repeat the following 2 fields "nr-extensions" times:
154
+ string extension-name
155
+ string extension-value (binary)
156
+ */
157
+ bufferParser.init(payload, 1);
158
+ const numExts = bufferParser.readUInt32BE();
159
+ let exts;
160
+ if (numExts !== undefined) {
161
+ exts = [];
162
+ for (let i = 0; i < numExts; ++i) {
163
+ const name = bufferParser.readString(true);
164
+ const data = bufferParser.readString();
165
+ if (data !== undefined) {
166
+ switch (name) {
167
+ case 'server-sig-algs': {
168
+ const algs = data.latin1Slice(0, data.length).split(',');
169
+ exts.push({ name, algs });
170
+ continue;
171
+ }
172
+ default:
173
+ continue;
174
+ }
175
+ }
176
+ // Malformed
177
+ exts = undefined;
178
+ break;
179
+ }
180
+ }
181
+ bufferParser.clear();
182
+
183
+ if (exts === undefined)
184
+ return doFatalError(self, 'Inbound: Malformed EXT_INFO packet');
185
+
186
+ self._debug && self._debug('Inbound: Received EXT_INFO');
187
+
188
+ const handler = self._handlers.EXT_INFO;
189
+ handler && handler(self, exts);
190
+ },
149
191
 
150
192
  // User auth protocol -- generic =============================================
151
193
  [MESSAGE.USERAUTH_REQUEST]: (self, payload) => {
@@ -195,7 +237,21 @@ module.exports = {
195
237
  const hasSig = bufferParser.readBool();
196
238
  if (hasSig !== undefined) {
197
239
  const keyAlgo = bufferParser.readString(true);
240
+ let realKeyAlgo = keyAlgo;
198
241
  const key = bufferParser.readString();
242
+
243
+ let hashAlgo;
244
+ switch (keyAlgo) {
245
+ case 'rsa-sha2-256':
246
+ realKeyAlgo = 'ssh-rsa';
247
+ hashAlgo = 'sha256';
248
+ break;
249
+ case 'rsa-sha2-512':
250
+ realKeyAlgo = 'ssh-rsa';
251
+ hashAlgo = 'sha512';
252
+ break;
253
+ }
254
+
199
255
  if (hasSig) {
200
256
  const blobEnd = bufferParser.pos();
201
257
  let signature = bufferParser.readString();
@@ -206,7 +262,7 @@ module.exports = {
206
262
  signature = bufferSlice(signature, 4 + keyAlgo.length + 4);
207
263
  }
208
264
 
209
- signature = sigSSHToASN1(signature, keyAlgo);
265
+ signature = sigSSHToASN1(signature, realKeyAlgo);
210
266
  if (signature) {
211
267
  const sessionID = self._kex.sessionID;
212
268
  const blob = Buffer.allocUnsafe(4 + sessionID.length + blobEnd);
@@ -217,15 +273,16 @@ module.exports = {
217
273
  4 + sessionID.length
218
274
  );
219
275
  methodData = {
220
- keyAlgo,
276
+ keyAlgo: realKeyAlgo,
221
277
  key,
222
278
  signature,
223
279
  blob,
280
+ hashAlgo,
224
281
  };
225
282
  }
226
283
  }
227
284
  } else {
228
- methodData = { keyAlgo, key };
285
+ methodData = { keyAlgo: realKeyAlgo, key, hashAlgo };
229
286
  methodDesc = 'publickey -- check';
230
287
  }
231
288
  }
@@ -241,10 +298,23 @@ module.exports = {
241
298
  string signature
242
299
  */
243
300
  const keyAlgo = bufferParser.readString(true);
301
+ let realKeyAlgo = keyAlgo;
244
302
  const key = bufferParser.readString();
245
303
  const localHostname = bufferParser.readString(true);
246
304
  const localUsername = bufferParser.readString(true);
247
305
 
306
+ let hashAlgo;
307
+ switch (keyAlgo) {
308
+ case 'rsa-sha2-256':
309
+ realKeyAlgo = 'ssh-rsa';
310
+ hashAlgo = 'sha256';
311
+ break;
312
+ case 'rsa-sha2-512':
313
+ realKeyAlgo = 'ssh-rsa';
314
+ hashAlgo = 'sha512';
315
+ break;
316
+ }
317
+
248
318
  const blobEnd = bufferParser.pos();
249
319
  let signature = bufferParser.readString();
250
320
  if (signature !== undefined) {
@@ -254,7 +324,7 @@ module.exports = {
254
324
  signature = bufferSlice(signature, 4 + keyAlgo.length + 4);
255
325
  }
256
326
 
257
- signature = sigSSHToASN1(signature, keyAlgo);
327
+ signature = sigSSHToASN1(signature, realKeyAlgo);
258
328
  if (signature !== undefined) {
259
329
  const sessionID = self._kex.sessionID;
260
330
  const blob = Buffer.allocUnsafe(4 + sessionID.length + blobEnd);
@@ -265,12 +335,13 @@ module.exports = {
265
335
  4 + sessionID.length
266
336
  );
267
337
  methodData = {
268
- keyAlgo,
338
+ keyAlgo: realKeyAlgo,
269
339
  key,
270
340
  signature,
271
341
  blob,
272
342
  localHostname,
273
343
  localUsername,
344
+ hashAlgo
274
345
  };
275
346
  }
276
347
  }
@@ -222,12 +222,39 @@ function handleKexInit(self, payload) {
222
222
  // Key exchange method =======================================================
223
223
  debug && debug(`Handshake: (local) KEX method: ${localKex}`);
224
224
  debug && debug(`Handshake: (remote) KEX method: ${remote.kex}`);
225
+ let remoteExtInfoEnabled;
225
226
  if (self._server) {
226
227
  serverList = localKex;
227
228
  clientList = remote.kex;
229
+ remoteExtInfoEnabled = (clientList.indexOf('ext-info-c') !== -1);
228
230
  } else {
229
231
  serverList = remote.kex;
230
232
  clientList = localKex;
233
+ remoteExtInfoEnabled = (serverList.indexOf('ext-info-s') !== -1);
234
+ }
235
+ if (self._strictMode === undefined) {
236
+ if (self._server) {
237
+ self._strictMode =
238
+ (clientList.indexOf('kex-strict-c-v00@openssh.com') !== -1);
239
+ } else {
240
+ self._strictMode =
241
+ (serverList.indexOf('kex-strict-s-v00@openssh.com') !== -1);
242
+ }
243
+ // Note: We check for seqno of 1 instead of 0 since we increment before
244
+ // calling the packet handler
245
+ if (self._strictMode) {
246
+ debug && debug('Handshake: strict KEX mode enabled');
247
+ if (self._decipher.inSeqno !== 1) {
248
+ if (debug)
249
+ debug('Handshake: KEXINIT not first packet in strict KEX mode');
250
+ return doFatalError(
251
+ self,
252
+ 'Handshake failed: KEXINIT not first packet in strict KEX mode',
253
+ 'handshake',
254
+ DISCONNECT_REASON.KEY_EXCHANGE_FAILED
255
+ );
256
+ }
257
+ }
231
258
  }
232
259
  // Check for agreeable key exchange algorithm
233
260
  for (i = 0;
@@ -235,7 +262,7 @@ function handleKexInit(self, payload) {
235
262
  ++i);
236
263
  if (i === clientList.length) {
237
264
  // No suitable match found!
238
- debug && debug('Handshake: No matching key exchange algorithm');
265
+ debug && debug('Handshake: no matching key exchange algorithm');
239
266
  return doFatalError(
240
267
  self,
241
268
  'Handshake failed: no matching key exchange algorithm',
@@ -479,6 +506,7 @@ function handleKexInit(self, payload) {
479
506
  }
480
507
 
481
508
  self._kex = createKeyExchange(init, self, payload);
509
+ self._kex.remoteExtInfoEnabled = remoteExtInfoEnabled;
482
510
  self._kex.start();
483
511
  }
484
512
 
@@ -510,6 +538,7 @@ const createKeyExchange = (() => {
510
538
 
511
539
  this.sessionID = (protocol._kex ? protocol._kex.sessionID : undefined);
512
540
  this.negotiated = negotiated;
541
+ this.remoteExtInfoEnabled = false;
513
542
  this._step = 1;
514
543
  this._public = null;
515
544
  this._dh = null;
@@ -527,7 +556,7 @@ const createKeyExchange = (() => {
527
556
  this._dhData = undefined;
528
557
  this._sig = undefined;
529
558
  }
530
- finish() {
559
+ finish(scOnly) {
531
560
  if (this._finished)
532
561
  return false;
533
562
  this._finished = true;
@@ -778,9 +807,26 @@ const createKeyExchange = (() => {
778
807
  this._protocol._packetRW.write.finalize(packet, true)
779
808
  );
780
809
  }
781
- trySendNEWKEYS(this);
782
810
 
783
- const completeHandshake = () => {
811
+ if (isServer || !scOnly)
812
+ trySendNEWKEYS(this);
813
+
814
+ let hsCipherConfig;
815
+ let hsWrite;
816
+ const completeHandshake = (partial) => {
817
+ if (hsCipherConfig) {
818
+ trySendNEWKEYS(this);
819
+ hsCipherConfig.outbound.seqno = this._protocol._cipher.outSeqno;
820
+ this._protocol._cipher.free();
821
+ this._protocol._cipher = createCipher(hsCipherConfig);
822
+ this._protocol._packetRW.write = hsWrite;
823
+ hsCipherConfig = undefined;
824
+ hsWrite = undefined;
825
+ this._protocol._onHandshakeComplete(negotiated);
826
+
827
+ return false;
828
+ }
829
+
784
830
  if (!this.sessionID)
785
831
  this.sessionID = exchangeHash;
786
832
 
@@ -863,9 +909,8 @@ const createKeyExchange = (() => {
863
909
  macKey: (isServer ? scMacKey : csMacKey),
864
910
  },
865
911
  };
866
- this._protocol._cipher && this._protocol._cipher.free();
867
- this._protocol._decipher && this._protocol._decipher.free();
868
- this._protocol._cipher = createCipher(config);
912
+ this._protocol._decipher.free();
913
+ hsCipherConfig = config;
869
914
  this._protocol._decipher = createDecipher(config);
870
915
 
871
916
  const rw = {
@@ -932,7 +977,8 @@ const createKeyExchange = (() => {
932
977
  }
933
978
  this._protocol._packetRW.read.cleanup();
934
979
  this._protocol._packetRW.write.cleanup();
935
- this._protocol._packetRW = rw;
980
+ this._protocol._packetRW.read = rw.read;
981
+ hsWrite = rw.write;
936
982
 
937
983
  // Cleanup/reset various state
938
984
  this._public = null;
@@ -945,13 +991,16 @@ const createKeyExchange = (() => {
945
991
  this._dhData = undefined;
946
992
  this._sig = undefined;
947
993
 
948
- this._protocol._onHandshakeComplete(negotiated);
949
-
994
+ if (!partial)
995
+ return completeHandshake();
950
996
  return false;
951
997
  };
998
+
999
+ if (isServer || scOnly)
1000
+ this.finish = completeHandshake;
1001
+
952
1002
  if (!isServer)
953
- return completeHandshake();
954
- this.finish = completeHandshake;
1003
+ return completeHandshake(scOnly);
955
1004
  }
956
1005
 
957
1006
  start() {
@@ -1211,13 +1260,11 @@ const createKeyExchange = (() => {
1211
1260
  'Inbound: NEWKEYS'
1212
1261
  );
1213
1262
  this._receivedNEWKEYS = true;
1263
+ if (this._protocol._strictMode)
1264
+ this._protocol._decipher.inSeqno = 0;
1214
1265
  ++this._step;
1215
- if (this._protocol._server || this._hostVerified)
1216
- return this.finish();
1217
1266
 
1218
- // Signal to current decipher that we need to change to a new decipher
1219
- // for the next packet
1220
- return false;
1267
+ return this.finish(!this._protocol._server && !this._hostVerified);
1221
1268
  default:
1222
1269
  return doFatalError(
1223
1270
  this._protocol,
@@ -1378,7 +1425,7 @@ const createKeyExchange = (() => {
1378
1425
  parse(payload) {
1379
1426
  const type = payload[0];
1380
1427
  switch (this._step) {
1381
- case 1:
1428
+ case 1: {
1382
1429
  if (this._protocol._server) {
1383
1430
  if (type !== MESSAGE.KEXDH_GEX_REQUEST) {
1384
1431
  return doFatalError(
@@ -1453,6 +1500,7 @@ const createKeyExchange = (() => {
1453
1500
 
1454
1501
  ++this._step;
1455
1502
  break;
1503
+ }
1456
1504
  case 2:
1457
1505
  if (this._protocol._server) {
1458
1506
  if (type !== MESSAGE.KEXDH_GEX_INIT) {
@@ -1734,11 +1782,20 @@ function onKEXPayload(state, payload) {
1734
1782
  payload = this._packetRW.read.read(payload);
1735
1783
 
1736
1784
  const type = payload[0];
1785
+
1786
+ if (!this._strictMode) {
1787
+ switch (type) {
1788
+ case MESSAGE.IGNORE:
1789
+ case MESSAGE.UNIMPLEMENTED:
1790
+ case MESSAGE.DEBUG:
1791
+ if (!MESSAGE_HANDLERS)
1792
+ MESSAGE_HANDLERS = require('./handlers.js');
1793
+ return MESSAGE_HANDLERS[type](this, payload);
1794
+ }
1795
+ }
1796
+
1737
1797
  switch (type) {
1738
1798
  case MESSAGE.DISCONNECT:
1739
- case MESSAGE.IGNORE:
1740
- case MESSAGE.UNIMPLEMENTED:
1741
- case MESSAGE.DEBUG:
1742
1799
  if (!MESSAGE_HANDLERS)
1743
1800
  MESSAGE_HANDLERS = require('./handlers.js');
1744
1801
  return MESSAGE_HANDLERS[type](this, payload);
@@ -1754,6 +1811,8 @@ function onKEXPayload(state, payload) {
1754
1811
  state.firstPacket = false;
1755
1812
  return handleKexInit(this, payload);
1756
1813
  default:
1814
+ // Ensure packet is either an algorithm negotiation or KEX
1815
+ // algorithm-specific packet
1757
1816
  if (type < 20 || type > 49) {
1758
1817
  return doFatalError(
1759
1818
  this,
@@ -1802,6 +1861,8 @@ function trySendNEWKEYS(kex) {
1802
1861
  kex._protocol._packetRW.write.finalize(packet, true)
1803
1862
  );
1804
1863
  kex._sentNEWKEYS = true;
1864
+ if (kex._protocol._strictMode)
1865
+ kex._protocol._cipher.outSeqno = 0;
1805
1866
  }
1806
1867
  }
1807
1868
 
@@ -1809,8 +1870,24 @@ module.exports = {
1809
1870
  KexInit,
1810
1871
  kexinit,
1811
1872
  onKEXPayload,
1812
- DEFAULT_KEXINIT: new KexInit({
1813
- kex: DEFAULT_KEX,
1873
+ DEFAULT_KEXINIT_CLIENT: new KexInit({
1874
+ kex: DEFAULT_KEX.concat(['ext-info-c', 'kex-strict-c-v00@openssh.com']),
1875
+ serverHostKey: DEFAULT_SERVER_HOST_KEY,
1876
+ cs: {
1877
+ cipher: DEFAULT_CIPHER,
1878
+ mac: DEFAULT_MAC,
1879
+ compress: DEFAULT_COMPRESSION,
1880
+ lang: [],
1881
+ },
1882
+ sc: {
1883
+ cipher: DEFAULT_CIPHER,
1884
+ mac: DEFAULT_MAC,
1885
+ compress: DEFAULT_COMPRESSION,
1886
+ lang: [],
1887
+ },
1888
+ }),
1889
+ DEFAULT_KEXINIT_SERVER: new KexInit({
1890
+ kex: DEFAULT_KEX.concat(['kex-strict-s-v00@openssh.com']),
1814
1891
  serverHostKey: DEFAULT_SERVER_HOST_KEY,
1815
1892
  cs: {
1816
1893
  cipher: DEFAULT_CIPHER,
@@ -446,7 +446,7 @@ const BaseKey = {
446
446
  this.type === parsed.type
447
447
  && this[SYM_PRIV_PEM] === parsed[SYM_PRIV_PEM]
448
448
  && this[SYM_PUB_PEM] === parsed[SYM_PUB_PEM]
449
- && this[SYM_PUB_SSH] === parsed[SYM_PUB_SSH]
449
+ && this[SYM_PUB_SSH].equals(parsed[SYM_PUB_SSH])
450
450
  );
451
451
  },
452
452
  };
@@ -512,7 +512,7 @@ OpenSSH_Private.prototype = BaseKey;
512
512
  switch (kdfName) {
513
513
  case 'none':
514
514
  return new Error('Malformed OpenSSH private key');
515
- case 'bcrypt':
515
+ case 'bcrypt': {
516
516
  /*
517
517
  string salt
518
518
  uint32 rounds
@@ -534,6 +534,7 @@ OpenSSH_Private.prototype = BaseKey;
534
534
  cipherKey = bufferSlice(gen, 0, encInfo.keyLen);
535
535
  cipherIV = bufferSlice(gen, encInfo.keyLen, gen.length);
536
536
  break;
537
+ }
537
538
  }
538
539
  } else if (kdfName !== 'none') {
539
540
  return new Error('Malformed OpenSSH private key');
@@ -573,6 +574,7 @@ OpenSSH_Private.prototype = BaseKey;
573
574
  cipherKey,
574
575
  cipherIV,
575
576
  options);
577
+ decipher.setAutoPadding(false);
576
578
  if (encInfo.authLen > 0) {
577
579
  if (data.length - data._pos < encInfo.authLen)
578
580
  return new Error('Malformed OpenSSH private key');
@@ -932,7 +934,7 @@ OpenSSH_Old_Private.prototype = BaseKey;
932
934
  }
933
935
  algo = 'sha1';
934
936
  break;
935
- case 'EC':
937
+ case 'EC': {
936
938
  let ecSSLName;
937
939
  let ecPriv;
938
940
  let ecOID;
@@ -981,6 +983,7 @@ OpenSSH_Old_Private.prototype = BaseKey;
981
983
  pubPEM = genOpenSSLECDSAPub(ecOID, pubBlob);
982
984
  pubSSH = genOpenSSHECDSAPub(ecOID, pubBlob);
983
985
  break;
986
+ }
984
987
  }
985
988
 
986
989
  return new OpenSSH_Old_Private(type, '', privPEM, pubPEM, pubSSH, algo,
@@ -1056,9 +1059,7 @@ PPK_Private.prototype = BaseKey;
1056
1059
  if (cipherKey.length > encInfo.keyLen)
1057
1060
  cipherKey = bufferSlice(cipherKey, 0, encInfo.keyLen);
1058
1061
  try {
1059
- const decipher = createDecipheriv(encInfo.sslName,
1060
- cipherKey,
1061
- PPK_IV);
1062
+ const decipher = createDecipheriv(encInfo.sslName, cipherKey, PPK_IV);
1062
1063
  decipher.setAutoPadding(false);
1063
1064
  privBlob = combineBuffers(decipher.update(privBlob),
1064
1065
  decipher.final());
@@ -69,8 +69,9 @@ class Zlib {
69
69
 
70
70
  writeSync(chunk, retChunks) {
71
71
  const handle = this._handle;
72
- if (!handle)
73
- throw new Error('Invalid Zlib instance');
72
+ if (!handle) {
73
+ return;
74
+ }
74
75
 
75
76
  let availInBefore = chunk.length;
76
77
  let availOutBefore = this._chunkSize - this._outOffset;
@@ -188,6 +189,9 @@ class ZlibPacketWriter {
188
189
  finalize(payload, force) {
189
190
  if (this._protocol._kexinit === undefined || force) {
190
191
  const output = this._zlib.writeSync(payload, true);
192
+ if (!output) {
193
+ return;
194
+ }
191
195
  const packet = this._protocol._cipher.allocPacket(output.totalLen);
192
196
  if (output.push === undefined) {
193
197
  packet.set(output, 5);