bdy 1.23.6-dev → 1.23.7-dev

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