kex.js 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831
  1. 'use strict';
  2. const {
  3. createDiffieHellman,
  4. createDiffieHellmanGroup,
  5. createECDH,
  6. createHash,
  7. createPublicKey,
  8. diffieHellman,
  9. generateKeyPairSync,
  10. randomFillSync,
  11. } = require('crypto');
  12. const { Ber } = require('asn1');
  13. const {
  14. COMPAT,
  15. curve25519Supported,
  16. DEFAULT_KEX,
  17. DEFAULT_SERVER_HOST_KEY,
  18. DEFAULT_CIPHER,
  19. DEFAULT_MAC,
  20. DEFAULT_COMPRESSION,
  21. DISCONNECT_REASON,
  22. MESSAGE,
  23. } = require('./constants.js');
  24. const {
  25. CIPHER_INFO,
  26. createCipher,
  27. createDecipher,
  28. MAC_INFO,
  29. } = require('./crypto.js');
  30. const { parseDERKey } = require('./keyParser.js');
  31. const {
  32. bufferFill,
  33. bufferParser,
  34. convertSignature,
  35. doFatalError,
  36. FastBuffer,
  37. sigSSHToASN1,
  38. writeUInt32BE,
  39. } = require('./utils.js');
  40. const {
  41. PacketReader,
  42. PacketWriter,
  43. ZlibPacketReader,
  44. ZlibPacketWriter,
  45. } = require('./zlib.js');
  46. let MESSAGE_HANDLERS;
  47. const GEX_MIN_BITS = 2048; // RFC 8270
  48. const GEX_MAX_BITS = 8192; // RFC 8270
  49. const EMPTY_BUFFER = Buffer.alloc(0);
  50. // Client/Server
  51. function kexinit(self) {
  52. /*
  53. byte SSH_MSG_KEXINIT
  54. byte[16] cookie (random bytes)
  55. name-list kex_algorithms
  56. name-list server_host_key_algorithms
  57. name-list encryption_algorithms_client_to_server
  58. name-list encryption_algorithms_server_to_client
  59. name-list mac_algorithms_client_to_server
  60. name-list mac_algorithms_server_to_client
  61. name-list compression_algorithms_client_to_server
  62. name-list compression_algorithms_server_to_client
  63. name-list languages_client_to_server
  64. name-list languages_server_to_client
  65. boolean first_kex_packet_follows
  66. uint32 0 (reserved for future extension)
  67. */
  68. let payload;
  69. if (self._compatFlags & COMPAT.BAD_DHGEX) {
  70. const entry = self._offer.lists.kex;
  71. let kex = entry.array;
  72. let found = false;
  73. for (let i = 0; i < kex.length; ++i) {
  74. if (kex[i].indexOf('group-exchange') !== -1) {
  75. if (!found) {
  76. found = true;
  77. // Copy array lazily
  78. kex = kex.slice();
  79. }
  80. kex.splice(i--, 1);
  81. }
  82. }
  83. if (found) {
  84. let len = 1 + 16 + self._offer.totalSize + 1 + 4;
  85. const newKexBuf = Buffer.from(kex.join(','));
  86. len -= (entry.buffer.length - newKexBuf.length);
  87. const all = self._offer.lists.all;
  88. const rest = new Uint8Array(
  89. all.buffer,
  90. all.byteOffset + 4 + entry.buffer.length,
  91. all.length - (4 + entry.buffer.length)
  92. );
  93. payload = Buffer.allocUnsafe(len);
  94. writeUInt32BE(payload, newKexBuf.length, 0);
  95. payload.set(newKexBuf, 4);
  96. payload.set(rest, 4 + newKexBuf.length);
  97. }
  98. }
  99. if (payload === undefined) {
  100. payload = Buffer.allocUnsafe(1 + 16 + self._offer.totalSize + 1 + 4);
  101. self._offer.copyAllTo(payload, 17);
  102. }
  103. self._debug && self._debug('Outbound: Sending KEXINIT');
  104. payload[0] = MESSAGE.KEXINIT;
  105. randomFillSync(payload, 1, 16);
  106. // Zero-fill first_kex_packet_follows and reserved bytes
  107. bufferFill(payload, 0, payload.length - 5);
  108. self._kexinit = payload;
  109. // Needed to correct the starting position in allocated "packets" when packets
  110. // will be buffered due to active key exchange
  111. self._packetRW.write.allocStart = 0;
  112. // TODO: only create single buffer and set _kexinit as slice of packet instead
  113. {
  114. const p = self._packetRW.write.allocStartKEX;
  115. const packet = self._packetRW.write.alloc(payload.length, true);
  116. packet.set(payload, p);
  117. self._cipher.encrypt(self._packetRW.write.finalize(packet, true));
  118. }
  119. }
  120. function handleKexInit(self, payload) {
  121. /*
  122. byte SSH_MSG_KEXINIT
  123. byte[16] cookie (random bytes)
  124. name-list kex_algorithms
  125. name-list server_host_key_algorithms
  126. name-list encryption_algorithms_client_to_server
  127. name-list encryption_algorithms_server_to_client
  128. name-list mac_algorithms_client_to_server
  129. name-list mac_algorithms_server_to_client
  130. name-list compression_algorithms_client_to_server
  131. name-list compression_algorithms_server_to_client
  132. name-list languages_client_to_server
  133. name-list languages_server_to_client
  134. boolean first_kex_packet_follows
  135. uint32 0 (reserved for future extension)
  136. */
  137. const init = {
  138. kex: undefined,
  139. serverHostKey: undefined,
  140. cs: {
  141. cipher: undefined,
  142. mac: undefined,
  143. compress: undefined,
  144. lang: undefined,
  145. },
  146. sc: {
  147. cipher: undefined,
  148. mac: undefined,
  149. compress: undefined,
  150. lang: undefined,
  151. },
  152. };
  153. bufferParser.init(payload, 17);
  154. if ((init.kex = bufferParser.readList()) === undefined
  155. || (init.serverHostKey = bufferParser.readList()) === undefined
  156. || (init.cs.cipher = bufferParser.readList()) === undefined
  157. || (init.sc.cipher = bufferParser.readList()) === undefined
  158. || (init.cs.mac = bufferParser.readList()) === undefined
  159. || (init.sc.mac = bufferParser.readList()) === undefined
  160. || (init.cs.compress = bufferParser.readList()) === undefined
  161. || (init.sc.compress = bufferParser.readList()) === undefined
  162. || (init.cs.lang = bufferParser.readList()) === undefined
  163. || (init.sc.lang = bufferParser.readList()) === undefined) {
  164. bufferParser.clear();
  165. return doFatalError(
  166. self,
  167. 'Received malformed KEXINIT',
  168. 'handshake',
  169. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  170. );
  171. }
  172. const pos = bufferParser.pos();
  173. const firstFollows = (pos < payload.length && payload[pos] === 1);
  174. bufferParser.clear();
  175. const local = self._offer;
  176. const remote = init;
  177. let localKex = local.lists.kex.array;
  178. if (self._compatFlags & COMPAT.BAD_DHGEX) {
  179. let found = false;
  180. for (let i = 0; i < localKex.length; ++i) {
  181. if (localKex[i].indexOf('group-exchange') !== -1) {
  182. if (!found) {
  183. found = true;
  184. // Copy array lazily
  185. localKex = localKex.slice();
  186. }
  187. localKex.splice(i--, 1);
  188. }
  189. }
  190. }
  191. let clientList;
  192. let serverList;
  193. let i;
  194. const debug = self._debug;
  195. debug && debug('Inbound: Handshake in progress');
  196. // Key exchange method =======================================================
  197. debug && debug(`Handshake: (local) KEX method: ${localKex}`);
  198. debug && debug(`Handshake: (remote) KEX method: ${remote.kex}`);
  199. if (self._server) {
  200. serverList = localKex;
  201. clientList = remote.kex;
  202. } else {
  203. serverList = remote.kex;
  204. clientList = localKex;
  205. }
  206. // Check for agreeable key exchange algorithm
  207. for (i = 0;
  208. i < clientList.length && serverList.indexOf(clientList[i]) === -1;
  209. ++i);
  210. if (i === clientList.length) {
  211. // No suitable match found!
  212. debug && debug('Handshake: No matching key exchange algorithm');
  213. return doFatalError(
  214. self,
  215. 'Handshake failed: no matching key exchange algorithm',
  216. 'handshake',
  217. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  218. );
  219. }
  220. init.kex = clientList[i];
  221. debug && debug(`Handshake: KEX algorithm: ${clientList[i]}`);
  222. if (firstFollows && (!remote.kex.length || clientList[i] !== remote.kex[0])) {
  223. // Ignore next inbound packet, it was a wrong first guess at KEX algorithm
  224. self._skipNextInboundPacket = true;
  225. }
  226. // Server host key format ====================================================
  227. const localSrvHostKey = local.lists.serverHostKey.array;
  228. debug && debug(`Handshake: (local) Host key format: ${localSrvHostKey}`);
  229. debug && debug(
  230. `Handshake: (remote) Host key format: ${remote.serverHostKey}`
  231. );
  232. if (self._server) {
  233. serverList = localSrvHostKey;
  234. clientList = remote.serverHostKey;
  235. } else {
  236. serverList = remote.serverHostKey;
  237. clientList = localSrvHostKey;
  238. }
  239. // Check for agreeable server host key format
  240. for (i = 0;
  241. i < clientList.length && serverList.indexOf(clientList[i]) === -1;
  242. ++i);
  243. if (i === clientList.length) {
  244. // No suitable match found!
  245. debug && debug('Handshake: No matching host key format');
  246. return doFatalError(
  247. self,
  248. 'Handshake failed: no matching host key format',
  249. 'handshake',
  250. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  251. );
  252. }
  253. init.serverHostKey = clientList[i];
  254. debug && debug(`Handshake: Host key format: ${clientList[i]}`);
  255. // Client->Server cipher =====================================================
  256. const localCSCipher = local.lists.cs.cipher.array;
  257. debug && debug(`Handshake: (local) C->S cipher: ${localCSCipher}`);
  258. debug && debug(`Handshake: (remote) C->S cipher: ${remote.cs.cipher}`);
  259. if (self._server) {
  260. serverList = localCSCipher;
  261. clientList = remote.cs.cipher;
  262. } else {
  263. serverList = remote.cs.cipher;
  264. clientList = localCSCipher;
  265. }
  266. // Check for agreeable client->server cipher
  267. for (i = 0;
  268. i < clientList.length && serverList.indexOf(clientList[i]) === -1;
  269. ++i);
  270. if (i === clientList.length) {
  271. // No suitable match found!
  272. debug && debug('Handshake: No matching C->S cipher');
  273. return doFatalError(
  274. self,
  275. 'Handshake failed: no matching C->S cipher',
  276. 'handshake',
  277. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  278. );
  279. }
  280. init.cs.cipher = clientList[i];
  281. debug && debug(`Handshake: C->S Cipher: ${clientList[i]}`);
  282. // Server->Client cipher =====================================================
  283. const localSCCipher = local.lists.sc.cipher.array;
  284. debug && debug(`Handshake: (local) S->C cipher: ${localSCCipher}`);
  285. debug && debug(`Handshake: (remote) S->C cipher: ${remote.sc.cipher}`);
  286. if (self._server) {
  287. serverList = localSCCipher;
  288. clientList = remote.sc.cipher;
  289. } else {
  290. serverList = remote.sc.cipher;
  291. clientList = localSCCipher;
  292. }
  293. // Check for agreeable server->client cipher
  294. for (i = 0;
  295. i < clientList.length && serverList.indexOf(clientList[i]) === -1;
  296. ++i);
  297. if (i === clientList.length) {
  298. // No suitable match found!
  299. debug && debug('Handshake: No matching S->C cipher');
  300. return doFatalError(
  301. self,
  302. 'Handshake failed: no matching S->C cipher',
  303. 'handshake',
  304. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  305. );
  306. }
  307. init.sc.cipher = clientList[i];
  308. debug && debug(`Handshake: S->C cipher: ${clientList[i]}`);
  309. // Client->Server MAC ========================================================
  310. const localCSMAC = local.lists.cs.mac.array;
  311. debug && debug(`Handshake: (local) C->S MAC: ${localCSMAC}`);
  312. debug && debug(`Handshake: (remote) C->S MAC: ${remote.cs.mac}`);
  313. if (CIPHER_INFO[init.cs.cipher].authLen > 0) {
  314. init.cs.mac = '';
  315. debug && debug('Handshake: C->S MAC: <implicit>');
  316. } else {
  317. if (self._server) {
  318. serverList = localCSMAC;
  319. clientList = remote.cs.mac;
  320. } else {
  321. serverList = remote.cs.mac;
  322. clientList = localCSMAC;
  323. }
  324. // Check for agreeable client->server hmac algorithm
  325. for (i = 0;
  326. i < clientList.length && serverList.indexOf(clientList[i]) === -1;
  327. ++i);
  328. if (i === clientList.length) {
  329. // No suitable match found!
  330. debug && debug('Handshake: No matching C->S MAC');
  331. return doFatalError(
  332. self,
  333. 'Handshake failed: no matching C->S MAC',
  334. 'handshake',
  335. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  336. );
  337. }
  338. init.cs.mac = clientList[i];
  339. debug && debug(`Handshake: C->S MAC: ${clientList[i]}`);
  340. }
  341. // Server->Client MAC ========================================================
  342. const localSCMAC = local.lists.sc.mac.array;
  343. debug && debug(`Handshake: (local) S->C MAC: ${localSCMAC}`);
  344. debug && debug(`Handshake: (remote) S->C MAC: ${remote.sc.mac}`);
  345. if (CIPHER_INFO[init.sc.cipher].authLen > 0) {
  346. init.sc.mac = '';
  347. debug && debug('Handshake: S->C MAC: <implicit>');
  348. } else {
  349. if (self._server) {
  350. serverList = localSCMAC;
  351. clientList = remote.sc.mac;
  352. } else {
  353. serverList = remote.sc.mac;
  354. clientList = localSCMAC;
  355. }
  356. // Check for agreeable server->client hmac algorithm
  357. for (i = 0;
  358. i < clientList.length && serverList.indexOf(clientList[i]) === -1;
  359. ++i);
  360. if (i === clientList.length) {
  361. // No suitable match found!
  362. debug && debug('Handshake: No matching S->C MAC');
  363. return doFatalError(
  364. self,
  365. 'Handshake failed: no matching S->C MAC',
  366. 'handshake',
  367. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  368. );
  369. }
  370. init.sc.mac = clientList[i];
  371. debug && debug(`Handshake: S->C MAC: ${clientList[i]}`);
  372. }
  373. // Client->Server compression ================================================
  374. const localCSCompress = local.lists.cs.compress.array;
  375. debug && debug(`Handshake: (local) C->S compression: ${localCSCompress}`);
  376. debug && debug(`Handshake: (remote) C->S compression: ${remote.cs.compress}`);
  377. if (self._server) {
  378. serverList = localCSCompress;
  379. clientList = remote.cs.compress;
  380. } else {
  381. serverList = remote.cs.compress;
  382. clientList = localCSCompress;
  383. }
  384. // Check for agreeable client->server compression algorithm
  385. for (i = 0;
  386. i < clientList.length && serverList.indexOf(clientList[i]) === -1;
  387. ++i);
  388. if (i === clientList.length) {
  389. // No suitable match found!
  390. debug && debug('Handshake: No matching C->S compression');
  391. return doFatalError(
  392. self,
  393. 'Handshake failed: no matching C->S compression',
  394. 'handshake',
  395. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  396. );
  397. }
  398. init.cs.compress = clientList[i];
  399. debug && debug(`Handshake: C->S compression: ${clientList[i]}`);
  400. // Server->Client compression ================================================
  401. const localSCCompress = local.lists.sc.compress.array;
  402. debug && debug(`Handshake: (local) S->C compression: ${localSCCompress}`);
  403. debug && debug(`Handshake: (remote) S->C compression: ${remote.sc.compress}`);
  404. if (self._server) {
  405. serverList = localSCCompress;
  406. clientList = remote.sc.compress;
  407. } else {
  408. serverList = remote.sc.compress;
  409. clientList = localSCCompress;
  410. }
  411. // Check for agreeable server->client compression algorithm
  412. for (i = 0;
  413. i < clientList.length && serverList.indexOf(clientList[i]) === -1;
  414. ++i);
  415. if (i === clientList.length) {
  416. // No suitable match found!
  417. debug && debug('Handshake: No matching S->C compression');
  418. return doFatalError(
  419. self,
  420. 'Handshake failed: no matching S->C compression',
  421. 'handshake',
  422. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  423. );
  424. }
  425. init.sc.compress = clientList[i];
  426. debug && debug(`Handshake: S->C compression: ${clientList[i]}`);
  427. init.cs.lang = '';
  428. init.sc.lang = '';
  429. // XXX: hack -- find a better way to do this
  430. if (self._kex) {
  431. if (!self._kexinit) {
  432. // We received a rekey request, but we haven't sent a KEXINIT in response
  433. // yet
  434. kexinit(self);
  435. }
  436. self._decipher._onPayload = onKEXPayload.bind(self, { firstPacket: false });
  437. }
  438. self._kex = createKeyExchange(init, self, payload);
  439. self._kex.start();
  440. }
  441. const createKeyExchange = (() => {
  442. function convertToMpint(buf) {
  443. let idx = 0;
  444. let length = buf.length;
  445. while (buf[idx] === 0x00) {
  446. ++idx;
  447. --length;
  448. }
  449. let newBuf;
  450. if (buf[idx] & 0x80) {
  451. newBuf = Buffer.allocUnsafe(1 + length);
  452. newBuf[0] = 0;
  453. buf.copy(newBuf, 1, idx);
  454. buf = newBuf;
  455. } else if (length !== buf.length) {
  456. newBuf = Buffer.allocUnsafe(length);
  457. buf.copy(newBuf, 0, idx);
  458. buf = newBuf;
  459. }
  460. return buf;
  461. }
  462. class KeyExchange {
  463. constructor(negotiated, protocol, remoteKexinit) {
  464. this._protocol = protocol;
  465. this.sessionID = (protocol._kex ? protocol._kex.sessionID : undefined);
  466. this.negotiated = negotiated;
  467. this._step = 1;
  468. this._public = null;
  469. this._dh = null;
  470. this._sentNEWKEYS = false;
  471. this._receivedNEWKEYS = false;
  472. this._finished = false;
  473. this._hostVerified = false;
  474. // Data needed for initializing cipher/decipher/etc.
  475. this._kexinit = protocol._kexinit;
  476. this._remoteKexinit = remoteKexinit;
  477. this._identRaw = protocol._identRaw;
  478. this._remoteIdentRaw = protocol._remoteIdentRaw;
  479. this._hostKey = undefined;
  480. this._dhData = undefined;
  481. this._sig = undefined;
  482. }
  483. finish() {
  484. if (this._finished)
  485. return false;
  486. this._finished = true;
  487. const isServer = this._protocol._server;
  488. const negotiated = this.negotiated;
  489. const pubKey = this.convertPublicKey(this._dhData);
  490. let secret = this.computeSecret(this._dhData);
  491. if (secret instanceof Error) {
  492. secret.message =
  493. `Error while computing DH secret (${this.type}): ${secret.message}`;
  494. secret.level = 'handshake';
  495. return doFatalError(
  496. this._protocol,
  497. secret,
  498. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  499. );
  500. }
  501. const hash = createHash(this.hashName);
  502. // V_C
  503. hashString(hash, (isServer ? this._remoteIdentRaw : this._identRaw));
  504. // "V_S"
  505. hashString(hash, (isServer ? this._identRaw : this._remoteIdentRaw));
  506. // "I_C"
  507. hashString(hash, (isServer ? this._remoteKexinit : this._kexinit));
  508. // "I_S"
  509. hashString(hash, (isServer ? this._kexinit : this._remoteKexinit));
  510. // "K_S"
  511. const serverPublicHostKey = (isServer
  512. ? this._hostKey.getPublicSSH()
  513. : this._hostKey);
  514. hashString(hash, serverPublicHostKey);
  515. if (this.type === 'groupex') {
  516. // Group exchange-specific
  517. const params = this.getDHParams();
  518. const num = Buffer.allocUnsafe(4);
  519. // min (uint32)
  520. writeUInt32BE(num, this._minBits, 0);
  521. hash.update(num);
  522. // preferred (uint32)
  523. writeUInt32BE(num, this._prefBits, 0);
  524. hash.update(num);
  525. // max (uint32)
  526. writeUInt32BE(num, this._maxBits, 0);
  527. hash.update(num);
  528. // prime
  529. hashString(hash, params.prime);
  530. // generator
  531. hashString(hash, params.generator);
  532. }
  533. // method-specific data sent by client
  534. hashString(hash, (isServer ? pubKey : this.getPublicKey()));
  535. // method-specific data sent by server
  536. const serverPublicKey = (isServer ? this.getPublicKey() : pubKey);
  537. hashString(hash, serverPublicKey);
  538. // shared secret ("K")
  539. hashString(hash, secret);
  540. // "H"
  541. const exchangeHash = hash.digest();
  542. if (!isServer) {
  543. bufferParser.init(this._sig, 0);
  544. const sigType = bufferParser.readString(true);
  545. if (!sigType) {
  546. return doFatalError(
  547. this._protocol,
  548. 'Malformed packet while reading signature',
  549. 'handshake',
  550. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  551. );
  552. }
  553. if (sigType !== negotiated.serverHostKey) {
  554. return doFatalError(
  555. this._protocol,
  556. `Wrong signature type: ${sigType}, `
  557. + `expected: ${negotiated.serverHostKey}`,
  558. 'handshake',
  559. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  560. );
  561. }
  562. // "s"
  563. let sigValue = bufferParser.readString();
  564. bufferParser.clear();
  565. if (sigValue === undefined) {
  566. return doFatalError(
  567. this._protocol,
  568. 'Malformed packet while reading signature',
  569. 'handshake',
  570. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  571. );
  572. }
  573. if (!(sigValue = sigSSHToASN1(sigValue, sigType))) {
  574. return doFatalError(
  575. this._protocol,
  576. 'Malformed signature',
  577. 'handshake',
  578. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  579. );
  580. }
  581. let parsedHostKey;
  582. {
  583. bufferParser.init(this._hostKey, 0);
  584. const name = bufferParser.readString(true);
  585. const hostKey = this._hostKey.slice(bufferParser.pos());
  586. bufferParser.clear();
  587. parsedHostKey = parseDERKey(hostKey, name);
  588. if (parsedHostKey instanceof Error) {
  589. parsedHostKey.level = 'handshake';
  590. return doFatalError(
  591. this._protocol,
  592. parsedHostKey,
  593. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  594. );
  595. }
  596. }
  597. let hashAlgo;
  598. // Check if we need to override the default hash algorithm
  599. switch (this.negotiated.serverHostKey) {
  600. case 'rsa-sha2-256': hashAlgo = 'sha256'; break;
  601. case 'rsa-sha2-512': hashAlgo = 'sha512'; break;
  602. }
  603. this._protocol._debug
  604. && this._protocol._debug('Verifying signature ...');
  605. const verified = parsedHostKey.verify(exchangeHash, sigValue, hashAlgo);
  606. if (verified !== true) {
  607. if (verified instanceof Error) {
  608. this._protocol._debug && this._protocol._debug(
  609. `Signature verification failed: ${verified.stack}`
  610. );
  611. } else {
  612. this._protocol._debug && this._protocol._debug(
  613. 'Signature verification failed'
  614. );
  615. }
  616. return doFatalError(
  617. this._protocol,
  618. 'Handshake failed: signature verification failed',
  619. 'handshake',
  620. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  621. );
  622. }
  623. this._protocol._debug && this._protocol._debug('Verified signature');
  624. } else {
  625. // Server
  626. let hashAlgo;
  627. // Check if we need to override the default hash algorithm
  628. switch (this.negotiated.serverHostKey) {
  629. case 'rsa-sha2-256': hashAlgo = 'sha256'; break;
  630. case 'rsa-sha2-512': hashAlgo = 'sha512'; break;
  631. }
  632. this._protocol._debug && this._protocol._debug(
  633. 'Generating signature ...'
  634. );
  635. let signature = this._hostKey.sign(exchangeHash, hashAlgo);
  636. if (signature instanceof Error) {
  637. return doFatalError(
  638. this._protocol,
  639. 'Handshake failed: signature generation failed for '
  640. + `${this._hostKey.type} host key: ${signature.message}`,
  641. 'handshake',
  642. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  643. );
  644. }
  645. signature = convertSignature(signature, this._hostKey.type);
  646. if (signature === false) {
  647. return doFatalError(
  648. this._protocol,
  649. 'Handshake failed: signature conversion failed for '
  650. + `${this._hostKey.type} host key`,
  651. 'handshake',
  652. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  653. );
  654. }
  655. // Send KEX reply
  656. /*
  657. byte SSH_MSG_KEXDH_REPLY
  658. / SSH_MSG_KEX_DH_GEX_REPLY
  659. / SSH_MSG_KEX_ECDH_REPLY
  660. string server public host key and certificates (K_S)
  661. string <method-specific data>
  662. string signature of H
  663. */
  664. const sigType = this.negotiated.serverHostKey;
  665. const sigTypeLen = Buffer.byteLength(sigType);
  666. const sigLen = 4 + sigTypeLen + 4 + signature.length;
  667. let p = this._protocol._packetRW.write.allocStartKEX;
  668. const packet = this._protocol._packetRW.write.alloc(
  669. 1
  670. + 4 + serverPublicHostKey.length
  671. + 4 + serverPublicKey.length
  672. + 4 + sigLen,
  673. true
  674. );
  675. packet[p] = MESSAGE.KEXDH_REPLY;
  676. writeUInt32BE(packet, serverPublicHostKey.length, ++p);
  677. packet.set(serverPublicHostKey, p += 4);
  678. writeUInt32BE(packet,
  679. serverPublicKey.length,
  680. p += serverPublicHostKey.length);
  681. packet.set(serverPublicKey, p += 4);
  682. writeUInt32BE(packet, sigLen, p += serverPublicKey.length);
  683. writeUInt32BE(packet, sigTypeLen, p += 4);
  684. packet.utf8Write(sigType, p += 4, sigTypeLen);
  685. writeUInt32BE(packet, signature.length, p += sigTypeLen);
  686. packet.set(signature, p += 4);
  687. if (this._protocol._debug) {
  688. let type;
  689. switch (this.type) {
  690. case 'group':
  691. type = 'KEXDH_REPLY';
  692. break;
  693. case 'groupex':
  694. type = 'KEXDH_GEX_REPLY';
  695. break;
  696. default:
  697. type = 'KEXECDH_REPLY';
  698. }
  699. this._protocol._debug(`Outbound: Sending ${type}`);
  700. }
  701. this._protocol._cipher.encrypt(
  702. this._protocol._packetRW.write.finalize(packet, true)
  703. );
  704. }
  705. trySendNEWKEYS(this);
  706. const completeHandshake = () => {
  707. if (!this.sessionID)
  708. this.sessionID = exchangeHash;
  709. {
  710. const newSecret = Buffer.allocUnsafe(4 + secret.length);
  711. writeUInt32BE(newSecret, secret.length, 0);
  712. newSecret.set(secret, 4);
  713. secret = newSecret;
  714. }
  715. // Initialize new ciphers, deciphers, etc.
  716. const csCipherInfo = CIPHER_INFO[negotiated.cs.cipher];
  717. const scCipherInfo = CIPHER_INFO[negotiated.sc.cipher];
  718. const csIV = generateKEXVal(csCipherInfo.ivLen,
  719. this.hashName,
  720. secret,
  721. exchangeHash,
  722. this.sessionID,
  723. 'A');
  724. const scIV = generateKEXVal(scCipherInfo.ivLen,
  725. this.hashName,
  726. secret,
  727. exchangeHash,
  728. this.sessionID,
  729. 'B');
  730. const csKey = generateKEXVal(csCipherInfo.keyLen,
  731. this.hashName,
  732. secret,
  733. exchangeHash,
  734. this.sessionID,
  735. 'C');
  736. const scKey = generateKEXVal(scCipherInfo.keyLen,
  737. this.hashName,
  738. secret,
  739. exchangeHash,
  740. this.sessionID,
  741. 'D');
  742. let csMacInfo;
  743. let csMacKey;
  744. if (!csCipherInfo.authLen) {
  745. csMacInfo = MAC_INFO[negotiated.cs.mac];
  746. csMacKey = generateKEXVal(csMacInfo.len,
  747. this.hashName,
  748. secret,
  749. exchangeHash,
  750. this.sessionID,
  751. 'E');
  752. }
  753. let scMacInfo;
  754. let scMacKey;
  755. if (!scCipherInfo.authLen) {
  756. scMacInfo = MAC_INFO[negotiated.sc.mac];
  757. scMacKey = generateKEXVal(scMacInfo.len,
  758. this.hashName,
  759. secret,
  760. exchangeHash,
  761. this.sessionID,
  762. 'F');
  763. }
  764. const config = {
  765. inbound: {
  766. onPayload: this._protocol._onPayload,
  767. seqno: this._protocol._decipher.inSeqno,
  768. decipherInfo: (!isServer ? scCipherInfo : csCipherInfo),
  769. decipherIV: (!isServer ? scIV : csIV),
  770. decipherKey: (!isServer ? scKey : csKey),
  771. macInfo: (!isServer ? scMacInfo : csMacInfo),
  772. macKey: (!isServer ? scMacKey : csMacKey),
  773. },
  774. outbound: {
  775. onWrite: this._protocol._onWrite,
  776. seqno: this._protocol._cipher.outSeqno,
  777. cipherInfo: (isServer ? scCipherInfo : csCipherInfo),
  778. cipherIV: (isServer ? scIV : csIV),
  779. cipherKey: (isServer ? scKey : csKey),
  780. macInfo: (isServer ? scMacInfo : csMacInfo),
  781. macKey: (isServer ? scMacKey : csMacKey),
  782. },
  783. };
  784. this._protocol._cipher && this._protocol._cipher.free();
  785. this._protocol._decipher && this._protocol._decipher.free();
  786. this._protocol._cipher = createCipher(config);
  787. this._protocol._decipher = createDecipher(config);
  788. const rw = {
  789. read: undefined,
  790. write: undefined,
  791. };
  792. switch (negotiated.cs.compress) {
  793. case 'zlib': // starts immediately
  794. if (isServer)
  795. rw.read = new ZlibPacketReader();
  796. else
  797. rw.write = new ZlibPacketWriter(this._protocol);
  798. break;
  799. case 'zlib@openssh.com':
  800. // Starts after successful user authentication
  801. if (this._protocol._authenticated) {
  802. // If a rekey happens and this compression method is selected and
  803. // we already authenticated successfully, we need to start
  804. // immediately instead
  805. if (isServer)
  806. rw.read = new ZlibPacketReader();
  807. else
  808. rw.write = new ZlibPacketWriter(this._protocol);
  809. break;
  810. }
  811. // FALLTHROUGH
  812. default:
  813. // none -- never any compression/decompression
  814. if (isServer)
  815. rw.read = new PacketReader();
  816. else
  817. rw.write = new PacketWriter(this._protocol);
  818. }
  819. switch (negotiated.sc.compress) {
  820. case 'zlib': // starts immediately
  821. if (isServer)
  822. rw.write = new ZlibPacketWriter(this._protocol);
  823. else
  824. rw.read = new ZlibPacketReader();
  825. break;
  826. case 'zlib@openssh.com':
  827. // Starts after successful user authentication
  828. if (this._protocol._authenticated) {
  829. // If a rekey happens and this compression method is selected and
  830. // we already authenticated successfully, we need to start
  831. // immediately instead
  832. if (isServer)
  833. rw.write = new ZlibPacketWriter(this._protocol);
  834. else
  835. rw.read = new ZlibPacketReader();
  836. break;
  837. }
  838. // FALLTHROUGH
  839. default:
  840. // none -- never any compression/decompression
  841. if (isServer)
  842. rw.write = new PacketWriter(this._protocol);
  843. else
  844. rw.read = new PacketReader();
  845. }
  846. this._protocol._packetRW.read.cleanup();
  847. this._protocol._packetRW.write.cleanup();
  848. this._protocol._packetRW = rw;
  849. // Cleanup/reset various state
  850. this._public = null;
  851. this._dh = null;
  852. this._kexinit = this._protocol._kexinit = undefined;
  853. this._remoteKexinit = undefined;
  854. this._identRaw = undefined;
  855. this._remoteIdentRaw = undefined;
  856. this._hostKey = undefined;
  857. this._dhData = undefined;
  858. this._sig = undefined;
  859. this._protocol._onHandshakeComplete(negotiated);
  860. return false;
  861. };
  862. if (!isServer)
  863. return completeHandshake();
  864. this.finish = completeHandshake;
  865. }
  866. start() {
  867. if (!this._protocol._server) {
  868. if (this._protocol._debug) {
  869. let type;
  870. switch (this.type) {
  871. case 'group':
  872. type = 'KEXDH_INIT';
  873. break;
  874. default:
  875. type = 'KEXECDH_INIT';
  876. }
  877. this._protocol._debug(`Outbound: Sending ${type}`);
  878. }
  879. const pubKey = this.getPublicKey();
  880. let p = this._protocol._packetRW.write.allocStartKEX;
  881. const packet = this._protocol._packetRW.write.alloc(
  882. 1 + 4 + pubKey.length,
  883. true
  884. );
  885. packet[p] = MESSAGE.KEXDH_INIT;
  886. writeUInt32BE(packet, pubKey.length, ++p);
  887. packet.set(pubKey, p += 4);
  888. this._protocol._cipher.encrypt(
  889. this._protocol._packetRW.write.finalize(packet, true)
  890. );
  891. }
  892. }
  893. getPublicKey() {
  894. this.generateKeys();
  895. const key = this._public;
  896. if (key)
  897. return this.convertPublicKey(key);
  898. }
  899. convertPublicKey(key) {
  900. let newKey;
  901. let idx = 0;
  902. let len = key.length;
  903. while (key[idx] === 0x00) {
  904. ++idx;
  905. --len;
  906. }
  907. if (key[idx] & 0x80) {
  908. newKey = Buffer.allocUnsafe(1 + len);
  909. newKey[0] = 0;
  910. key.copy(newKey, 1, idx);
  911. return newKey;
  912. }
  913. if (len !== key.length) {
  914. newKey = Buffer.allocUnsafe(len);
  915. key.copy(newKey, 0, idx);
  916. key = newKey;
  917. }
  918. return key;
  919. }
  920. computeSecret(otherPublicKey) {
  921. this.generateKeys();
  922. try {
  923. return convertToMpint(this._dh.computeSecret(otherPublicKey));
  924. } catch (ex) {
  925. return ex;
  926. }
  927. }
  928. parse(payload) {
  929. const type = payload[0];
  930. switch (this._step) {
  931. case 1:
  932. if (this._protocol._server) {
  933. // Server
  934. if (type !== MESSAGE.KEXDH_INIT) {
  935. return doFatalError(
  936. this._protocol,
  937. `Received packet ${type} instead of ${MESSAGE.KEXDH_INIT}`,
  938. 'handshake',
  939. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  940. );
  941. }
  942. this._protocol._debug && this._protocol._debug(
  943. 'Received DH Init'
  944. );
  945. /*
  946. byte SSH_MSG_KEXDH_INIT
  947. / SSH_MSG_KEX_ECDH_INIT
  948. string <method-specific data>
  949. */
  950. bufferParser.init(payload, 1);
  951. const dhData = bufferParser.readString();
  952. bufferParser.clear();
  953. if (dhData === undefined) {
  954. return doFatalError(
  955. this._protocol,
  956. 'Received malformed KEX*_INIT',
  957. 'handshake',
  958. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  959. );
  960. }
  961. // Client public key
  962. this._dhData = dhData;
  963. let hostKey =
  964. this._protocol._hostKeys[this.negotiated.serverHostKey];
  965. if (Array.isArray(hostKey))
  966. hostKey = hostKey[0];
  967. this._hostKey = hostKey;
  968. this.finish();
  969. } else {
  970. // Client
  971. if (type !== MESSAGE.KEXDH_REPLY) {
  972. return doFatalError(
  973. this._protocol,
  974. `Received packet ${type} instead of ${MESSAGE.KEXDH_REPLY}`,
  975. 'handshake',
  976. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  977. );
  978. }
  979. this._protocol._debug && this._protocol._debug(
  980. 'Received DH Reply'
  981. );
  982. /*
  983. byte SSH_MSG_KEXDH_REPLY
  984. / SSH_MSG_KEX_DH_GEX_REPLY
  985. / SSH_MSG_KEX_ECDH_REPLY
  986. string server public host key and certificates (K_S)
  987. string <method-specific data>
  988. string signature of H
  989. */
  990. bufferParser.init(payload, 1);
  991. let hostPubKey;
  992. let dhData;
  993. let sig;
  994. if ((hostPubKey = bufferParser.readString()) === undefined
  995. || (dhData = bufferParser.readString()) === undefined
  996. || (sig = bufferParser.readString()) === undefined) {
  997. bufferParser.clear();
  998. return doFatalError(
  999. this._protocol,
  1000. 'Received malformed KEX*_REPLY',
  1001. 'handshake',
  1002. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1003. );
  1004. }
  1005. bufferParser.clear();
  1006. // Check that the host public key type matches what was negotiated
  1007. // during KEXINIT swap
  1008. bufferParser.init(hostPubKey, 0);
  1009. const hostPubKeyType = bufferParser.readString(true);
  1010. bufferParser.clear();
  1011. if (hostPubKeyType === undefined) {
  1012. return doFatalError(
  1013. this._protocol,
  1014. 'Received malformed host public key',
  1015. 'handshake',
  1016. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1017. );
  1018. }
  1019. if (hostPubKeyType !== this.negotiated.serverHostKey) {
  1020. // Check if we need to make an exception
  1021. switch (this.negotiated.serverHostKey) {
  1022. case 'rsa-sha2-256':
  1023. case 'rsa-sha2-512':
  1024. if (hostPubKeyType === 'ssh-rsa')
  1025. break;
  1026. // FALLTHROUGH
  1027. default:
  1028. return doFatalError(
  1029. this._protocol,
  1030. 'Host key does not match negotiated type',
  1031. 'handshake',
  1032. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1033. );
  1034. }
  1035. }
  1036. this._hostKey = hostPubKey;
  1037. this._dhData = dhData;
  1038. this._sig = sig;
  1039. let checked = false;
  1040. let ret;
  1041. if (this._protocol._hostVerifier === undefined) {
  1042. ret = true;
  1043. this._protocol._debug && this._protocol._debug(
  1044. 'Host accepted by default (no verification)'
  1045. );
  1046. } else {
  1047. ret = this._protocol._hostVerifier(hostPubKey, (permitted) => {
  1048. if (checked)
  1049. return;
  1050. checked = true;
  1051. if (permitted === false) {
  1052. this._protocol._debug && this._protocol._debug(
  1053. 'Host denied (verification failed)'
  1054. );
  1055. return doFatalError(
  1056. this._protocol,
  1057. 'Host denied (verification failed)',
  1058. 'handshake',
  1059. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1060. );
  1061. }
  1062. this._protocol._debug && this._protocol._debug(
  1063. 'Host accepted (verified)'
  1064. );
  1065. this._hostVerified = true;
  1066. if (this._receivedNEWKEYS)
  1067. this.finish();
  1068. else
  1069. trySendNEWKEYS(this);
  1070. });
  1071. }
  1072. if (ret === undefined) {
  1073. // Async host verification
  1074. ++this._step;
  1075. return;
  1076. }
  1077. checked = true;
  1078. if (ret === false) {
  1079. this._protocol._debug && this._protocol._debug(
  1080. 'Host denied (verification failed)'
  1081. );
  1082. return doFatalError(
  1083. this._protocol,
  1084. 'Host denied (verification failed)',
  1085. 'handshake',
  1086. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1087. );
  1088. }
  1089. this._protocol._debug && this._protocol._debug(
  1090. 'Host accepted (verified)'
  1091. );
  1092. this._hostVerified = true;
  1093. trySendNEWKEYS(this);
  1094. }
  1095. ++this._step;
  1096. break;
  1097. case 2:
  1098. if (type !== MESSAGE.NEWKEYS) {
  1099. return doFatalError(
  1100. this._protocol,
  1101. `Received packet ${type} instead of ${MESSAGE.NEWKEYS}`,
  1102. 'handshake',
  1103. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1104. );
  1105. }
  1106. this._protocol._debug && this._protocol._debug(
  1107. 'Inbound: NEWKEYS'
  1108. );
  1109. this._receivedNEWKEYS = true;
  1110. ++this._step;
  1111. if (this._protocol._server || this._hostVerified)
  1112. return this.finish();
  1113. // Signal to current decipher that we need to change to a new decipher
  1114. // for the next packet
  1115. return false;
  1116. default:
  1117. return doFatalError(
  1118. this._protocol,
  1119. `Received unexpected packet ${type} after NEWKEYS`,
  1120. 'handshake',
  1121. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1122. );
  1123. }
  1124. }
  1125. }
  1126. class Curve25519Exchange extends KeyExchange {
  1127. constructor(hashName, ...args) {
  1128. super(...args);
  1129. this.type = '25519';
  1130. this.hashName = hashName;
  1131. this._keys = null;
  1132. }
  1133. generateKeys() {
  1134. if (!this._keys)
  1135. this._keys = generateKeyPairSync('x25519');
  1136. }
  1137. getPublicKey() {
  1138. this.generateKeys();
  1139. const key = this._keys.publicKey.export({ type: 'spki', format: 'der' });
  1140. return key.slice(-32); // HACK: avoids parsing DER/BER header
  1141. }
  1142. convertPublicKey(key) {
  1143. let newKey;
  1144. let idx = 0;
  1145. let len = key.length;
  1146. while (key[idx] === 0x00) {
  1147. ++idx;
  1148. --len;
  1149. }
  1150. if (key.length === 32)
  1151. return key;
  1152. if (len !== key.length) {
  1153. newKey = Buffer.allocUnsafe(len);
  1154. key.copy(newKey, 0, idx);
  1155. key = newKey;
  1156. }
  1157. return key;
  1158. }
  1159. computeSecret(otherPublicKey) {
  1160. this.generateKeys();
  1161. try {
  1162. const asnWriter = new Ber.Writer();
  1163. asnWriter.startSequence();
  1164. // algorithm
  1165. asnWriter.startSequence();
  1166. asnWriter.writeOID('1.3.101.110'); // id-X25519
  1167. asnWriter.endSequence();
  1168. // PublicKey
  1169. asnWriter.startSequence(Ber.BitString);
  1170. asnWriter.writeByte(0x00);
  1171. // XXX: hack to write a raw buffer without a tag -- yuck
  1172. asnWriter._ensure(otherPublicKey.length);
  1173. otherPublicKey.copy(asnWriter._buf,
  1174. asnWriter._offset,
  1175. 0,
  1176. otherPublicKey.length);
  1177. asnWriter._offset += otherPublicKey.length;
  1178. asnWriter.endSequence();
  1179. asnWriter.endSequence();
  1180. return convertToMpint(diffieHellman({
  1181. privateKey: this._keys.privateKey,
  1182. publicKey: createPublicKey({
  1183. key: asnWriter.buffer,
  1184. type: 'spki',
  1185. format: 'der',
  1186. }),
  1187. }));
  1188. } catch (ex) {
  1189. return ex;
  1190. }
  1191. }
  1192. }
  1193. class ECDHExchange extends KeyExchange {
  1194. constructor(curveName, hashName, ...args) {
  1195. super(...args);
  1196. this.type = 'ecdh';
  1197. this.curveName = curveName;
  1198. this.hashName = hashName;
  1199. }
  1200. generateKeys() {
  1201. if (!this._dh) {
  1202. this._dh = createECDH(this.curveName);
  1203. this._public = this._dh.generateKeys();
  1204. }
  1205. }
  1206. }
  1207. class DHGroupExchange extends KeyExchange {
  1208. constructor(hashName, ...args) {
  1209. super(...args);
  1210. this.type = 'groupex';
  1211. this.hashName = hashName;
  1212. this._prime = null;
  1213. this._generator = null;
  1214. this._minBits = GEX_MIN_BITS;
  1215. this._prefBits = dhEstimate(this.negotiated);
  1216. if (this._protocol._compatFlags & COMPAT.BUG_DHGEX_LARGE)
  1217. this._prefBits = Math.min(this._prefBits, 4096);
  1218. this._maxBits = GEX_MAX_BITS;
  1219. }
  1220. start() {
  1221. if (this._protocol._server)
  1222. return;
  1223. this._protocol._debug && this._protocol._debug(
  1224. 'Outbound: Sending KEXDH_GEX_REQUEST'
  1225. );
  1226. let p = this._protocol._packetRW.write.allocStartKEX;
  1227. const packet = this._protocol._packetRW.write.alloc(
  1228. 1 + 4 + 4 + 4,
  1229. true
  1230. );
  1231. packet[p] = MESSAGE.KEXDH_GEX_REQUEST;
  1232. writeUInt32BE(packet, this._minBits, ++p);
  1233. writeUInt32BE(packet, this._prefBits, p += 4);
  1234. writeUInt32BE(packet, this._maxBits, p += 4);
  1235. this._protocol._cipher.encrypt(
  1236. this._protocol._packetRW.write.finalize(packet, true)
  1237. );
  1238. }
  1239. generateKeys() {
  1240. if (!this._dh && this._prime && this._generator) {
  1241. this._dh = createDiffieHellman(this._prime, this._generator);
  1242. this._public = this._dh.generateKeys();
  1243. }
  1244. }
  1245. setDHParams(prime, generator) {
  1246. if (!Buffer.isBuffer(prime))
  1247. throw new Error('Invalid prime value');
  1248. if (!Buffer.isBuffer(generator))
  1249. throw new Error('Invalid generator value');
  1250. this._prime = prime;
  1251. this._generator = generator;
  1252. }
  1253. getDHParams() {
  1254. if (this._dh) {
  1255. return {
  1256. prime: convertToMpint(this._dh.getPrime()),
  1257. generator: convertToMpint(this._dh.getGenerator()),
  1258. };
  1259. }
  1260. }
  1261. parse(payload) {
  1262. const type = payload[0];
  1263. switch (this._step) {
  1264. case 1:
  1265. if (this._protocol._server) {
  1266. if (type !== MESSAGE.KEXDH_GEX_REQUEST) {
  1267. return doFatalError(
  1268. this._protocol,
  1269. `Received packet ${type} instead of `
  1270. + MESSAGE.KEXDH_GEX_REQUEST,
  1271. 'handshake',
  1272. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1273. );
  1274. }
  1275. // TODO: allow user implementation to provide safe prime and
  1276. // generator on demand to support group exchange on server side
  1277. return doFatalError(
  1278. this._protocol,
  1279. 'Group exchange not implemented for server',
  1280. 'handshake',
  1281. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1282. );
  1283. }
  1284. if (type !== MESSAGE.KEXDH_GEX_GROUP) {
  1285. return doFatalError(
  1286. this._protocol,
  1287. `Received packet ${type} instead of ${MESSAGE.KEXDH_GEX_GROUP}`,
  1288. 'handshake',
  1289. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1290. );
  1291. }
  1292. this._protocol._debug && this._protocol._debug(
  1293. 'Received DH GEX Group'
  1294. );
  1295. /*
  1296. byte SSH_MSG_KEX_DH_GEX_GROUP
  1297. mpint p, safe prime
  1298. mpint g, generator for subgroup in GF(p)
  1299. */
  1300. bufferParser.init(payload, 1);
  1301. let prime;
  1302. let gen;
  1303. if ((prime = bufferParser.readString()) === undefined
  1304. || (gen = bufferParser.readString()) === undefined) {
  1305. bufferParser.clear();
  1306. return doFatalError(
  1307. this._protocol,
  1308. 'Received malformed KEXDH_GEX_GROUP',
  1309. 'handshake',
  1310. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1311. );
  1312. }
  1313. bufferParser.clear();
  1314. // TODO: validate prime
  1315. this.setDHParams(prime, gen);
  1316. this.generateKeys();
  1317. const pubkey = this.getPublicKey();
  1318. this._protocol._debug && this._protocol._debug(
  1319. 'Outbound: Sending KEXDH_GEX_INIT'
  1320. );
  1321. let p = this._protocol._packetRW.write.allocStartKEX;
  1322. const packet =
  1323. this._protocol._packetRW.write.alloc(1 + 4 + pubkey.length, true);
  1324. packet[p] = MESSAGE.KEXDH_GEX_INIT;
  1325. writeUInt32BE(packet, pubkey.length, ++p);
  1326. packet.set(pubkey, p += 4);
  1327. this._protocol._cipher.encrypt(
  1328. this._protocol._packetRW.write.finalize(packet, true)
  1329. );
  1330. ++this._step;
  1331. break;
  1332. case 2:
  1333. if (this._protocol._server) {
  1334. if (type !== MESSAGE.KEXDH_GEX_INIT) {
  1335. return doFatalError(
  1336. this._protocol,
  1337. `Received packet ${type} instead of ${MESSAGE.KEXDH_GEX_INIT}`,
  1338. 'handshake',
  1339. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1340. );
  1341. }
  1342. this._protocol._debug && this._protocol._debug(
  1343. 'Received DH GEX Init'
  1344. );
  1345. return doFatalError(
  1346. this._protocol,
  1347. 'Group exchange not implemented for server',
  1348. 'handshake',
  1349. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1350. );
  1351. } else if (type !== MESSAGE.KEXDH_GEX_REPLY) {
  1352. return doFatalError(
  1353. this._protocol,
  1354. `Received packet ${type} instead of ${MESSAGE.KEXDH_GEX_REPLY}`,
  1355. 'handshake',
  1356. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1357. );
  1358. }
  1359. this._protocol._debug && this._protocol._debug(
  1360. 'Received DH GEX Reply'
  1361. );
  1362. this._step = 1;
  1363. payload[0] = MESSAGE.KEXDH_REPLY;
  1364. this.parse = KeyExchange.prototype.parse;
  1365. this.parse(payload);
  1366. }
  1367. }
  1368. }
  1369. class DHExchange extends KeyExchange {
  1370. constructor(groupName, hashName, ...args) {
  1371. super(...args);
  1372. this.type = 'group';
  1373. this.groupName = groupName;
  1374. this.hashName = hashName;
  1375. }
  1376. start() {
  1377. if (!this._protocol._server) {
  1378. this._protocol._debug && this._protocol._debug(
  1379. 'Outbound: Sending KEXDH_INIT'
  1380. );
  1381. const pubKey = this.getPublicKey();
  1382. let p = this._protocol._packetRW.write.allocStartKEX;
  1383. const packet =
  1384. this._protocol._packetRW.write.alloc(1 + 4 + pubKey.length, true);
  1385. packet[p] = MESSAGE.KEXDH_INIT;
  1386. writeUInt32BE(packet, pubKey.length, ++p);
  1387. packet.set(pubKey, p += 4);
  1388. this._protocol._cipher.encrypt(
  1389. this._protocol._packetRW.write.finalize(packet, true)
  1390. );
  1391. }
  1392. }
  1393. generateKeys() {
  1394. if (!this._dh) {
  1395. this._dh = createDiffieHellmanGroup(this.groupName);
  1396. this._public = this._dh.generateKeys();
  1397. }
  1398. }
  1399. getDHParams() {
  1400. if (this._dh) {
  1401. return {
  1402. prime: convertToMpint(this._dh.getPrime()),
  1403. generator: convertToMpint(this._dh.getGenerator()),
  1404. };
  1405. }
  1406. }
  1407. }
  1408. return (negotiated, ...args) => {
  1409. if (typeof negotiated !== 'object' || negotiated === null)
  1410. throw new Error('Invalid negotiated argument');
  1411. const kexType = negotiated.kex;
  1412. if (typeof kexType === 'string') {
  1413. args = [negotiated, ...args];
  1414. switch (kexType) {
  1415. case 'curve25519-sha256':
  1416. case 'curve25519-sha256@libssh.org':
  1417. if (!curve25519Supported)
  1418. break;
  1419. return new Curve25519Exchange('sha256', ...args);
  1420. case 'ecdh-sha2-nistp256':
  1421. return new ECDHExchange('prime256v1', 'sha256', ...args);
  1422. case 'ecdh-sha2-nistp384':
  1423. return new ECDHExchange('secp384r1', 'sha384', ...args);
  1424. case 'ecdh-sha2-nistp521':
  1425. return new ECDHExchange('secp521r1', 'sha512', ...args);
  1426. case 'diffie-hellman-group1-sha1':
  1427. return new DHExchange('modp2', 'sha1', ...args);
  1428. case 'diffie-hellman-group14-sha1':
  1429. return new DHExchange('modp14', 'sha1', ...args);
  1430. case 'diffie-hellman-group14-sha256':
  1431. return new DHExchange('modp14', 'sha256', ...args);
  1432. case 'diffie-hellman-group15-sha512':
  1433. return new DHExchange('modp15', 'sha512', ...args);
  1434. case 'diffie-hellman-group16-sha512':
  1435. return new DHExchange('modp16', 'sha512', ...args);
  1436. case 'diffie-hellman-group17-sha512':
  1437. return new DHExchange('modp17', 'sha512', ...args);
  1438. case 'diffie-hellman-group18-sha512':
  1439. return new DHExchange('modp18', 'sha512', ...args);
  1440. case 'diffie-hellman-group-exchange-sha1':
  1441. return new DHGroupExchange('sha1', ...args);
  1442. case 'diffie-hellman-group-exchange-sha256':
  1443. return new DHGroupExchange('sha256', ...args);
  1444. }
  1445. throw new Error(`Unsupported key exchange algorithm: ${kexType}`);
  1446. }
  1447. throw new Error(`Invalid key exchange type: ${kexType}`);
  1448. };
  1449. })();
  1450. const KexInit = (() => {
  1451. const KEX_PROPERTY_NAMES = [
  1452. 'kex',
  1453. 'serverHostKey',
  1454. ['cs', 'cipher' ],
  1455. ['sc', 'cipher' ],
  1456. ['cs', 'mac' ],
  1457. ['sc', 'mac' ],
  1458. ['cs', 'compress' ],
  1459. ['sc', 'compress' ],
  1460. ['cs', 'lang' ],
  1461. ['sc', 'lang' ],
  1462. ];
  1463. return class KexInit {
  1464. constructor(obj) {
  1465. if (typeof obj !== 'object' || obj === null)
  1466. throw new TypeError('Argument must be an object');
  1467. const lists = {
  1468. kex: undefined,
  1469. serverHostKey: undefined,
  1470. cs: {
  1471. cipher: undefined,
  1472. mac: undefined,
  1473. compress: undefined,
  1474. lang: undefined,
  1475. },
  1476. sc: {
  1477. cipher: undefined,
  1478. mac: undefined,
  1479. compress: undefined,
  1480. lang: undefined,
  1481. },
  1482. all: undefined,
  1483. };
  1484. let totalSize = 0;
  1485. for (const prop of KEX_PROPERTY_NAMES) {
  1486. let base;
  1487. let val;
  1488. let desc;
  1489. let key;
  1490. if (typeof prop === 'string') {
  1491. base = lists;
  1492. val = obj[prop];
  1493. desc = key = prop;
  1494. } else {
  1495. const parent = prop[0];
  1496. base = lists[parent];
  1497. key = prop[1];
  1498. val = obj[parent][key];
  1499. desc = `${parent}.${key}`;
  1500. }
  1501. const entry = { array: undefined, buffer: undefined };
  1502. if (Buffer.isBuffer(val)) {
  1503. entry.array = ('' + val).split(',');
  1504. entry.buffer = val;
  1505. totalSize += 4 + val.length;
  1506. } else {
  1507. if (typeof val === 'string')
  1508. val = val.split(',');
  1509. if (Array.isArray(val)) {
  1510. entry.array = val;
  1511. entry.buffer = Buffer.from(val.join(','));
  1512. } else {
  1513. throw new TypeError(`Invalid \`${desc}\` type: ${typeof val}`);
  1514. }
  1515. totalSize += 4 + entry.buffer.length;
  1516. }
  1517. base[key] = entry;
  1518. }
  1519. const all = Buffer.allocUnsafe(totalSize);
  1520. lists.all = all;
  1521. let allPos = 0;
  1522. for (const prop of KEX_PROPERTY_NAMES) {
  1523. let data;
  1524. if (typeof prop === 'string')
  1525. data = lists[prop].buffer;
  1526. else
  1527. data = lists[prop[0]][prop[1]].buffer;
  1528. allPos = writeUInt32BE(all, data.length, allPos);
  1529. all.set(data, allPos);
  1530. allPos += data.length;
  1531. }
  1532. this.totalSize = totalSize;
  1533. this.lists = lists;
  1534. }
  1535. copyAllTo(buf, offset) {
  1536. const src = this.lists.all;
  1537. if (typeof offset !== 'number')
  1538. throw new TypeError(`Invalid offset value: ${typeof offset}`);
  1539. if (buf.length - offset < src.length)
  1540. throw new Error('Insufficient space to copy list');
  1541. buf.set(src, offset);
  1542. return src.length;
  1543. }
  1544. };
  1545. })();
  1546. const hashString = (() => {
  1547. const LEN = Buffer.allocUnsafe(4);
  1548. return (hash, buf) => {
  1549. writeUInt32BE(LEN, buf.length, 0);
  1550. hash.update(LEN);
  1551. hash.update(buf);
  1552. };
  1553. })();
  1554. function generateKEXVal(len, hashName, secret, exchangeHash, sessionID, char) {
  1555. let ret;
  1556. if (len) {
  1557. let digest = createHash(hashName)
  1558. .update(secret)
  1559. .update(exchangeHash)
  1560. .update(char)
  1561. .update(sessionID)
  1562. .digest();
  1563. while (digest.length < len) {
  1564. const chunk = createHash(hashName)
  1565. .update(secret)
  1566. .update(exchangeHash)
  1567. .update(digest)
  1568. .digest();
  1569. const extended = Buffer.allocUnsafe(digest.length + chunk.length);
  1570. extended.set(digest, 0);
  1571. extended.set(chunk, digest.length);
  1572. digest = extended;
  1573. }
  1574. if (digest.length === len)
  1575. ret = digest;
  1576. else
  1577. ret = new FastBuffer(digest.buffer, digest.byteOffset, len);
  1578. } else {
  1579. ret = EMPTY_BUFFER;
  1580. }
  1581. return ret;
  1582. }
  1583. function onKEXPayload(state, payload) {
  1584. // XXX: move this to the Decipher implementations?
  1585. if (payload.length === 0) {
  1586. this._debug && this._debug('Inbound: Skipping empty packet payload');
  1587. return;
  1588. }
  1589. if (this._skipNextInboundPacket) {
  1590. this._skipNextInboundPacket = false;
  1591. return;
  1592. }
  1593. payload = this._packetRW.read.read(payload);
  1594. const type = payload[0];
  1595. switch (type) {
  1596. case MESSAGE.DISCONNECT:
  1597. case MESSAGE.IGNORE:
  1598. case MESSAGE.UNIMPLEMENTED:
  1599. case MESSAGE.DEBUG:
  1600. if (!MESSAGE_HANDLERS)
  1601. MESSAGE_HANDLERS = require('./handlers.js');
  1602. return MESSAGE_HANDLERS[type](this, payload);
  1603. case MESSAGE.KEXINIT:
  1604. if (!state.firstPacket) {
  1605. return doFatalError(
  1606. this,
  1607. 'Received extra KEXINIT during handshake',
  1608. 'handshake',
  1609. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1610. );
  1611. }
  1612. state.firstPacket = false;
  1613. return handleKexInit(this, payload);
  1614. default:
  1615. if (type < 20 || type > 49) {
  1616. return doFatalError(
  1617. this,
  1618. `Received unexpected packet type ${type}`,
  1619. 'handshake',
  1620. DISCONNECT_REASON.KEY_EXCHANGE_FAILED
  1621. );
  1622. }
  1623. }
  1624. return this._kex.parse(payload);
  1625. }
  1626. function dhEstimate(neg) {
  1627. const csCipher = CIPHER_INFO[neg.cs.cipher];
  1628. const scCipher = CIPHER_INFO[neg.sc.cipher];
  1629. // XXX: if OpenSSH's `umac-*` MACs are ever supported, their key lengths will
  1630. // also need to be considered when calculating `bits`
  1631. const bits = Math.max(
  1632. 0,
  1633. (csCipher.sslName === 'des-ede3-cbc' ? 14 : csCipher.keyLen),
  1634. csCipher.blockLen,
  1635. csCipher.ivLen,
  1636. (scCipher.sslName === 'des-ede3-cbc' ? 14 : scCipher.keyLen),
  1637. scCipher.blockLen,
  1638. scCipher.ivLen
  1639. ) * 8;
  1640. if (bits <= 112)
  1641. return 2048;
  1642. if (bits <= 128)
  1643. return 3072;
  1644. if (bits <= 192)
  1645. return 7680;
  1646. return 8192;
  1647. }
  1648. function trySendNEWKEYS(kex) {
  1649. if (!kex._sentNEWKEYS) {
  1650. kex._protocol._debug && kex._protocol._debug(
  1651. 'Outbound: Sending NEWKEYS'
  1652. );
  1653. const p = kex._protocol._packetRW.write.allocStartKEX;
  1654. const packet = kex._protocol._packetRW.write.alloc(1, true);
  1655. packet[p] = MESSAGE.NEWKEYS;
  1656. kex._protocol._cipher.encrypt(
  1657. kex._protocol._packetRW.write.finalize(packet, true)
  1658. );
  1659. kex._sentNEWKEYS = true;
  1660. }
  1661. }
  1662. module.exports = {
  1663. KexInit,
  1664. kexinit,
  1665. onKEXPayload,
  1666. DEFAULT_KEXINIT: new KexInit({
  1667. kex: DEFAULT_KEX,
  1668. serverHostKey: DEFAULT_SERVER_HOST_KEY,
  1669. cs: {
  1670. cipher: DEFAULT_CIPHER,
  1671. mac: DEFAULT_MAC,
  1672. compress: DEFAULT_COMPRESSION,
  1673. lang: [],
  1674. },
  1675. sc: {
  1676. cipher: DEFAULT_CIPHER,
  1677. mac: DEFAULT_MAC,
  1678. compress: DEFAULT_COMPRESSION,
  1679. lang: [],
  1680. },
  1681. }),
  1682. HANDLERS: {
  1683. [MESSAGE.KEXINIT]: handleKexInit,
  1684. },
  1685. };