@electerm/ssh2 1.11.2 → 1.14.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/lib/client.js +137 -8
- package/lib/index.js +1 -0
- package/lib/keygen.js +582 -0
- package/lib/protocol/Protocol.js +65 -13
- package/lib/protocol/SFTP.js +189 -1
- package/lib/protocol/constants.js +5 -2
- package/lib/protocol/handlers.misc.js +76 -5
- package/lib/protocol/kex.js +56 -18
- package/lib/protocol/keyParser.js +6 -5
- package/lib/server.js +9 -0
- package/package.json +4 -4
package/lib/protocol/kex.js
CHANGED
|
@@ -222,12 +222,15 @@ 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);
|
|
231
234
|
}
|
|
232
235
|
// Check for agreeable key exchange algorithm
|
|
233
236
|
for (i = 0;
|
|
@@ -479,6 +482,7 @@ function handleKexInit(self, payload) {
|
|
|
479
482
|
}
|
|
480
483
|
|
|
481
484
|
self._kex = createKeyExchange(init, self, payload);
|
|
485
|
+
self._kex.remoteExtInfoEnabled = remoteExtInfoEnabled;
|
|
482
486
|
self._kex.start();
|
|
483
487
|
}
|
|
484
488
|
|
|
@@ -510,6 +514,7 @@ const createKeyExchange = (() => {
|
|
|
510
514
|
|
|
511
515
|
this.sessionID = (protocol._kex ? protocol._kex.sessionID : undefined);
|
|
512
516
|
this.negotiated = negotiated;
|
|
517
|
+
this.remoteExtInfoEnabled = false;
|
|
513
518
|
this._step = 1;
|
|
514
519
|
this._public = null;
|
|
515
520
|
this._dh = null;
|
|
@@ -527,7 +532,7 @@ const createKeyExchange = (() => {
|
|
|
527
532
|
this._dhData = undefined;
|
|
528
533
|
this._sig = undefined;
|
|
529
534
|
}
|
|
530
|
-
finish() {
|
|
535
|
+
finish(scOnly) {
|
|
531
536
|
if (this._finished)
|
|
532
537
|
return false;
|
|
533
538
|
this._finished = true;
|
|
@@ -778,9 +783,26 @@ const createKeyExchange = (() => {
|
|
|
778
783
|
this._protocol._packetRW.write.finalize(packet, true)
|
|
779
784
|
);
|
|
780
785
|
}
|
|
781
|
-
trySendNEWKEYS(this);
|
|
782
786
|
|
|
783
|
-
|
|
787
|
+
if (isServer || !scOnly)
|
|
788
|
+
trySendNEWKEYS(this);
|
|
789
|
+
|
|
790
|
+
let hsCipherConfig;
|
|
791
|
+
let hsWrite;
|
|
792
|
+
const completeHandshake = (partial) => {
|
|
793
|
+
if (hsCipherConfig) {
|
|
794
|
+
trySendNEWKEYS(this);
|
|
795
|
+
hsCipherConfig.outbound.seqno = this._protocol._cipher.outSeqno;
|
|
796
|
+
this._protocol._cipher.free();
|
|
797
|
+
this._protocol._cipher = createCipher(hsCipherConfig);
|
|
798
|
+
this._protocol._packetRW.write = hsWrite;
|
|
799
|
+
hsCipherConfig = undefined;
|
|
800
|
+
hsWrite = undefined;
|
|
801
|
+
this._protocol._onHandshakeComplete(negotiated);
|
|
802
|
+
|
|
803
|
+
return false;
|
|
804
|
+
}
|
|
805
|
+
|
|
784
806
|
if (!this.sessionID)
|
|
785
807
|
this.sessionID = exchangeHash;
|
|
786
808
|
|
|
@@ -863,9 +885,8 @@ const createKeyExchange = (() => {
|
|
|
863
885
|
macKey: (isServer ? scMacKey : csMacKey),
|
|
864
886
|
},
|
|
865
887
|
};
|
|
866
|
-
this._protocol.
|
|
867
|
-
|
|
868
|
-
this._protocol._cipher = createCipher(config);
|
|
888
|
+
this._protocol._decipher.free();
|
|
889
|
+
hsCipherConfig = config;
|
|
869
890
|
this._protocol._decipher = createDecipher(config);
|
|
870
891
|
|
|
871
892
|
const rw = {
|
|
@@ -932,7 +953,8 @@ const createKeyExchange = (() => {
|
|
|
932
953
|
}
|
|
933
954
|
this._protocol._packetRW.read.cleanup();
|
|
934
955
|
this._protocol._packetRW.write.cleanup();
|
|
935
|
-
this._protocol._packetRW = rw;
|
|
956
|
+
this._protocol._packetRW.read = rw.read;
|
|
957
|
+
hsWrite = rw.write;
|
|
936
958
|
|
|
937
959
|
// Cleanup/reset various state
|
|
938
960
|
this._public = null;
|
|
@@ -945,13 +967,16 @@ const createKeyExchange = (() => {
|
|
|
945
967
|
this._dhData = undefined;
|
|
946
968
|
this._sig = undefined;
|
|
947
969
|
|
|
948
|
-
|
|
949
|
-
|
|
970
|
+
if (!partial)
|
|
971
|
+
return completeHandshake();
|
|
950
972
|
return false;
|
|
951
973
|
};
|
|
974
|
+
|
|
975
|
+
if (isServer || scOnly)
|
|
976
|
+
this.finish = completeHandshake;
|
|
977
|
+
|
|
952
978
|
if (!isServer)
|
|
953
|
-
return completeHandshake();
|
|
954
|
-
this.finish = completeHandshake;
|
|
979
|
+
return completeHandshake(scOnly);
|
|
955
980
|
}
|
|
956
981
|
|
|
957
982
|
start() {
|
|
@@ -1212,12 +1237,8 @@ const createKeyExchange = (() => {
|
|
|
1212
1237
|
);
|
|
1213
1238
|
this._receivedNEWKEYS = true;
|
|
1214
1239
|
++this._step;
|
|
1215
|
-
if (this._protocol._server || this._hostVerified)
|
|
1216
|
-
return this.finish();
|
|
1217
1240
|
|
|
1218
|
-
|
|
1219
|
-
// for the next packet
|
|
1220
|
-
return false;
|
|
1241
|
+
return this.finish(!this._protocol._server && !this._hostVerified);
|
|
1221
1242
|
default:
|
|
1222
1243
|
return doFatalError(
|
|
1223
1244
|
this._protocol,
|
|
@@ -1378,7 +1399,7 @@ const createKeyExchange = (() => {
|
|
|
1378
1399
|
parse(payload) {
|
|
1379
1400
|
const type = payload[0];
|
|
1380
1401
|
switch (this._step) {
|
|
1381
|
-
case 1:
|
|
1402
|
+
case 1: {
|
|
1382
1403
|
if (this._protocol._server) {
|
|
1383
1404
|
if (type !== MESSAGE.KEXDH_GEX_REQUEST) {
|
|
1384
1405
|
return doFatalError(
|
|
@@ -1453,6 +1474,7 @@ const createKeyExchange = (() => {
|
|
|
1453
1474
|
|
|
1454
1475
|
++this._step;
|
|
1455
1476
|
break;
|
|
1477
|
+
}
|
|
1456
1478
|
case 2:
|
|
1457
1479
|
if (this._protocol._server) {
|
|
1458
1480
|
if (type !== MESSAGE.KEXDH_GEX_INIT) {
|
|
@@ -1809,7 +1831,23 @@ module.exports = {
|
|
|
1809
1831
|
KexInit,
|
|
1810
1832
|
kexinit,
|
|
1811
1833
|
onKEXPayload,
|
|
1812
|
-
|
|
1834
|
+
DEFAULT_KEXINIT_CLIENT: new KexInit({
|
|
1835
|
+
kex: DEFAULT_KEX.concat(['ext-info-c']),
|
|
1836
|
+
serverHostKey: DEFAULT_SERVER_HOST_KEY,
|
|
1837
|
+
cs: {
|
|
1838
|
+
cipher: DEFAULT_CIPHER,
|
|
1839
|
+
mac: DEFAULT_MAC,
|
|
1840
|
+
compress: DEFAULT_COMPRESSION,
|
|
1841
|
+
lang: [],
|
|
1842
|
+
},
|
|
1843
|
+
sc: {
|
|
1844
|
+
cipher: DEFAULT_CIPHER,
|
|
1845
|
+
mac: DEFAULT_MAC,
|
|
1846
|
+
compress: DEFAULT_COMPRESSION,
|
|
1847
|
+
lang: [],
|
|
1848
|
+
},
|
|
1849
|
+
}),
|
|
1850
|
+
DEFAULT_KEXINIT_SERVER: new KexInit({
|
|
1813
1851
|
kex: DEFAULT_KEX,
|
|
1814
1852
|
serverHostKey: DEFAULT_SERVER_HOST_KEY,
|
|
1815
1853
|
cs: {
|
|
@@ -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());
|
package/lib/server.js
CHANGED
|
@@ -136,6 +136,7 @@ class PKAuthContext extends AuthContext {
|
|
|
136
136
|
super(protocol, username, service, method, cb);
|
|
137
137
|
|
|
138
138
|
this.key = { algo: pkInfo.keyAlgo, data: pkInfo.key };
|
|
139
|
+
this.hashAlgo = pkInfo.hashAlgo;
|
|
139
140
|
this.signature = pkInfo.signature;
|
|
140
141
|
this.blob = pkInfo.blob;
|
|
141
142
|
}
|
|
@@ -155,6 +156,7 @@ class HostbasedAuthContext extends AuthContext {
|
|
|
155
156
|
super(protocol, username, service, method, cb);
|
|
156
157
|
|
|
157
158
|
this.key = { algo: pkInfo.keyAlgo, data: pkInfo.key };
|
|
159
|
+
this.hashAlgo = pkInfo.hashAlgo;
|
|
158
160
|
this.signature = pkInfo.signature;
|
|
159
161
|
this.blob = pkInfo.blob;
|
|
160
162
|
this.localHostname = pkInfo.localHostname;
|
|
@@ -1316,6 +1318,13 @@ class Client extends EventEmitter {
|
|
|
1316
1318
|
this.once('rekey', cb);
|
|
1317
1319
|
}
|
|
1318
1320
|
}
|
|
1321
|
+
|
|
1322
|
+
setNoDelay(noDelay) {
|
|
1323
|
+
if (this._sock && typeof this._sock.setNoDelay === 'function')
|
|
1324
|
+
this._sock.setNoDelay(noDelay);
|
|
1325
|
+
|
|
1326
|
+
return this;
|
|
1327
|
+
}
|
|
1319
1328
|
}
|
|
1320
1329
|
|
|
1321
1330
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electerm/ssh2",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.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",
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"node": ">=10.16.0"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"asn1": "^0.2.
|
|
11
|
+
"asn1": "^0.2.6",
|
|
12
12
|
"bcrypt-pbkdf": "^1.0.2"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@mscdex/eslint-config": "^1.1.0",
|
|
16
|
-
"eslint": "^7.
|
|
17
|
-
"nan": "^2.
|
|
16
|
+
"eslint": "^7.32.0",
|
|
17
|
+
"nan": "^2.17.0"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"install": "node install.js",
|