Skip to content

Commit

Permalink
lib: add strict key exchange mode support
Browse files Browse the repository at this point in the history
  • Loading branch information
mscdex committed Dec 18, 2023
1 parent 739a589 commit 97b223f
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 9 deletions.
11 changes: 9 additions & 2 deletions lib/protocol/Protocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,18 @@ class Protocol {
if (typeof offer !== 'object' || offer === null) {
offer = (this._server ? DEFAULT_KEXINIT_SERVER : DEFAULT_KEXINIT_CLIENT);
} else if (offer.constructor !== KexInit) {
if (!this._server)
offer.kex = offer.kex.concat(['ext-info-c']);
if (this._server) {
offer.kex = offer.kex.concat(['kex-strict-s-v00@openssh.com']);
} else {
offer.kex = offer.kex.concat([
'ext-info-c',
'kex-strict-c-v00@openssh.com',
]);
}
offer = new KexInit(offer);
}
this._kex = undefined;
this._strictMode = undefined;
this._kexinit = undefined;
this._offer = offer;
this._cipher = new NullCipher(0, this._onWrite);
Expand Down
51 changes: 45 additions & 6 deletions lib/protocol/kex.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,37 @@ function handleKexInit(self, payload) {
clientList = localKex;
remoteExtInfoEnabled = (serverList.indexOf('ext-info-s') !== -1);
}
if (self._strictMode === undefined) {
if (self._server) {
self._strictMode =
(clientList.indexOf('kex-strict-c-v00@openssh.com') !== -1);
} else {
self._strictMode =
(serverList.indexOf('kex-strict-s-v00@openssh.com') !== -1);
}
// Note: We check for seqno of 1 instead of 0 since we increment before
// calling the packet handler
if (self._strictMode) {
debug && debug('Handshake: strict KEX mode enabled');
if (self._decipher.inSeqno !== 1) {
if (debug)
debug('Handshake: KEXINIT not first packet in strict KEX mode');
return doFatalError(
self,
'Handshake failed: KEXINIT not first packet in strict KEX mode',
'handshake',
DISCONNECT_REASON.KEY_EXCHANGE_FAILED
);
}
}
}
// Check for agreeable key exchange algorithm
for (i = 0;
i < clientList.length && serverList.indexOf(clientList[i]) === -1;
++i);
if (i === clientList.length) {
// No suitable match found!
debug && debug('Handshake: No matching key exchange algorithm');
debug && debug('Handshake: no matching key exchange algorithm');
return doFatalError(
self,
'Handshake failed: no matching key exchange algorithm',
Expand Down Expand Up @@ -1236,6 +1260,8 @@ const createKeyExchange = (() => {
'Inbound: NEWKEYS'
);
this._receivedNEWKEYS = true;
if (this._protocol._strictMode)
this._protocol._decipher.inSeqno = 0;
++this._step;

return this.finish(!this._protocol._server && !this._hostVerified);
Expand Down Expand Up @@ -1756,11 +1782,20 @@ function onKEXPayload(state, payload) {
payload = this._packetRW.read.read(payload);

const type = payload[0];

if (!this._strictMode) {
switch (type) {
case MESSAGE.IGNORE:
case MESSAGE.UNIMPLEMENTED:
case MESSAGE.DEBUG:
if (!MESSAGE_HANDLERS)
MESSAGE_HANDLERS = require('./handlers.js');
return MESSAGE_HANDLERS[type](this, payload);
}
}

switch (type) {
case MESSAGE.DISCONNECT:
case MESSAGE.IGNORE:
case MESSAGE.UNIMPLEMENTED:
case MESSAGE.DEBUG:
if (!MESSAGE_HANDLERS)
MESSAGE_HANDLERS = require('./handlers.js');
return MESSAGE_HANDLERS[type](this, payload);
Expand All @@ -1776,6 +1811,8 @@ function onKEXPayload(state, payload) {
state.firstPacket = false;
return handleKexInit(this, payload);
default:
// Ensure packet is either an algorithm negotiation or KEX
// algorithm-specific packet
if (type < 20 || type > 49) {
return doFatalError(
this,
Expand Down Expand Up @@ -1824,6 +1861,8 @@ function trySendNEWKEYS(kex) {
kex._protocol._packetRW.write.finalize(packet, true)
);
kex._sentNEWKEYS = true;
if (kex._protocol._strictMode)
kex._protocol._cipher.outSeqno = 0;
}
}

Expand All @@ -1832,7 +1871,7 @@ module.exports = {
kexinit,
onKEXPayload,
DEFAULT_KEXINIT_CLIENT: new KexInit({
kex: DEFAULT_KEX.concat(['ext-info-c']),
kex: DEFAULT_KEX.concat(['ext-info-c', 'kex-strict-c-v00@openssh.com']),
serverHostKey: DEFAULT_SERVER_HOST_KEY,
cs: {
cipher: DEFAULT_CIPHER,
Expand All @@ -1848,7 +1887,7 @@ module.exports = {
},
}),
DEFAULT_KEXINIT_SERVER: new KexInit({
kex: DEFAULT_KEX,
kex: DEFAULT_KEX.concat(['kex-strict-s-v00@openssh.com']),
serverHostKey: DEFAULT_SERVER_HOST_KEY,
cs: {
cipher: DEFAULT_CIPHER,
Expand Down
6 changes: 5 additions & 1 deletion lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,11 @@ class Server extends EventEmitter {
}

const algorithms = {
kex: generateAlgorithmList(cfgAlgos.kex, DEFAULT_KEX, SUPPORTED_KEX),
kex: generateAlgorithmList(
cfgAlgos.kex,
DEFAULT_KEX,
SUPPORTED_KEX
).concat(['kex-strict-s-v00@openssh.com']),
serverHostKey: hostKeyAlgoOrder,
cs: {
cipher: generateAlgorithmList(
Expand Down

0 comments on commit 97b223f

Please sign in to comment.