ssh_kex_dh_gex.c
Go to the documentation of this file.
1 /**
2  * @file ssh_kex_dh_gex.c
3  * @brief DH GEX (Diffie-Hellman Group Exchange) key exchange
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSSH Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.2
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SSH_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "ssh/ssh_algorithms.h"
37 #include "ssh/ssh_transport.h"
38 #include "ssh/ssh_kex.h"
39 #include "ssh/ssh_kex_dh_gex.h"
40 #include "ssh/ssh_packet.h"
41 #include "ssh/ssh_key_material.h"
42 #include "ssh/ssh_exchange_hash.h"
43 #include "ssh/ssh_key_verify.h"
44 #include "ssh/ssh_cert_verify.h"
45 #include "ssh/ssh_misc.h"
46 #include "pkix/pem_import.h"
47 #include "debug.h"
48 
49 //Check SSH stack configuration
50 #if (SSH_SUPPORT == ENABLED && SSH_DH_GEX_KEX_SUPPORT == ENABLED)
51 
52 
53 /**
54  * @brief Send SSH_MSG_KEX_DH_GEX_REQUEST message
55  * @param[in] connection Pointer to the SSH connection
56  * @return Error code
57  **/
58 
60 {
61 #if (SSH_CLIENT_SUPPORT == ENABLED)
62  error_t error;
63  size_t length;
64  uint8_t *message;
65 
66  //Point to the buffer where to format the message
67  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
68 
69  //Format SSH_MSG_KEX_DH_GEX_REQUEST message
70  error = sshFormatKexDhGexRequest(connection, message, &length);
71 
72  //Check status code
73  if(!error)
74  {
75  //Debug message
76  TRACE_INFO("Sending SSH_MSG_KEX_DH_GEX_REQUEST message (%" PRIuSIZE " bytes)...\r\n", length);
78 
79  //Send message
80  error = sshSendPacket(connection, message, length);
81  }
82 
83  //Check status code
84  if(!error)
85  {
86  //The server responds with an SSH_MSG_KEX_DH_GEX_GROUP message
87  connection->state = SSH_CONN_STATE_KEX_DH_GEX_GROUP;
88  }
89 
90  //Return status code
91  return error;
92 #else
93  //Client operation mode is not implemented
94  return ERROR_NOT_IMPLEMENTED;
95 #endif
96 }
97 
98 
99 /**
100  * @brief Send SSH_MSG_KEX_DH_GEX_GROUP message
101  * @param[in] connection Pointer to the SSH connection
102  * @return Error code
103  **/
104 
106 {
107 #if (SSH_SERVER_SUPPORT == ENABLED)
108  error_t error;
109  size_t length;
110  uint8_t *message;
111  SshContext *context;
112  SshDhGexGroup *dhGexGroup;
113 
114  //Point to the SSH context
115  context = connection->context;
116 
117  //Point to the buffer where to format the message
118  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
119 
120  //Valid Diffie-Hellman group?
121  if(connection->dhGexGroupIndex >= 0)
122  {
123  //Point to the selected group
124  dhGexGroup = &context->dhGexGroups[connection->dhGexGroupIndex];
125 
126  //Decode the PEM structure that holds Diffie-Hellman parameters
127  error = pemImportDhParameters(&connection->dhContext.params,
128  dhGexGroup->dhParams, dhGexGroup->dhParamsLen);
129  }
130  else
131  {
132  //Report an error
133  error = ERROR_INVALID_GROUP;
134  }
135 
136  //Check status code
137  if(!error)
138  {
139  //Format SSH_MSG_KEX_DH_GEX_GROUP message
140  error = sshFormatKexDhGexGroup(connection, message, &length);
141  }
142 
143  //Check status code
144  if(!error)
145  {
146  //Debug message
147  TRACE_INFO("Sending SSH_MSG_KEX_DH_GEX_GROUP message (%" PRIuSIZE " bytes)...\r\n", length);
149 
150  //Send message
151  error = sshSendPacket(connection, message, length);
152  }
153 
154  //Check status code
155  if(!error)
156  {
157  //The client responds with an SSH_MSG_KEX_DH_GEX_INIT message
158  connection->state = SSH_CONN_STATE_KEX_DH_GEX_INIT;
159  }
160 
161  //Return status code
162  return error;
163 #else
164  //Server operation mode is not implemented
165  return ERROR_NOT_IMPLEMENTED;
166 #endif
167 }
168 
169 
170 /**
171  * @brief Send SSH_MSG_KEX_DH_GEX_INIT message
172  * @param[in] connection Pointer to the SSH connection
173  * @return Error code
174  **/
175 
177 {
178 #if (SSH_CLIENT_SUPPORT == ENABLED)
179  error_t error;
180  size_t length;
181  uint8_t *message;
182  SshContext *context;
183 
184  //Point to the SSH context
185  context = connection->context;
186 
187  //Point to the buffer where to format the message
188  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
189 
190  //Generate an ephemeral key pair
191  error = dhGenerateKeyPair(&connection->dhContext, context->prngAlgo,
192  context->prngContext);
193 
194  //Check status code
195  if(!error)
196  {
197  //Format SSH_MSG_KEX_DH_GEX_INIT message
198  error = sshFormatKexDhGexInit(connection, message, &length);
199  }
200 
201  //Check status code
202  if(!error)
203  {
204  //Debug message
205  TRACE_INFO("Sending SSH_MSG_KEX_DH_GEX_INIT message (%" PRIuSIZE " bytes)...\r\n", length);
207 
208  //Send message
209  error = sshSendPacket(connection, message, length);
210  }
211 
212  //Check status code
213  if(!error)
214  {
215  //The server responds with an SSH_MSG_KEX_DH_GEX_REPLY message
216  connection->state = SSH_CONN_STATE_KEX_DH_GEX_REPLY;
217  }
218 
219  //Return status code
220  return error;
221 #else
222  //Client operation mode is not implemented
223  return ERROR_NOT_IMPLEMENTED;
224 #endif
225 }
226 
227 
228 /**
229  * @brief Send SSH_MSG_KEX_DH_GEX_REPLY message
230  * @param[in] connection Pointer to the SSH connection
231  * @return Error code
232  **/
233 
235 {
236 #if (SSH_SERVER_SUPPORT == ENABLED)
237  error_t error;
238  size_t length;
239  uint8_t *message;
240  SshContext *context;
241 
242  //Point to the SSH context
243  context = connection->context;
244 
245  //Point to the buffer where to format the message
246  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
247 
248  //Generate an ephemeral key pair
249  error = dhGenerateKeyPair(&connection->dhContext, context->prngAlgo,
250  context->prngContext);
251 
252  //Check status code
253  if(!error)
254  {
255  //Format SSH_MSG_KEX_DH_GEX_REPLY message
256  error = sshFormatKexDhGexReply(connection, message, &length);
257  }
258 
259  //Check status code
260  if(!error)
261  {
262  //Debug message
263  TRACE_INFO("Sending SSH_MSG_KEX_DH_GEX_REPLY message (%" PRIuSIZE " bytes)...\r\n", length);
265 
266  //Send message
267  error = sshSendPacket(connection, message, length);
268  }
269 
270  //Check status code
271  if(!error)
272  {
273  //Key exchange ends by each side sending an SSH_MSG_NEWKEYS message
274  connection->state = SSH_CONN_STATE_SERVER_NEW_KEYS;
275  }
276 
277  //Return status code
278  return error;
279 #else
280  //Server operation mode is not implemented
281  return ERROR_NOT_IMPLEMENTED;
282 #endif
283 }
284 
285 
286 /**
287  * @brief Format SSH_MSG_KEX_DH_GEX_REQUEST message
288  * @param[in] connection Pointer to the SSH connection
289  * @param[out] p Buffer where to format the message
290  * @param[out] length Length of the resulting message, in bytes
291  * @return Error code
292  **/
293 
295  size_t *length)
296 {
297 #if (SSH_CLIENT_SUPPORT == ENABLED)
298  //Total length of the message
299  *length = 0;
300 
301  //Set message type
303 
304  //Point to the first field of the message
305  p += sizeof(uint8_t);
306  *length += sizeof(uint8_t);
307 
308  //Minimal size in bits of an acceptable group (min)
310  //Preferred size in bits of the group the server will send (n)
312  //Maximal size in bits of an acceptable group (max)
314 
315  //Total length of the packet
316  *length += 3 * sizeof(uint32_t);
317 
318  //Successful processing
319  return NO_ERROR;
320 #else
321  //Client operation mode is not implemented
322  return ERROR_NOT_IMPLEMENTED;
323 #endif
324 }
325 
326 
327 /**
328  * @brief Format SSH_MSG_KEX_DH_GEX_GROUP message
329  * @param[in] connection Pointer to the SSH connection
330  * @param[out] p Buffer where to format the message
331  * @param[out] length Length of the resulting message, in bytes
332  * @return Error code
333  **/
334 
336  size_t *length)
337 {
338 #if (SSH_SERVER_SUPPORT == ENABLED)
339  error_t error;
340  size_t n;
341 
342  //Total length of the message
343  *length = 0;
344 
345  //Set message type
347 
348  //Point to the first field of the message
349  p += sizeof(uint8_t);
350  *length += sizeof(uint8_t);
351 
352  //Format safe prime (p)
353  error = sshFormatMpint(&connection->dhContext.params.p, p, &n);
354  //Any error to report?
355  if(error)
356  return error;
357 
358  //Update exchange hash H with p (safe prime)
359  error = sshUpdateExchangeHashRaw(connection, p, n);
360  //Any error to report?
361  if(error)
362  return error;
363 
364  //Point to the next field
365  p += n;
366  *length += n;
367 
368  //Format generator (g)
369  error = sshFormatMpint(&connection->dhContext.params.g, p, &n);
370  //Any error to report?
371  if(error)
372  return error;
373 
374  //Update exchange hash H with g (generator for subgroup)
375  error = sshUpdateExchangeHashRaw(connection, p, n);
376  //Any error to report?
377  if(error)
378  return error;
379 
380  //Total length of the packet
381  *length += n;
382 
383  //Successful processing
384  return NO_ERROR;
385 #else
386  //Server operation mode is not implemented
387  return ERROR_NOT_IMPLEMENTED;
388 #endif
389 }
390 
391 
392 /**
393  * @brief Format SSH_MSG_KEX_DH_GEX_INIT message
394  * @param[in] connection Pointer to the SSH connection
395  * @param[out] p Buffer where to format the message
396  * @param[out] length Length of the resulting message, in bytes
397  * @return Error code
398  **/
399 
401  size_t *length)
402 {
403 #if (SSH_CLIENT_SUPPORT == ENABLED)
404  error_t error;
405  size_t n;
406 
407  //Total length of the message
408  *length = 0;
409 
410  //Set message type
412 
413  //Point to the first field of the message
414  p += sizeof(uint8_t);
415  *length += sizeof(uint8_t);
416 
417  //Format client's ephemeral public key
418  error = sshFormatMpint(&connection->dhContext.ya, p, &n);
419  //Any error to report?
420  if(error)
421  return error;
422 
423  //Total length of the message
424  *length += n;
425 
426  //Successful processing
427  return NO_ERROR;
428 #else
429  //Client operation mode is not implemented
430  return ERROR_NOT_IMPLEMENTED;
431 #endif
432 }
433 
434 
435 /**
436  * @brief Format SSH_MSG_KEX_DH_GEX_REPLY message
437  * @param[in] connection Pointer to the SSH connection
438  * @param[out] p Buffer where to format the message
439  * @param[out] length Length of the resulting message, in bytes
440  * @return Error code
441  **/
442 
444  size_t *length)
445 {
446 #if (SSH_SERVER_SUPPORT == ENABLED)
447  error_t error;
448  size_t n;
449 
450  //Total length of the message
451  *length = 0;
452 
453  //Set message type
455 
456  //Point to the first field of the message
457  p += sizeof(uint8_t);
458  *length += sizeof(uint8_t);
459 
460  //Format server's public host key (K_S)
461  error = sshFormatHostKey(connection, p + sizeof(uint32_t), &n);
462  //Any error to report?
463  if(error)
464  return error;
465 
466  //The octet string value is preceded by a uint32 containing its length
467  STORE32BE(n, p);
468 
469  //Point to the next field
470  p += sizeof(uint32_t) + n;
471  *length += sizeof(uint32_t) + n;
472 
473  //Format server's ephemeral public key (f)
474  error = sshFormatMpint(&connection->dhContext.ya, p, &n);
475  //Any error to report?
476  if(error)
477  return error;
478 
479  //Update exchange hash H with f (exchange value sent by the server)
480  error = sshUpdateExchangeHashRaw(connection, p, n);
481  //Any error to report?
482  if(error)
483  return error;
484 
485  //Point to the next field
486  p += n;
487  *length += n;
488 
489  //Compute the shared secret K
490  error = sshComputeDhGexSharedSecret(connection);
491  //Any error to report?
492  if(error)
493  return error;
494 
495  //Update exchange hash H with K (shared secret)
496  error = sshUpdateExchangeHashRaw(connection, connection->k,
497  connection->kLen);
498  //Any error to report?
499  if(error)
500  return error;
501 
502  //Compute the signature on the exchange hash
503  error = sshGenerateExchangeHashSignature(connection, p + sizeof(uint32_t),
504  &n);
505  //Any error to report?
506  if(error)
507  return error;
508 
509  //The octet string value is preceded by a uint32 containing its length
510  STORE32BE(n, p);
511 
512  //Total length of the message
513  *length += sizeof(uint32_t) + n;
514 
515  //The ephemeral private key shall be destroyed as soon as possible (refer
516  //to RFC 9212, section 6)
517  dhFree(&connection->dhContext);
518  dhInit(&connection->dhContext);
519 
520  //Successful processing
521  return NO_ERROR;
522 #else
523  //Server operation mode is not implemented
524  return ERROR_NOT_IMPLEMENTED;
525 #endif
526 }
527 
528 
529 /**
530  * @brief Parse SSH_MSG_KEX_DH_GEX_REQUEST message
531  * @param[in] connection Pointer to the SSH connection
532  * @param[in] message Pointer to message
533  * @param[in] length Length of the message, in bytes
534  * @return Error code
535  **/
536 
538  const uint8_t *message, size_t length)
539 {
540 #if (SSH_SERVER_SUPPORT == ENABLED)
541  error_t error;
542  const uint8_t *p;
543  uint32_t minDhModulusSize;
544  uint32_t preferredDhModulusSize;
545  uint32_t maxDhModulusSize;
546 
547  //Debug message
548  TRACE_INFO("SSH_MSG_KEX_DH_GEX_REQUEST message received (%" PRIuSIZE " bytes)...\r\n", length);
550 
551  //Check operation mode
552  if(connection->context->mode != SSH_OPERATION_MODE_SERVER)
554 
555  //Check connection state
556  if(connection->state != SSH_CONN_STATE_KEX_DH_GEX_REQUEST)
558 
559  //Sanity check
560  if(length < sizeof(uint8_t))
561  return ERROR_INVALID_MESSAGE;
562 
563  //Point to the first field of the message
564  p = message + sizeof(uint8_t);
565  //Remaining bytes to process
566  length -= sizeof(uint8_t);
567 
568  //Malformed message?
569  if(length != (3 * sizeof(uint32_t)))
570  return ERROR_INVALID_MESSAGE;
571 
572  //Minimal size in bits of an acceptable group
573  minDhModulusSize = LOAD32BE(p);
574  //Preferred size in bits of the group the server will send
575  preferredDhModulusSize = LOAD32BE(p + 4);
576  //Maximal size in bits of an acceptable group
577  maxDhModulusSize = LOAD32BE(p + 8);
578 
579  //Debug message
580  TRACE_DEBUG(" min = %" PRIu32 "\r\n", minDhModulusSize);
581  TRACE_DEBUG(" n = %" PRIu32 "\r\n", preferredDhModulusSize);
582  TRACE_DEBUG(" max = %" PRIu32 "\r\n", maxDhModulusSize);
583 
584  //The server finds a group that best matches the client's request (refer
585  //to RFC 4419, section 3)
586  connection->dhGexGroupIndex = sshSelectDhGexGroup(connection->context,
587  minDhModulusSize, preferredDhModulusSize, maxDhModulusSize);
588 
589  //No Diffie-Hellman group found?
590  if(connection->dhGexGroupIndex < 0)
591  return ERROR_INVALID_GROUP;
592 
593  //Update exchange hash H with min, n and max
594  error = sshUpdateExchangeHashRaw(connection, p, length);
595  //Any error to report?
596  if(error)
597  return error;
598 
599  //The server responds with an SSH_MSG_KEX_DH_GEX_GROUP message
600  return sshSendKexDhGexGroup(connection);
601 #else
602  //Server operation mode is not implemented
604 #endif
605 }
606 
607 
608 /**
609  * @brief Parse SSH_MSG_KEX_DH_GEX_REQUEST_OLD message
610  * @param[in] connection Pointer to the SSH connection
611  * @param[in] message Pointer to message
612  * @param[in] length Length of the message, in bytes
613  * @return Error code
614  **/
615 
617  const uint8_t *message, size_t length)
618 {
619 #if (SSH_SERVER_SUPPORT == ENABLED)
620  error_t error;
621  const uint8_t *p;
622  uint32_t preferredDhModulusSize;
623 
624  //Debug message
625  TRACE_INFO("SSH_MSG_KEX_DH_GEX_REQUEST_OLD message received (%" PRIuSIZE " bytes)...\r\n", length);
627 
628  //Check operation mode
629  if(connection->context->mode != SSH_OPERATION_MODE_SERVER)
631 
632  //Check connection state
633  if(connection->state != SSH_CONN_STATE_KEX_DH_GEX_REQUEST)
635 
636  //Sanity check
637  if(length < sizeof(uint8_t))
638  return ERROR_INVALID_MESSAGE;
639 
640  //Point to the first field of the message
641  p = message + sizeof(uint8_t);
642  //Remaining bytes to process
643  length -= sizeof(uint8_t);
644 
645  //Malformed message?
646  if(length != sizeof(uint32_t))
647  return ERROR_INVALID_MESSAGE;
648 
649  //SSH_MSG_KEX_DH_GEX_REQUEST_OLD is used for backward compatibility.
650  //Instead of sending min, n and max, the client only sends n (refer to
651  //RFC 4419, section 5)
652  preferredDhModulusSize = LOAD32BE(p);
653 
654  //Debug message
655  TRACE_DEBUG(" n = %" PRIu32 "\r\n", preferredDhModulusSize);
656 
657  //The server finds a group that best matches the client's request (refer
658  //to RFC 4419, section 3)
659  connection->dhGexGroupIndex = sshSelectDhGexGroup(connection->context,
660  SSH_MIN_DH_MODULUS_SIZE, preferredDhModulusSize, SSH_MAX_DH_MODULUS_SIZE);
661 
662  //No Diffie-Hellman group found?
663  if(connection->dhGexGroupIndex < 0)
664  return ERROR_INVALID_GROUP;
665 
666  //The hash is calculated using only n instead of min, n and max (refer to
667  //RFC 4419, section 5)
668  error = sshUpdateExchangeHashRaw(connection, p, length);
669  //Any error to report?
670  if(error)
671  return error;
672 
673  //The server responds with an SSH_MSG_KEX_DH_GEX_GROUP message
674  return sshSendKexDhGexGroup(connection);
675 #else
676  //Server operation mode is not implemented
678 #endif
679 }
680 
681 
682 /**
683  * @brief Parse SSH_MSG_KEX_DH_GEX_GROUP message
684  * @param[in] connection Pointer to the SSH connection
685  * @param[in] message Pointer to message
686  * @param[in] length Length of the message, in bytes
687  * @return Error code
688  **/
689 
691  size_t length)
692 {
693 #if (SSH_CLIENT_SUPPORT == ENABLED)
694  error_t error;
695  uint_t k;
696  const uint8_t *p;
697  SshBinaryString prime;
698  SshBinaryString generator;
699 
700  //Debug message
701  TRACE_INFO("SSH_MSG_KEX_DH_GEX_GROUP message received (%" PRIuSIZE " bytes)...\r\n", length);
703 
704  //Check operation mode
705  if(connection->context->mode != SSH_OPERATION_MODE_CLIENT)
707 
708  //Check connection state
709  if(connection->state != SSH_CONN_STATE_KEX_DH_GEX_GROUP)
711 
712  //Sanity check
713  if(length < sizeof(uint8_t))
714  return ERROR_INVALID_MESSAGE;
715 
716  //Point to the first field of the message
717  p = message + sizeof(uint8_t);
718  //Remaining bytes to process
719  length -= sizeof(uint8_t);
720 
721  //Decode safe prime (p)
722  error = sshParseBinaryString(p, length, &prime);
723  //Any error to report?
724  if(error)
725  return error;
726 
727  //Point to the next field
728  p += sizeof(uint32_t) + prime.length;
729  length -= sizeof(uint32_t) + prime.length;
730 
731  //Decode generator (g)
732  error = sshParseBinaryString(p, length, &generator);
733  //Any error to report?
734  if(error)
735  return error;
736 
737  //Point to the next field
738  p += sizeof(uint32_t) + generator.length;
739  length -= sizeof(uint32_t) + generator.length;
740 
741  //Malformed message?
742  if(length != 0)
743  return ERROR_INVALID_MESSAGE;
744 
745  //Convert the prime modulus to a multiple precision integer
746  error = mpiImport(&connection->dhContext.params.p, prime.value,
748  //Any error to report?
749  if(error)
750  return error;
751 
752  //Get the length of the prime modulus, in bits
753  k = mpiGetBitLength(&connection->dhContext.params.p);
754 
755  //Make sure the prime modulus is acceptable
756  if(k < SSH_MIN_DH_MODULUS_SIZE || k > SSH_MAX_DH_MODULUS_SIZE)
757  return ERROR_INVALID_GROUP;
758 
759  //Convert the generator to a multiple precision integer
760  error = mpiImport(&connection->dhContext.params.g, generator.value,
761  generator.length, MPI_FORMAT_BIG_ENDIAN);
762  //Any error to report?
763  if(error)
764  return error;
765 
766  //The client responds with an SSH_MSG_KEX_DH_GEX_INIT message
767  return sshSendKexDhGexInit(connection);
768 #else
769  //Client operation mode is not implemented
771 #endif
772 }
773 
774 
775 /**
776  * @brief Parse SSH_MSG_KEX_DH_GEX_INIT message
777  * @param[in] connection Pointer to the SSH connection
778  * @param[in] message Pointer to message
779  * @param[in] length Length of the message, in bytes
780  * @return Error code
781  **/
782 
783 error_t sshParseKexDhGexInit(SshConnection *connection, const uint8_t *message,
784  size_t length)
785 {
786 #if (SSH_SERVER_SUPPORT == ENABLED)
787  error_t error;
788  const uint8_t *p;
789  SshBinaryString publicKey;
790 
791  //Debug message
792  TRACE_INFO("SSH_MSG_KEX_DH_GEX_INIT message received (%" PRIuSIZE " bytes)...\r\n", length);
794 
795  //Check operation mode
796  if(connection->context->mode != SSH_OPERATION_MODE_SERVER)
798 
799  //Check connection state
800  if(connection->state != SSH_CONN_STATE_KEX_DH_GEX_INIT)
802 
803  //Sanity check
804  if(length < sizeof(uint8_t))
805  return ERROR_INVALID_MESSAGE;
806 
807  //Point to the first field of the message
808  p = message + sizeof(uint8_t);
809  //Remaining bytes to process
810  length -= sizeof(uint8_t);
811 
812  //Decode client's ephemeral public key (e)
813  error = sshParseBinaryString(p, length, &publicKey);
814  //Any error to report?
815  if(error)
816  return error;
817 
818  //Point to the next field
819  p += sizeof(uint32_t) + publicKey.length;
820  length -= sizeof(uint32_t) + publicKey.length;
821 
822  //Malformed message?
823  if(length != 0)
824  return ERROR_INVALID_MESSAGE;
825 
826  //Update exchange hash H with e (exchange value sent by the client)
827  error = sshUpdateExchangeHash(connection, publicKey.value,
828  publicKey.length);
829  //Any error to report?
830  if(error)
831  return error;
832 
833  //Load client's ephemeral public key
834  error = dhImportPeerPublicKey(&connection->dhContext, publicKey.value,
835  publicKey.length, MPI_FORMAT_BIG_ENDIAN);
836  //Any error to report?
837  if(error)
838  return error;
839 
840  //The server responds with an SSH_MSG_KEX_DH_GEX_REPLY message
841  return sshSendKexDhGexReply(connection);
842 #else
843  //Server operation mode is not implemented
845 #endif
846 }
847 
848 
849 /**
850  * @brief Parse SSH_MSG_KEX_DH_GEX_REPLY message
851  * @param[in] connection Pointer to the SSH connection
852  * @param[in] message Pointer to message
853  * @param[in] length Length of the message, in bytes
854  * @return Error code
855  **/
856 
858  size_t length)
859 {
860 #if (SSH_CLIENT_SUPPORT == ENABLED)
861  error_t error;
862  const uint8_t *p;
863  SshString hostKeyAlgo;
864  SshBinaryString hostKey;
865  SshBinaryString publicKey;
866  SshBinaryString signature;
867 
868  //Debug message
869  TRACE_INFO("SSH_MSG_KEX_DH_GEX_REPLY message received (%" PRIuSIZE " bytes)...\r\n", length);
871 
872  //Check operation mode
873  if(connection->context->mode != SSH_OPERATION_MODE_CLIENT)
875 
876  //Check connection state
877  if(connection->state != SSH_CONN_STATE_KEX_DH_GEX_REPLY)
879 
880  //Sanity check
881  if(length < sizeof(uint8_t))
882  return ERROR_INVALID_MESSAGE;
883 
884  //Point to the first field of the message
885  p = message + sizeof(uint8_t);
886  //Remaining bytes to process
887  length -= sizeof(uint8_t);
888 
889  //Decode server's public host key (K_S)
890  error = sshParseBinaryString(p, length, &hostKey);
891  //Any error to report?
892  if(error)
893  return error;
894 
895  //Point to the next field
896  p += sizeof(uint32_t) + hostKey.length;
897  length -= sizeof(uint32_t) + hostKey.length;
898 
899  //Decode server's ephemeral public key (f)
900  error = sshParseBinaryString(p, length, &publicKey);
901  //Any error to report?
902  if(error)
903  return error;
904 
905  //Point to the next field
906  p += sizeof(uint32_t) + publicKey.length;
907  length -= sizeof(uint32_t) + publicKey.length;
908 
909  //Decode the signature field
910  error = sshParseBinaryString(p, length, &signature);
911  //Any error to report?
912  if(error)
913  return error;
914 
915  //Point to the next field
916  p += sizeof(uint32_t) + signature.length;
917  length -= sizeof(uint32_t) + signature.length;
918 
919  //Malformed message?
920  if(length != 0)
921  return ERROR_INVALID_MESSAGE;
922 
923  //Get the selected server's host key algorithm
924  hostKeyAlgo.value = connection->serverHostKeyAlgo;
925  hostKeyAlgo.length = osStrlen(connection->serverHostKeyAlgo);
926 
927 #if (SSH_CERT_SUPPORT == ENABLED)
928  //Certificate-based authentication?
929  if(sshIsCertPublicKeyAlgo(&hostKeyAlgo))
930  {
931  //Verify server's certificate
932  error = sshVerifyServerCertificate(connection, &hostKeyAlgo, &hostKey);
933  }
934  else
935 #endif
936  {
937  //Verify server's host key
938  error = sshVerifyServerHostKey(connection, &hostKeyAlgo, &hostKey);
939  }
940 
941  //If the client fails to verify the server's host key, it should disconnect
942  //from the server by sending an SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE message
943  if(error)
944  return ERROR_INVALID_KEY;
945 
946  //Update exchange hash H with K_S (server's public host key)
947  error = sshUpdateExchangeHash(connection, hostKey.value, hostKey.length);
948  //Any error to report?
949  if(error)
950  return error;
951 
952  //Update exchange hash H with min, n, max, p, g and e
953  error = sshDigestDhGexParams(connection);
954  //Any error to report?
955  if(error)
956  return error;
957 
958  //Update exchange hash H with f (exchange value sent by the server)
959  error = sshUpdateExchangeHash(connection, publicKey.value, publicKey.length);
960  //Any error to report?
961  if(error)
962  return error;
963 
964  //Load server's ephemeral public key
965  error = dhImportPeerPublicKey(&connection->dhContext, publicKey.value,
966  publicKey.length, MPI_FORMAT_BIG_ENDIAN);
967  //Any error to report?
968  if(error)
969  return error;
970 
971  //Compute the shared secret K
972  error = sshComputeDhGexSharedSecret(connection);
973  //Any error to report?
974  if(error)
975  return error;
976 
977  //Update exchange hash H with K (shared secret)
978  error = sshUpdateExchangeHashRaw(connection, connection->k,
979  connection->kLen);
980  //Any error to report?
981  if(error)
982  return error;
983 
984  //Verify the signature on the exchange hash
985  error = sshVerifyExchangeHashSignature(connection, &hostKey, &signature);
986  //Any error to report?
987  if(error)
988  return error;
989 
990  //The ephemeral private key shall be destroyed as soon as possible (refer
991  //to RFC 9212, section 6)
992  dhFree(&connection->dhContext);
993  dhInit(&connection->dhContext);
994 
995  //Key exchange ends by each side sending an SSH_MSG_NEWKEYS message
996  return sshSendNewKeys(connection);
997 #else
998  //Client operation mode is not implemented
1000 #endif
1001 }
1002 
1003 
1004 /**
1005  * @brief Parse Diffie-Hellman Group Exchange specific messages
1006  * @param[in] connection Pointer to the SSH connection
1007  * @param[in] type SSH message type
1008  * @param[in] message Pointer to message
1009  * @param[in] length Length of the message, in bytes
1010  * @return Error code
1011  **/
1012 
1014  const uint8_t *message, size_t length)
1015 {
1016  error_t error;
1017 
1018 #if (SSH_CLIENT_SUPPORT == ENABLED)
1019  //Client operation mode?
1020  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
1021  {
1022  //Check message type
1024  {
1025  //Parse SSH_MSG_KEX_DH_GEX_GROUP message
1026  error = sshParseKexDhGexGroup(connection, message, length);
1027  }
1028  else if(type == SSH_MSG_KEX_DH_GEX_REPLY)
1029  {
1030  //Parse SSH_MSG_KEX_DH_GEX_REPLY message
1031  error = sshParseKexDhGexReply(connection, message, length);
1032  }
1033  else
1034  {
1035  //Unknown message type
1036  error = ERROR_INVALID_TYPE;
1037  }
1038  }
1039  else
1040 #endif
1041 #if (SSH_SERVER_SUPPORT == ENABLED)
1042  //Server operation mode?
1043  if(connection->context->mode == SSH_OPERATION_MODE_SERVER)
1044  {
1045  //Check message type
1047  {
1048  //Parse SSH_MSG_KEX_DH_GEX_REQUEST message
1049  error = sshParseKexDhGexRequest(connection, message, length);
1050  }
1052  {
1053  //Parse SSH_MSG_KEX_DH_GEX_REQUEST_OLD message
1054  error = sshParseKexDhGexRequestOld(connection, message, length);
1055  }
1056  else if(type == SSH_MSG_KEX_DH_GEX_INIT)
1057  {
1058  //Parse SSH_MSG_KEX_DH_GEX_INIT message
1059  error = sshParseKexDhGexInit(connection, message, length);
1060  }
1061  else
1062  {
1063  //Unknown message type
1064  error = ERROR_INVALID_TYPE;
1065  }
1066  }
1067  else
1068 #endif
1069  //Invalid operation mode?
1070  {
1071  //Report an error
1072  error = ERROR_INVALID_TYPE;
1073  }
1074 
1075  //Return status code
1076  return error;
1077 }
1078 
1079 
1080 /**
1081  * @brief Select a Diffie-Hellman group that best matches client's request
1082  * @param[in] context Pointer to the SSH context
1083  * @param[in] minDhModulusSize Minimum acceptable size for Diffie-Hellman prime modulus
1084  * @param[in] preferredDhModulusSize Preferred size for Diffie-Hellman prime modulus
1085  * @param[in] maxDhModulusSize Maximum acceptable size for Diffie-Hellman prime modulus
1086  * @return Index of the selected Diffie-Hellman group, if any
1087  **/
1088 
1089 int_t sshSelectDhGexGroup(SshContext *context, uint32_t minDhModulusSize,
1090  uint32_t preferredDhModulusSize, uint32_t maxDhModulusSize)
1091 {
1092 #if (SSH_SERVER_SUPPORT == ENABLED)
1093  uint_t i;
1094  uint32_t a;
1095  uint32_t b;
1096  int_t bestIndex;
1097  const SshDhGexGroup *group;
1098  const SshDhGexGroup *bestGroup;
1099 
1100  //Initialize index
1101  bestIndex = -1;
1102  bestGroup = NULL;
1103 
1104  //Loop through the Diffie-Hellman groups
1105  for(i = 0; i < SSH_MAX_DH_GEX_GROUPS; i++)
1106  {
1107  //Point to the current group
1108  group = &context->dhGexGroups[i];
1109 
1110  //Valid prime modulus?
1111  if(group->dhModulusSize >= SSH_MIN_DH_MODULUS_SIZE &&
1113  {
1114  //Check whether the current group is acceptable
1115  if(group->dhModulusSize >= minDhModulusSize &&
1116  group->dhModulusSize <= maxDhModulusSize)
1117  {
1118  //Select the group that best matches client's request
1119  if(bestIndex < 0 || bestGroup == NULL)
1120  {
1121  bestIndex = i;
1122  bestGroup = group;
1123  }
1124  else
1125  {
1126  //The client indicates the preferred size
1127  if(group->dhModulusSize > preferredDhModulusSize)
1128  {
1129  a = group->dhModulusSize - preferredDhModulusSize;
1130  }
1131  else
1132  {
1133  a = preferredDhModulusSize - group->dhModulusSize;
1134  }
1135 
1136  if(bestGroup->dhModulusSize > preferredDhModulusSize)
1137  {
1138  b = bestGroup->dhModulusSize - preferredDhModulusSize;
1139  }
1140  else
1141  {
1142  b = preferredDhModulusSize - bestGroup->dhModulusSize;
1143  }
1144 
1145  if(a < b)
1146  {
1147  bestIndex = i;
1148  bestGroup = group;
1149  }
1150  }
1151  }
1152  }
1153  }
1154 
1155  //Return the index of the Diffie-Hellman group
1156  return bestIndex;
1157 #else
1158  //Server operation mode is not implemented
1159  return -1;
1160 #endif
1161 }
1162 
1163 
1164 /**
1165  * @brief Diffie-Hellman shared secret calculation
1166  * @param[in] connection Pointer to the SSH connection
1167  * @return Error code
1168  **/
1169 
1171 {
1172  error_t error;
1173 
1174  //Compute the shared secret K
1175  error = dhComputeSharedSecret(&connection->dhContext, connection->k,
1176  SSH_MAX_SHARED_SECRET_LEN - SSH_MAX_MPINT_OVERHEAD, &connection->kLen);
1177 
1178  //Check status code
1179  if(!error)
1180  {
1181  //Log shared secret (for debugging purpose only)
1182  sshDumpKey(connection, "SHARED_SECRET", connection->k, connection->kLen);
1183 
1184  //Convert the shared secret K to mpint representation
1185  error = sshConvertArrayToMpint(connection->k, connection->kLen,
1186  connection->k, &connection->kLen);
1187  }
1188 
1189  //Return status code
1190  return error;
1191 }
1192 
1193 
1194 /**
1195  * @brief Update exchange hash with min, n, max, p, g and e
1196  * @param[in] connection Pointer to the SSH connection
1197  * @return Error code
1198  **/
1199 
1201 {
1202  error_t error;
1203  size_t n;
1204  uint8_t *buffer;
1205 
1206  //Allocate a temporary buffer
1207  buffer = sshAllocMem(SSH_BUFFER_SIZE);
1208 
1209  //Successful memory allocation?
1210  if(buffer != NULL)
1211  {
1212  //Minimal size in bits of an acceptable group (min)
1214  //Preferred size in bits of the group the server will send (n)
1216  //Maximal size in bits of an acceptable group (max)
1217  STORE32BE(SSH_MAX_DH_MODULUS_SIZE, buffer + 8);
1218 
1219  //Update exchange hash H with min, n and max
1220  error = sshUpdateExchangeHashRaw(connection, buffer, 3 * sizeof(uint32_t));
1221 
1222  //Check status code
1223  if(!error)
1224  {
1225  //Format Diffie-Hellman prime modulus
1226  error = sshFormatMpint(&connection->dhContext.params.p, buffer, &n);
1227  }
1228 
1229  //Check status code
1230  if(!error)
1231  {
1232  //Update exchange hash H with p (safe prime)
1233  error = sshUpdateExchangeHashRaw(connection, buffer, n);
1234  }
1235 
1236  //Check status code
1237  if(!error)
1238  {
1239  //Format Diffie-Hellman generator
1240  error = sshFormatMpint(&connection->dhContext.params.g, buffer, &n);
1241  }
1242 
1243  //Check status code
1244  if(!error)
1245  {
1246  //Update exchange hash H with g (generator for subgroup)
1247  error = sshUpdateExchangeHashRaw(connection, buffer, n);
1248  }
1249 
1250  //Check status code
1251  if(!error)
1252  {
1253  //Format client's ephemeral public key
1254  error = sshFormatMpint(&connection->dhContext.ya, buffer, &n);
1255  }
1256 
1257  //Check status code
1258  if(!error)
1259  {
1260  //Update exchange hash H with e (exchange value sent by the client)
1261  error = sshUpdateExchangeHashRaw(connection, buffer, n);
1262  }
1263 
1264  //Release previously allocated memory
1265  sshFreeMem(buffer);
1266  }
1267  else
1268  {
1269  //Failed to allocate memory
1270  error = ERROR_OUT_OF_MEMORY;
1271  }
1272 
1273  //Return status code
1274  return error;
1275 }
1276 
1277 #endif
error_t sshGenerateExchangeHashSignature(SshConnection *connection, uint8_t *p, size_t *written)
Compute the signature on the exchange hash.
uint8_t b
Definition: nbns_common.h:104
uint8_t a
Definition: ndp.h:411
signed int int_t
Definition: compiler_port.h:56
#define LOAD32BE(p)
Definition: cpu_endian.h:210
SSH host key verification.
Binary string.
Definition: ssh_types.h:67
@ SSH_CONN_STATE_KEX_DH_GEX_REQUEST
Definition: ssh.h:1043
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
error_t sshDigestDhGexParams(SshConnection *connection)
Update exchange hash with min, n, max, p, g and e.
@ ERROR_UNEXPECTED_MESSAGE
Definition: error.h:195
void sshDumpKey(SshConnection *connection, const char_t *label, const uint8_t *key, size_t keyLen)
Dump secret key (for debugging purpose only)
uint8_t p
Definition: ndp.h:300
uint8_t message[]
Definition: chap.h:154
error_t sshFormatKexDhGexInit(SshConnection *connection, uint8_t *p, size_t *length)
Format SSH_MSG_KEX_DH_GEX_INIT message.
error_t sshParseKexDhGexGroup(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEX_DH_GEX_GROUP message.
error_t sshVerifyServerHostKey(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *hostKey)
Verify server's host key.
error_t sshVerifyExchangeHashSignature(SshConnection *connection, const SshBinaryString *serverHostKey, const SshBinaryString *signature)
Verify the signature on the exchange hash.
error_t sshParseKexDhGexRequest(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEX_DH_GEX_REQUEST message.
SSH transport layer protocol.
error_t sshUpdateExchangeHashRaw(SshConnection *connection, const void *data, size_t length)
Update exchange hash calculation (raw data)
uint8_t type
Definition: coap_common.h:176
SSH certificate verification.
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
error_t sshParseKexDhGexInit(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEX_DH_GEX_INIT message.
size_t length
Definition: ssh_types.h:58
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
#define SSH_MAX_SHARED_SECRET_LEN
Definition: ssh.h:842
#define osStrlen(s)
Definition: os_port.h:168
error_t sshFormatKexDhGexRequest(SshConnection *connection, uint8_t *p, size_t *length)
Format SSH_MSG_KEX_DH_GEX_REQUEST message.
int_t sshSelectDhGexGroup(SshContext *context, uint32_t minDhModulusSize, uint32_t preferredDhModulusSize, uint32_t maxDhModulusSize)
Select a Diffie-Hellman group that best matches client's request.
@ SSH_CONN_STATE_KEX_DH_GEX_GROUP
Definition: ssh.h:1044
#define SSH_PACKET_HEADER_SIZE
Definition: ssh_packet.h:38
error_t sshSendPacket(SshConnection *connection, uint8_t *payload, size_t payloadLen)
Send SSH packet.
Definition: ssh_packet.c:57
bool_t sshIsCertPublicKeyAlgo(const SshString *publicKeyAlgo)
Test if the specified public key algorithm is using certificates.
error_t pemImportDhParameters(DhParameters *params, const char_t *input, size_t length)
Decode a PEM file containing Diffie-Hellman parameters.
Definition: pem_import.c:137
error_t sshSendNewKeys(SshConnection *connection)
Send SSH_MSG_NEWKEYS message.
Definition: ssh_kex.c:194
@ SSH_MSG_KEX_DH_GEX_REPLY
Definition: ssh.h:956
size_t length
Definition: ssh_types.h:69
@ ERROR_INVALID_GROUP
Definition: error.h:276
Key material generation.
error_t dhComputeSharedSecret(DhContext *context, uint8_t *output, size_t outputSize, size_t *outputLen)
Compute Diffie-Hellman shared secret.
Definition: dh.c:289
error_t sshConvertArrayToMpint(const uint8_t *value, size_t length, uint8_t *p, size_t *written)
Convert a binary string to mpint representation.
Definition: ssh_misc.c:1625
PEM file import functions.
#define SshContext
Definition: ssh.h:870
DH GEX (Diffie-Hellman Group Exchange) key exchange.
const char_t * value
Definition: ssh_types.h:57
#define SSH_MAX_MPINT_OVERHEAD
Definition: ssh.h:861
error_t
Error codes.
Definition: error.h:43
error_t sshParseKexDhGexMessage(SshConnection *connection, uint8_t type, const uint8_t *message, size_t length)
Parse Diffie-Hellman Group Exchange specific messages.
@ SSH_OPERATION_MODE_SERVER
Definition: ssh.h:893
error_t sshFormatKexDhGexGroup(SshConnection *connection, uint8_t *p, size_t *length)
Format SSH_MSG_KEX_DH_GEX_GROUP message.
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:892
@ SSH_MSG_KEX_DH_GEX_REQUEST_OLD
Definition: ssh.h:952
error_t sshVerifyServerCertificate(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *hostKey)
Verify server's certificate.
error_t sshParseKexDhGexReply(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEX_DH_GEX_REPLY message.
error_t sshFormatKexDhGexReply(SshConnection *connection, uint8_t *p, size_t *length)
Format SSH_MSG_KEX_DH_GEX_REPLY message.
error_t mpiImport(Mpi *r, const uint8_t *input, size_t length, MpiFormat format)
Octet string to integer conversion.
Definition: mpi.c:712
Diffie-Hellman group.
Definition: ssh.h:1131
void dhFree(DhContext *context)
Release Diffie-Hellman context.
Definition: dh.c:71
const char_t * dhParams
Diffie-Hellman parameters (PEM format)
Definition: ssh.h:1133
@ ERROR_INVALID_TYPE
Definition: error.h:115
error_t sshComputeDhGexSharedSecret(SshConnection *connection)
Diffie-Hellman shared secret calculation.
error_t dhImportPeerPublicKey(DhContext *context, const uint8_t *input, size_t length, MpiFormat format)
Import peer's public key.
Definition: dh.c:218
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
@ SSH_CONN_STATE_SERVER_NEW_KEYS
Definition: ssh.h:1052
String.
Definition: ssh_types.h:56
SSH key exchange.
uint_t mpiGetBitLength(const Mpi *a)
Get the actual length in bits.
Definition: mpi.c:254
const uint8_t * value
Definition: ssh_types.h:68
error_t sshSendKexDhGexReply(SshConnection *connection)
Send SSH_MSG_KEX_DH_GEX_REPLY message.
error_t sshFormatMpint(const Mpi *value, uint8_t *p, size_t *written)
Format a multiple precision integer.
Definition: ssh_misc.c:1507
error_t sshSendKexDhGexRequest(SshConnection *connection)
Send SSH_MSG_KEX_DH_GEX_REQUEST message.
#define TRACE_DEBUG(...)
Definition: debug.h:119
#define sshFreeMem(p)
Definition: ssh.h:729
uint8_t n
Exchange hash calculation.
error_t sshSendKexDhGexInit(SshConnection *connection)
Send SSH_MSG_KEX_DH_GEX_INIT message.
error_t sshSendKexDhGexGroup(SshConnection *connection)
Send SSH_MSG_KEX_DH_GEX_GROUP message.
#define SshConnection
Definition: ssh.h:874
#define SSH_MAX_DH_MODULUS_SIZE
Definition: ssh.h:689
@ SSH_MSG_KEX_DH_GEX_INIT
Definition: ssh.h:955
#define SSH_MAX_DH_GEX_GROUPS
Definition: ssh.h:668
#define SSH_MIN_DH_MODULUS_SIZE
Definition: ssh.h:675
@ SSH_CONN_STATE_KEX_DH_GEX_REPLY
Definition: ssh.h:1046
SSH helper functions.
@ MPI_FORMAT_BIG_ENDIAN
Definition: mpi.h:93
@ SSH_MSG_KEX_DH_GEX_REQUEST
Definition: ssh.h:953
error_t sshFormatHostKey(SshConnection *connection, uint8_t *p, size_t *written)
Format host key structure.
Definition: ssh_misc.c:864
uint_t dhModulusSize
Length of the prime modulus, in bits.
Definition: ssh.h:1132
SSH packet encryption/decryption.
error_t sshUpdateExchangeHash(SshConnection *connection, const void *data, size_t length)
Update exchange hash calculation.
@ SSH_CONN_STATE_KEX_DH_GEX_INIT
Definition: ssh.h:1045
error_t sshParseBinaryString(const uint8_t *p, size_t length, SshBinaryString *string)
Parse a binary string.
Definition: ssh_misc.c:1204
#define sshAllocMem(size)
Definition: ssh.h:724
error_t dhGenerateKeyPair(DhContext *context, const PrngAlgo *prngAlgo, void *prngContext)
Diffie-Hellman key pair generation.
Definition: dh.c:119
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:57
@ SSH_MSG_KEX_DH_GEX_GROUP
Definition: ssh.h:954
Secure Shell (SSH)
SSH algorithm negotiation.
void dhInit(DhContext *context)
Initialize Diffie-Hellman context.
Definition: dh.c:54
#define SSH_BUFFER_SIZE
Definition: ssh.h:866
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
error_t sshParseKexDhGexRequestOld(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEX_DH_GEX_REQUEST_OLD message.
@ ERROR_INVALID_KEY
Definition: error.h:106
@ NO_ERROR
Success.
Definition: error.h:44
#define SSH_PREFERRED_DH_MODULUS_SIZE
Definition: ssh.h:682
Debugging facilities.
size_t dhParamsLen
Length of the Diffie-Hellman parameters.
Definition: ssh.h:1134
#define TRACE_VERBOSE_ARRAY(p, a, n)
Definition: debug.h:140