ssh_algorithms.c
Go to the documentation of this file.
1 /**
2  * @file ssh_algorithms.c
3  * @brief SSH algorithm negotiation
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_kex_rsa.h"
38 #include "ssh/ssh_kex_dh_gex.h"
39 #include "ssh/ssh_misc.h"
40 #include "debug.h"
41 
42 //Check SSH stack configuration
43 #if (SSH_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief List of supported key exchange algorithms
48  **/
49 
50 static const char_t *const sshSupportedKexAlgos[] =
51 {
52 #if (SSH_HYBRID_KEX_SUPPORT == ENABLED && SSH_SNTRUP761_SUPPORT == ENABLED && \
53  SSH_CURVE25519_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED)
54  "sntrup761x25519-sha512",
55  "sntrup761x25519-sha512@openssh.com",
56 #endif
57 #if (SSH_HYBRID_KEX_SUPPORT == ENABLED && SSH_MLKEM768_SUPPORT == ENABLED && \
58  SSH_CURVE25519_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED)
59  "mlkem768x25519-sha256",
60 #endif
61 #if (SSH_HYBRID_KEX_SUPPORT == ENABLED && SSH_MLKEM768_SUPPORT == ENABLED && \
62  SSH_NISTP256_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED)
63  "mlkem768nistp256-sha256",
64 #endif
65 #if (SSH_HYBRID_KEX_SUPPORT == ENABLED && SSH_MLKEM1024_SUPPORT == ENABLED && \
66  SSH_NISTP384_SUPPORT == ENABLED && SSH_SHA384_SUPPORT == ENABLED)
67  "mlkem1024nistp384-sha384",
68 #endif
69 #if (SSH_ECDH_KEX_SUPPORT == ENABLED && SSH_CURVE25519_SUPPORT == ENABLED && \
70  SSH_SHA256_SUPPORT == ENABLED)
71  "curve25519-sha256",
72  "curve25519-sha256@libssh.org",
73 #endif
74 #if (SSH_ECDH_KEX_SUPPORT == ENABLED && SSH_CURVE448_SUPPORT == ENABLED && \
75  SSH_SHA512_SUPPORT == ENABLED)
76  "curve448-sha512",
77 #endif
78 #if (SSH_ECDH_KEX_SUPPORT == ENABLED && SSH_NISTP256_SUPPORT == ENABLED && \
79  SSH_SHA256_SUPPORT == ENABLED)
80  "ecdh-sha2-nistp256",
81 #endif
82 #if (SSH_ECDH_KEX_SUPPORT == ENABLED && SSH_NISTP384_SUPPORT == ENABLED && \
83  SSH_SHA384_SUPPORT == ENABLED)
84  "ecdh-sha2-nistp384",
85 #endif
86 #if (SSH_ECDH_KEX_SUPPORT == ENABLED && SSH_NISTP521_SUPPORT == ENABLED && \
87  SSH_SHA512_SUPPORT == ENABLED)
88  "ecdh-sha2-nistp521",
89 #endif
90 #if (SSH_DH_GEX_KEX_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED)
91  "diffie-hellman-group-exchange-sha256",
92 #endif
93 #if (SSH_DH_GEX_KEX_SUPPORT == ENABLED && SSH_SHA384_SUPPORT == ENABLED)
94  "diffie-hellman-group-exchange-sha384@ssh.com",
95 #endif
96 #if (SSH_DH_GEX_KEX_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED)
97  "diffie-hellman-group-exchange-sha512@ssh.com",
98 #endif
99 #if (SSH_DH_KEX_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED && \
100  SSH_MAX_DH_MODULUS_SIZE >= 2048 && SSH_MIN_DH_MODULUS_SIZE <= 2048)
101  "diffie-hellman-group14-sha256",
102 #endif
103 #if (SSH_DH_KEX_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED && \
104  SSH_MAX_DH_MODULUS_SIZE >= 3072 && SSH_MIN_DH_MODULUS_SIZE <= 3072)
105  "diffie-hellman-group15-sha512",
106 #endif
107 #if (SSH_DH_KEX_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED && \
108  SSH_MAX_DH_MODULUS_SIZE >= 4096 && SSH_MIN_DH_MODULUS_SIZE <= 4096)
109  "diffie-hellman-group16-sha512",
110 #endif
111 #if (SSH_DH_KEX_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED && \
112  SSH_MAX_DH_MODULUS_SIZE >= 6144 && SSH_MIN_DH_MODULUS_SIZE <= 6144)
113  "diffie-hellman-group17-sha512",
114 #endif
115 #if (SSH_DH_KEX_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED && \
116  SSH_MAX_DH_MODULUS_SIZE >= 8192 && SSH_MIN_DH_MODULUS_SIZE <= 8192)
117  "diffie-hellman-group18-sha512",
118 #endif
119 #if (SSH_RSA_KEX_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED && \
120  SSH_MAX_RSA_MODULUS_SIZE >= 2048)
121  "rsa2048-sha256",
122 #endif
123 #if (SSH_DH_GEX_KEX_SUPPORT == ENABLED && SSH_SHA224_SUPPORT == ENABLED)
124  "diffie-hellman-group-exchange-sha224@ssh.com",
125 #endif
126 #if (SSH_DH_GEX_KEX_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED)
127  "diffie-hellman-group-exchange-sha1",
128 #endif
129 #if (SSH_DH_KEX_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED && \
130  SSH_MAX_DH_MODULUS_SIZE >= 2048 && SSH_MIN_DH_MODULUS_SIZE <= 2048)
131  "diffie-hellman-group14-sha1",
132 #endif
133 #if (SSH_DH_KEX_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED && \
134  SSH_MAX_DH_MODULUS_SIZE >= 1024 && SSH_MIN_DH_MODULUS_SIZE <= 1024)
135  "diffie-hellman-group1-sha1",
136 #endif
137 #if (SSH_RSA_KEX_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED && \
138  SSH_MAX_RSA_MODULUS_SIZE >= 1024)
139  "rsa1024-sha1",
140 #endif
141 };
142 
143 
144 /**
145  * @brief List of supported host key algorithms
146  **/
147 
148 static const SshHostKeyAlgo sshSupportedHostKeyAlgos[] =
149 {
150 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
151  {
152  "ssh-ed25519-cert",
153  "ssh-ed25519-cert",
154  "ssh-ed25519"
155  },
156  {
157  "ssh-ed25519-cert-v01@openssh.com",
158  "ssh-ed25519-cert-v01@openssh.com",
159  "ssh-ed25519"
160  },
161 #endif
162 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
163  {
164  "ssh-ed25519",
165  "ssh-ed25519",
166  "ssh-ed25519"
167  },
168 #endif
169 #if (SSH_ED448_SIGN_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
170  {
171  "ssh-ed448-cert",
172  "ssh-ed448-cert",
173  "ssh-ed448"
174  },
175 #endif
176 #if (SSH_ED448_SIGN_SUPPORT == ENABLED)
177  {
178  "ssh-ed448",
179  "ssh-ed448",
180  "ssh-ed448"
181  },
182 #endif
183 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP256_SUPPORT == ENABLED && \
184  SSH_SHA256_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
185  {
186  "ecdsa-sha2-nistp256-cert",
187  "ecdsa-sha2-nistp256-cert",
188  "ecdsa-sha2-nistp256"
189  },
190  {
191  "ecdsa-sha2-nistp256-cert-v01@openssh.com",
192  "ecdsa-sha2-nistp256-cert-v01@openssh.com",
193  "ecdsa-sha2-nistp256"
194  },
195 #endif
196 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP256_SUPPORT == ENABLED && \
197  SSH_SHA256_SUPPORT == ENABLED)
198  {
199  "ecdsa-sha2-nistp256",
200  "ecdsa-sha2-nistp256",
201  "ecdsa-sha2-nistp256"
202  },
203 #endif
204 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP384_SUPPORT == ENABLED && \
205  SSH_SHA384_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
206  {
207  "ecdsa-sha2-nistp384-cert",
208  "ecdsa-sha2-nistp384-cert",
209  "ecdsa-sha2-nistp384"
210  },
211  {
212  "ecdsa-sha2-nistp384-cert-v01@openssh.com",
213  "ecdsa-sha2-nistp384-cert-v01@openssh.com",
214  "ecdsa-sha2-nistp384"
215  },
216 #endif
217 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP384_SUPPORT == ENABLED && \
218  SSH_SHA384_SUPPORT == ENABLED)
219  {
220  "ecdsa-sha2-nistp384",
221  "ecdsa-sha2-nistp384",
222  "ecdsa-sha2-nistp384"
223  },
224 #endif
225 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP521_SUPPORT == ENABLED && \
226  SSH_SHA512_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
227  {
228  "ecdsa-sha2-nistp521-cert",
229  "ecdsa-sha2-nistp521-cert",
230  "ecdsa-sha2-nistp521"
231  },
232  {
233  "ecdsa-sha2-nistp521-cert-v01@openssh.com",
234  "ecdsa-sha2-nistp521-cert-v01@openssh.com",
235  "ecdsa-sha2-nistp521"
236  },
237 #endif
238 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP521_SUPPORT == ENABLED && \
239  SSH_SHA512_SUPPORT == ENABLED)
240  {
241  "ecdsa-sha2-nistp521",
242  "ecdsa-sha2-nistp521",
243  "ecdsa-sha2-nistp521"
244  },
245 #endif
246 #if (SSH_RSA_SIGN_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED && \
247  SSH_CERT_SUPPORT == ENABLED)
248  {
249  "rsa-sha2-256-cert-v01@openssh.com",
250  "ssh-rsa-cert-v01@openssh.com",
251  "rsa-sha2-256"
252  },
253 #endif
254 #if (SSH_RSA_SIGN_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED)
255  {
256  "rsa-sha2-256",
257  "ssh-rsa",
258  "rsa-sha2-256"
259  },
260 #endif
261 #if (SSH_RSA_SIGN_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED && \
262  SSH_CERT_SUPPORT == ENABLED)
263  {
264  "rsa-sha2-512-cert-v01@openssh.com",
265  "ssh-rsa-cert-v01@openssh.com",
266  "rsa-sha2-512"
267  },
268 #endif
269 #if (SSH_RSA_SIGN_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED)
270  {
271  "rsa-sha2-512",
272  "ssh-rsa",
273  "rsa-sha2-512"
274  },
275 #endif
276 #if (SSH_RSA_SIGN_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED && \
277  SSH_CERT_SUPPORT == ENABLED)
278  {
279  "ssh-rsa-cert",
280  "ssh-rsa-cert",
281  "ssh-rsa"
282  },
283  {
284  "ssh-rsa-cert-v01@openssh.com",
285  "ssh-rsa-cert-v01@openssh.com",
286  "ssh-rsa"
287  },
288 #endif
289 #if (SSH_RSA_SIGN_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED)
290  {
291  "ssh-rsa",
292  "ssh-rsa",
293  "ssh-rsa"
294  },
295 #endif
296 #if (SSH_DSA_SIGN_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED && \
297  SSH_CERT_SUPPORT == ENABLED)
298  {
299  "ssh-dss-cert",
300  "ssh-dss-cert",
301  "ssh-dss"
302  },
303  {
304  "ssh-dss-cert-v01@openssh.com",
305  "ssh-dss-cert-v01@openssh.com",
306  "ssh-dss"
307  },
308 #endif
309 #if (SSH_DSA_SIGN_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED)
310  {
311  "ssh-dss",
312  "ssh-dss",
313  "ssh-dss"
314  },
315 #endif
316 };
317 
318 
319 /**
320  * @brief List of supported encryption algorithms
321  **/
322 
323 static const char_t *const sshSupportedEncAlgos[] =
324 {
325 #if (SSH_CHACHA20_POLY1305_SUPPORT == ENABLED)
326  "chacha20-poly1305",
327  "chacha20-poly1305@openssh.com",
328 #endif
329 #if (SSH_AES_128_SUPPORT == ENABLED && SSH_GCM_CIPHER_SUPPORT == ENABLED)
330  "aes128-gcm",
331  "aes128-gcm@openssh.com",
332 #endif
333 #if (SSH_AES_256_SUPPORT == ENABLED && SSH_GCM_CIPHER_SUPPORT == ENABLED)
334  "aes256-gcm",
335  "aes256-gcm@openssh.com",
336 #endif
337 #if (SSH_AES_128_SUPPORT == ENABLED && SSH_RFC5647_SUPPORT == ENABLED)
338  "AEAD_AES_128_GCM",
339 #endif
340 #if (SSH_AES_256_SUPPORT == ENABLED && SSH_RFC5647_SUPPORT == ENABLED)
341  "AEAD_AES_256_GCM",
342 #endif
343 #if (SSH_CAMELLIA_128_SUPPORT == ENABLED && SSH_RFC5647_SUPPORT == ENABLED)
344  "AEAD_CAMELLIA_128_GCM",
345 #endif
346 #if (SSH_CAMELLIA_256_SUPPORT == ENABLED && SSH_RFC5647_SUPPORT == ENABLED)
347  "AEAD_CAMELLIA_256_GCM",
348 #endif
349 #if (SSH_AES_128_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
350  "aes128-ctr",
351 #endif
352 #if (SSH_AES_192_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
353  "aes192-ctr",
354 #endif
355 #if (SSH_AES_256_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
356  "aes256-ctr",
357 #endif
358 #if (SSH_TWOFISH_128_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
359  "twofish128-ctr",
360 #endif
361 #if (SSH_TWOFISH_192_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
362  "twofish192-ctr",
363 #endif
364 #if (SSH_TWOFISH_256_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
365  "twofish256-ctr",
366 #endif
367 #if (SSH_SERPENT_128_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
368  "serpent128-ctr",
369 #endif
370 #if (SSH_SERPENT_192_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
371  "serpent192-ctr",
372 #endif
373 #if (SSH_SERPENT_256_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
374  "serpent256-ctr",
375 #endif
376 #if (SSH_CAMELLIA_128_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
377  "camellia128-ctr",
378 #endif
379 #if (SSH_CAMELLIA_192_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
380  "camellia192-ctr",
381 #endif
382 #if (SSH_CAMELLIA_256_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
383  "camellia256-ctr",
384 #endif
385 #if (SSH_AES_128_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
386  "aes128-cbc",
387 #endif
388 #if (SSH_AES_192_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
389  "aes192-cbc",
390 #endif
391 #if (SSH_AES_256_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
392  "aes256-cbc",
393 #endif
394 #if (SSH_TWOFISH_128_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
395  "twofish128-cbc",
396 #endif
397 #if (SSH_TWOFISH_192_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
398  "twofish192-cbc",
399 #endif
400 #if (SSH_TWOFISH_256_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
401  "twofish256-cbc",
402  "twofish-cbc",
403 #endif
404 #if (SSH_SERPENT_128_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
405  "serpent128-cbc",
406 #endif
407 #if (SSH_SERPENT_192_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
408  "serpent192-cbc",
409 #endif
410 #if (SSH_SERPENT_256_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
411  "serpent256-cbc",
412 #endif
413 #if (SSH_CAMELLIA_128_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
414  "camellia128-cbc",
415 #endif
416 #if (SSH_CAMELLIA_192_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
417  "camellia192-cbc",
418 #endif
419 #if (SSH_CAMELLIA_256_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
420  "camellia256-cbc",
421 #endif
422 #if (SSH_SEED_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
423  "seed-cbc@ssh.com",
424 #endif
425 #if (SSH_3DES_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
426  "3des-ctr",
427 #endif
428 #if (SSH_3DES_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
429  "3des-cbc",
430 #endif
431 #if (SSH_BLOWFISH_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
432  "blowfish-ctr",
433 #endif
434 #if (SSH_BLOWFISH_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
435  "blowfish-cbc",
436 #endif
437 #if (SSH_IDEA_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
438  "idea-ctr",
439 #endif
440 #if (SSH_IDEA_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
441  "idea-cbc",
442 #endif
443 #if (SSH_CAST128_SUPPORT == ENABLED && SSH_CTR_CIPHER_SUPPORT == ENABLED)
444  "cast128-ctr",
445 #endif
446 #if (SSH_CAST128_SUPPORT == ENABLED && SSH_CBC_CIPHER_SUPPORT == ENABLED)
447  "cast128-cbc",
448 #endif
449 #if (SSH_RC4_256_SUPPORT == ENABLED && SSH_STREAM_CIPHER_SUPPORT == ENABLED)
450  "arcfour256",
451 #endif
452 #if (SSH_RC4_128_SUPPORT == ENABLED && SSH_STREAM_CIPHER_SUPPORT == ENABLED)
453  "arcfour128",
454 #endif
455 #if (SSH_RC4_SUPPORT == ENABLED && SSH_STREAM_CIPHER_SUPPORT == ENABLED)
456  "arcfour",
457 #endif
458 };
459 
460 
461 /**
462  * @brief List of supported MAC algorithms
463  **/
464 
465 static const char_t *const sshSupportedMacAlgos[] =
466 {
467 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED && \
468  SSH_ETM_SUPPORT == ENABLED)
469  "hmac-sha2-256-etm@openssh.com",
470 #endif
471 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_SHA256_SUPPORT == ENABLED)
472  "hmac-sha2-256",
473 #endif
474 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED && \
475  SSH_ETM_SUPPORT == ENABLED)
476  "hmac-sha2-512-etm@openssh.com",
477 #endif
478 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_SHA512_SUPPORT == ENABLED)
479  "hmac-sha2-512",
480 #endif
481 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED && \
482  SSH_ETM_SUPPORT == ENABLED)
483  "hmac-sha1-etm@openssh.com",
484 #endif
485 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_SHA1_SUPPORT == ENABLED)
486  "hmac-sha1",
487 #endif
488 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_RIPEMD160_SUPPORT == ENABLED && \
489  SSH_ETM_SUPPORT == ENABLED)
490  "hmac-ripemd160-etm@openssh.com",
491 #endif
492 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_RIPEMD160_SUPPORT == ENABLED)
493  "hmac-ripemd160",
494  "hmac-ripemd160@openssh.com",
495 #endif
496 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_MD5_SUPPORT == ENABLED && \
497  SSH_ETM_SUPPORT == ENABLED)
498  "hmac-md5-etm@openssh.com",
499 #endif
500 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_MD5_SUPPORT == ENABLED)
501  "hmac-md5",
502 #endif
503 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_SHA1_96_SUPPORT == ENABLED && \
504  SSH_ETM_SUPPORT == ENABLED)
505  "hmac-sha1-96-etm@openssh.com",
506 #endif
507 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_SHA1_96_SUPPORT == ENABLED)
508  "hmac-sha1-96",
509 #endif
510 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_MD5_96_SUPPORT == ENABLED && \
511  SSH_ETM_SUPPORT == ENABLED)
512  "hmac-md5-96-etm@openssh.com",
513 #endif
514 #if (SSH_HMAC_SUPPORT == ENABLED && SSH_MD5_96_SUPPORT == ENABLED)
515  "hmac-md5-96",
516 #endif
517 #if (SSH_AES_128_SUPPORT == ENABLED && SSH_RFC5647_SUPPORT == ENABLED)
518  "AEAD_AES_128_GCM",
519 #endif
520 #if (SSH_AES_256_SUPPORT == ENABLED && SSH_RFC5647_SUPPORT == ENABLED)
521  "AEAD_AES_256_GCM",
522 #endif
523 #if (SSH_CAMELLIA_128_SUPPORT == ENABLED && SSH_RFC5647_SUPPORT == ENABLED)
524  "AEAD_CAMELLIA_128_GCM",
525 #endif
526 #if (SSH_CAMELLIA_256_SUPPORT == ENABLED && SSH_RFC5647_SUPPORT == ENABLED)
527  "AEAD_CAMELLIA_256_GCM",
528 #endif
529  ""
530 };
531 
532 
533 /**
534  * @brief List of supported compression algorithms
535  **/
536 
537 static const char_t *const sshSupportedCompressionAlgos[] =
538 {
539  "none"
540 };
541 
542 
543 /**
544  * @brief Format the list of key exchange algorithms
545  * @param[in] connection Pointer to the SSH connection
546  * @param[out] p Output stream where to write the name-list
547  * @param[out] written Total number of bytes that have been written
548  * @return Error code
549  **/
550 
552  size_t *written)
553 {
554  uint_t i;
555  size_t n;
556  bool_t acceptable;
557 
558  //The algorithm name-list is represented as a uint32 containing its length
559  //followed by a comma-separated list of zero or more names
560  n = sizeof(uint32_t);
561 
562  //Loop through the list of key exchange algorithms
563  for(i = 0; i < arraysize(sshSupportedKexAlgos); i++)
564  {
565  //Initialize flag
566  acceptable = FALSE;
567 
568 #if (SSH_RSA_KEX_SUPPORT == ENABLED)
569  //RSA key exchange algorithm?
570  if(connection->context->mode == SSH_OPERATION_MODE_SERVER &&
571  sshIsRsaKexAlgo(sshSupportedKexAlgos[i]))
572  {
573  //RSA algorithms can only be negotiated at server-side if a valid
574  //transient RSA key has been loaded
575  if(sshSelectTransientRsaKey(connection->context,
576  sshSupportedKexAlgos[i]) >= 0)
577  {
578  acceptable = TRUE;
579  }
580  }
581  else
582 #endif
583 #if (SSH_DH_GEX_KEX_SUPPORT == ENABLED)
584  //DH GEX key exchange algorithm?
585  if(connection->context->mode == SSH_OPERATION_MODE_SERVER &&
586  sshIsDhGexKexAlgo(sshSupportedKexAlgos[i]))
587  {
588  //Diffie-Hellman Group Exchange algorithms can only be negotiated at
589  //server-side if a valid group has been loaded
590  if(sshSelectDhGexGroup(connection->context, SSH_MIN_DH_MODULUS_SIZE,
592  {
593  acceptable = TRUE;
594  }
595  }
596  else
597 #endif
598  //Diffie-Hellman or ECDH key exchange algorithm?
599  {
600  //The current key exchange algorithm is acceptable
601  acceptable = TRUE;
602  }
603 
604  //Acceptable key exchange algorithm?
605  if(acceptable)
606  {
607  //Names are separated by commas
608  if(n != sizeof(uint32_t))
609  {
610  p[n++] = ',';
611  }
612 
613  //A name must have a non-zero length and it must not contain a comma
614  osStrcpy((char_t *) p + n, sshSupportedKexAlgos[i]);
615 
616  //Update the length of the name list
617  n += osStrlen(sshSupportedKexAlgos[i]);
618  }
619  }
620 
621 #if (SSH_EXT_INFO_SUPPORT == ENABLED)
622  //Applications implementing the extension negotiation mechanism must add an
623  //indicator name to the field kex_algorithms in the SSH_MSG_KEXINIT message
624  //sent by the application in the first key exchange (refer to RFC 8308,
625  //section 2.1)
626  if(!connection->newKeysSent)
627  {
628  const char_t *indicatorName;
629 
630  //Names are separated by commas
631  if(n != sizeof(uint32_t))
632  {
633  p[n++] = ',';
634  }
635 
636  //The indicator names inserted by the client and server are different
637  //to ensure these names will not produce a match and therefore not
638  //affect the algorithm chosen in key exchange algorithm negotiation
639  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
640  {
641  indicatorName = "ext-info-c";
642  }
643  else
644  {
645  indicatorName = "ext-info-s";
646  }
647 
648  //The indicator name may be added at any position in the name-list
649  osStrcpy((char_t *) p + n, indicatorName);
650 
651  //Update the length of the name list
652  n += osStrlen(indicatorName);
653  }
654 #endif
655 
656 #if (SSH_KEX_STRICT_SUPPORT == ENABLED)
657  //The strict key exchange extension is signaled by including a additional
658  //algorithm in the initial kex_algorithms field
659  if(!connection->newKeysSent)
660  {
661  const char_t *indicatorName;
662 
663  //Names are separated by commas
664  if(n != sizeof(uint32_t))
665  {
666  p[n++] = ',';
667  }
668 
669  //The indicator names inserted by the client and server are different
670  //to ensure these names will not produce a match and therefore not
671  //affect the algorithm chosen in key exchange algorithm negotiation
672  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
673  {
674  indicatorName = "kex-strict-c-v00@openssh.com";
675  }
676  else
677  {
678  indicatorName = "kex-strict-s-v00@openssh.com";
679  }
680 
681  //The indicator name may be added at any position in the name-list
682  osStrcpy((char_t *) p + n, indicatorName);
683 
684  //Update the length of the name list
685  n += osStrlen(indicatorName);
686  }
687 #endif
688 
689  //The name list is preceded by a uint32 containing its length
690  STORE32BE(n - sizeof(uint32_t), p);
691 
692  //Total number of bytes that have been written
693  *written = n;
694 
695  //Successful processing
696  return NO_ERROR;
697 }
698 
699 
700 /**
701  * @brief Format the list of host key algorithms
702  * @param[in] context Pointer to the SSH context
703  * @param[out] p Output stream where to write the name-list
704  * @param[out] written Total number of bytes that have been written
705  * @return Error code
706  **/
707 
709  size_t *written)
710 {
711  uint_t i;
712  size_t n;
713  const SshHostKeyAlgo *entry;
714 
715  //A name-list is represented as a uint32 containing its length followed
716  //by a comma-separated list of zero or more names
717  n = sizeof(uint32_t);
718 
719  //Loop through the supported host key algorithms
720  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos); i++)
721  {
722  //Point to the current entry
723  entry = &sshSupportedHostKeyAlgos[i];
724 
725  //The client lists the algorithms that it is willing to accept. The
726  //server lists the algorithms for which it has host keys (refer to
727  //RFC 4253, section 7.1)
728  if(context->mode == SSH_OPERATION_MODE_CLIENT ||
729  sshSelectHostKey(context, entry->publicKeyAlgo) >= 0)
730  {
731  //Algorithm names are separated by commas
732  if(n != sizeof(uint32_t))
733  {
734  p[n++] = ',';
735  }
736 
737  //A name must have a non-zero length and it must not contain a comma
738  osStrcpy((char_t *) p + n, entry->publicKeyAlgo);
739 
740  //Update the length of the name list
741  n += osStrlen(entry->publicKeyAlgo);
742  }
743  }
744 
745  //The name list is preceded by a uint32 containing its length
746  STORE32BE(n - sizeof(uint32_t), p);
747 
748  //Total number of bytes that have been written
749  *written = n;
750 
751  //Successful processing
752  return NO_ERROR;
753 }
754 
755 
756 /**
757  * @brief Format the list of encryption algorithms
758  * @param[in] context Pointer to the SSH context
759  * @param[out] p Output stream where to write the name-list
760  * @param[out] written Total number of bytes that have been written
761  * @return Error code
762  **/
763 
764 error_t sshFormatEncAlgoList(SshContext *context, uint8_t *p, size_t *written)
765 {
766  //The algorithm name-list must be a comma-separated list of algorithm names.
767  //Each supported algorithm must be listed in order of preference
768  return sshFormatNameList(sshSupportedEncAlgos,
769  arraysize(sshSupportedEncAlgos), p, written);
770 }
771 
772 
773 /**
774  * @brief Format the list of integrity algorithms
775  * @param[in] context Pointer to the SSH context
776  * @param[out] p Output stream where to write the name-list
777  * @param[out] written Total number of bytes that have been written
778  * @return Error code
779  **/
780 
781 error_t sshFormatMacAlgoList(SshContext *context, uint8_t *p, size_t *written)
782 {
783  //The algorithm name-list must be a comma-separated list of algorithm names.
784  //Each supported algorithm must be listed in order of preference
785  return sshFormatNameList(sshSupportedMacAlgos,
786  arraysize(sshSupportedMacAlgos) - 1, p, written);
787 }
788 
789 
790 /**
791  * @brief Format the list of compression algorithms
792  * @param[in] context Pointer to the SSH context
793  * @param[out] p Output stream where to write the name-list
794  * @param[out] written Total number of bytes that have been written
795  * @return Error code
796  **/
797 
799  size_t *written)
800 {
801  //The algorithm name-list must be a comma-separated list of algorithm names.
802  //Each supported algorithm must be listed in order of preference
803  return sshFormatNameList(sshSupportedCompressionAlgos,
804  arraysize(sshSupportedCompressionAlgos), p, written);
805 }
806 
807 
808 /**
809  * @brief Format the list of public key algorithms
810  * @param[in] context Pointer to the SSH context
811  * @param[out] p Output stream where to write the name-list
812  * @param[out] written Total number of bytes that have been written
813  * @return Error code
814  **/
815 
817  size_t *written)
818 {
819  uint_t i;
820  size_t n;
821  const SshHostKeyAlgo *entry;
822 
823  //A name-list is represented as a uint32 containing its length followed
824  //by a comma-separated list of zero or more names
825  n = sizeof(uint32_t);
826 
827  //Enumerate all public key algorithms that are supported
828  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos); i++)
829  {
830  //Point to the current entry
831  entry = &sshSupportedHostKeyAlgos[i];
832 
833  //Algorithm names are separated by commas
834  if(n != sizeof(uint32_t))
835  {
836  p[n++] = ',';
837  }
838 
839  //A name must have a non-zero length and it must not contain a comma
840  osStrcpy((char_t *) p + n, entry->publicKeyAlgo);
841 
842  //Update the length of the name list
843  n += osStrlen(entry->publicKeyAlgo);
844  }
845 
846  //The name list is preceded by a uint32 containing its length
847  STORE32BE(n - sizeof(uint32_t), p);
848 
849  //Total number of bytes that have been written
850  *written = n;
851 
852  //Successful processing
853  return NO_ERROR;
854 }
855 
856 
857 /**
858  * @brief Generic algorithm negotiation
859  * @param[in] context Pointer to the SSH context
860  * @param[in] peerAlgoList List of algorithms supported by the peer
861  * @param[in] supportedAlgoList List of algorithms supported by the entity
862  * @param[in] supportedAlgoListLen Number of items in the name list
863  * @return Name of the selected algorithm, if any
864  **/
865 
866 const char_t *sshSelectAlgo(SshContext *context, const SshNameList *peerAlgoList,
867  const char_t *const *supportedAlgoList, uint_t supportedAlgoListLen)
868 {
869  uint_t i;
870  uint_t j;
871  SshString name;
872  const char_t *selectedAlgo;
873 
874  //Name of the chosen algorithm
875  selectedAlgo = NULL;
876 
877  //Check whether SSH operates as a client or a server
878  if(context->mode == SSH_OPERATION_MODE_CLIENT)
879  {
880  //Loop through the list of algorithms supported by the SSH client
881  for(i = 0; i < supportedAlgoListLen && selectedAlgo == NULL; i++)
882  {
883  //Loop through the list of algorithms offered by the SSH server
884  for(j = 0; selectedAlgo == NULL; j++)
885  {
886  //Algorithm names are separated by commas
887  if(sshGetName(peerAlgoList, j, &name))
888  {
889  //Compare algorithm names
890  if(sshCompareString(&name, supportedAlgoList[i]))
891  {
892  //The chosen algorithm must be the first algorithm on the
893  //client's name list that is also on the server's name list
894  selectedAlgo = supportedAlgoList[i];
895  }
896  }
897  else
898  {
899  //The end of the list was reached
900  break;
901  }
902  }
903  }
904  }
905  else
906  {
907  //Loop through the list of algorithms offered by the SSH client
908  for(j = 0; selectedAlgo == NULL; j++)
909  {
910  //Algorithm names are separated by commas
911  if(sshGetName(peerAlgoList, j, &name))
912  {
913  //Loop through the list of algorithms supported by the SSH server
914  for(i = 0; i < supportedAlgoListLen && selectedAlgo == NULL; i++)
915  {
916  //Compare algorithm names
917  if(sshCompareString(&name, supportedAlgoList[i]))
918  {
919  //The chosen algorithm must be the first algorithm on the
920  //client's name list that is also on the server's name list
921  selectedAlgo = supportedAlgoList[i];
922  }
923  }
924  }
925  else
926  {
927  //The end of the list was reached
928  break;
929  }
930  }
931  }
932 
933  //Return the name of the chosen algorithm, if any
934  return selectedAlgo;
935 }
936 
937 
938 /**
939  * @brief Key exchange algorithm negotiation
940  * @param[in] connection Pointer to the SSH connection
941  * @param[in] peerAlgoList List of algorithms supported by the peer
942  * @return Name of the selected algorithm, if any
943  **/
944 
946  const SshNameList *peerAlgoList)
947 {
948  uint_t i;
949  uint_t j;
950  SshString name;
951  const char_t *selectedAlgo;
952 
953  //Name of the chosen host key algorithm
954  selectedAlgo = NULL;
955 
956  //Check whether SSH operates as a client or a server
957  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
958  {
959  //Loop through the list of algorithms supported by the SSH client
960  for(i = 0; i < arraysize(sshSupportedKexAlgos) &&
961  selectedAlgo == NULL; i++)
962  {
963  //Loop through the list of algorithms offered by the SSH server
964  for(j = 0; selectedAlgo == NULL; j++)
965  {
966  //Algorithm names are separated by commas
967  if(sshGetName(peerAlgoList, j, &name))
968  {
969  //Compare algorithm names
970  if(sshCompareString(&name, sshSupportedKexAlgos[i]))
971  {
972  //The chosen algorithm must be the first algorithm on the
973  //client's name list that is also on the server's name list
974  selectedAlgo = sshSupportedKexAlgos[i];
975  }
976  }
977  else
978  {
979  //The end of the list was reached
980  break;
981  }
982  }
983  }
984  }
985  else
986  {
987  //Loop through the list of algorithms offered by the SSH client
988  for(j = 0; selectedAlgo == NULL; j++)
989  {
990  //Algorithm names are separated by commas
991  if(sshGetName(peerAlgoList, j, &name))
992  {
993  //Loop through the list of algorithms supported by the SSH server
994  for(i = 0; i < arraysize(sshSupportedKexAlgos) &&
995  selectedAlgo == NULL; i++)
996  {
997  //Compare algorithm names
998  if(sshCompareString(&name, sshSupportedKexAlgos[i]))
999  {
1000 #if (SSH_RSA_KEX_SUPPORT == ENABLED)
1001  //RSA key exchange algorithm?
1002  if(sshIsRsaKexAlgo(sshSupportedKexAlgos[i]))
1003  {
1004  //RSA algorithms can only be negotiated at server-side if
1005  //a valid transient RSA key has been loaded
1006  if(sshSelectTransientRsaKey(connection->context,
1007  sshSupportedKexAlgos[i]) >= 0)
1008  {
1009  selectedAlgo = sshSupportedKexAlgos[i];
1010  }
1011  }
1012  else
1013 #endif
1014 #if (SSH_DH_GEX_KEX_SUPPORT == ENABLED)
1015  //DH GEX key exchange algorithm?
1016  if(sshIsDhGexKexAlgo(sshSupportedKexAlgos[i]))
1017  {
1018  //Diffie-Hellman Group Exchange algorithms can only be
1019  //negotiated at server-side if a valid group has been
1020  //loaded
1021  if(sshSelectDhGexGroup(connection->context,
1024  {
1025  selectedAlgo = sshSupportedKexAlgos[i];
1026  }
1027  }
1028  else
1029 #endif
1030  //Diffie-Hellman or ECDH key exchange algorithm?
1031  {
1032  //Select current host key algorithm
1033  selectedAlgo = sshSupportedKexAlgos[i];
1034  }
1035  }
1036  }
1037  }
1038  else
1039  {
1040  //The end of the list was reached
1041  break;
1042  }
1043  }
1044  }
1045 
1046 #if (SSH_EXT_INFO_SUPPORT == ENABLED)
1047  //Applications implementing the extension negotiation mechanism must add an
1048  //indicator name to the field kex_algorithms in the SSH_MSG_KEXINIT message
1049  //sent by the application in the first key exchange (refer to RFC 8308,
1050  //section 2.1)
1051  if(!connection->newKeysSent)
1052  {
1053  const char_t *indicatorName;
1054 
1055  //The indicator names inserted by the client and server are different
1056  //to ensure these names will not produce a match and therefore not
1057  //affect the algorithm chosen in key exchange algorithm negotiation
1058  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
1059  {
1060  indicatorName = "ext-info-s";
1061  }
1062  else
1063  {
1064  indicatorName = "ext-info-c";
1065  }
1066 
1067  //The indicator name may be added at any position in the name-list
1068  if(sshFindName(peerAlgoList, indicatorName) >= 0)
1069  {
1070  connection->extInfoReceived = TRUE;
1071  }
1072  else
1073  {
1074  connection->extInfoReceived = FALSE;
1075  }
1076  }
1077 #endif
1078 
1079 #if (SSH_KEX_STRICT_SUPPORT == ENABLED)
1080  //The strict key exchange extension is signaled by including a additional
1081  //algorithm in the initial kex_algorithms field
1082  if(!connection->newKeysSent)
1083  {
1084  const char_t *indicatorName;
1085 
1086  //The indicator names inserted by the client and server are different
1087  //to ensure these names will not produce a match and therefore not
1088  //affect the algorithm chosen in key exchange algorithm negotiation
1089  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
1090  {
1091  indicatorName = "kex-strict-s-v00@openssh.com";
1092  }
1093  else
1094  {
1095  indicatorName = "kex-strict-c-v00@openssh.com";
1096  }
1097 
1098  //The indicator name may be added at any position in the name-list
1099  if(sshFindName(peerAlgoList, indicatorName) >= 0)
1100  {
1101  connection->kexStrictReceived = TRUE;
1102  }
1103  else
1104  {
1105  connection->kexStrictReceived = FALSE;
1106  }
1107  }
1108 #endif
1109 
1110  //Return the name of the chosen host key algorithm, if any
1111  return selectedAlgo;
1112 }
1113 
1114 
1115 /**
1116  * @brief Host key algorithm negotiation
1117  * @param[in] context Pointer to the SSH context
1118  * @param[in] peerAlgoList List of algorithms supported by the peer
1119  * @return Name of the selected algorithm, if any
1120  **/
1121 
1123  const SshNameList *peerAlgoList)
1124 {
1125  uint_t i;
1126  uint_t j;
1127  SshString name;
1128  const char_t *selectedAlgo;
1129  const SshHostKeyAlgo *entry;
1130 
1131  //Name of the chosen host key algorithm
1132  selectedAlgo = NULL;
1133 
1134  //Check whether SSH operates as a client or a server
1135  if(context->mode == SSH_OPERATION_MODE_CLIENT)
1136  {
1137  //Loop through the list of algorithms supported by the SSH client
1138  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1139  selectedAlgo == NULL; i++)
1140  {
1141  //Point to the current entry
1142  entry = &sshSupportedHostKeyAlgos[i];
1143 
1144  //Loop through the list of algorithms offered by the SSH server
1145  for(j = 0; selectedAlgo == NULL; j++)
1146  {
1147  //Algorithm names are separated by commas
1148  if(sshGetName(peerAlgoList, j, &name))
1149  {
1150  //Compare algorithm names
1151  if(sshCompareString(&name, entry->publicKeyAlgo))
1152  {
1153  //The chosen algorithm must be the first algorithm on the
1154  //client's name list that is also on the server's name list
1155  selectedAlgo = entry->publicKeyAlgo;
1156  }
1157  }
1158  else
1159  {
1160  //The end of the list was reached
1161  break;
1162  }
1163  }
1164  }
1165  }
1166  else
1167  {
1168  //Loop through the list of algorithms offered by the SSH client
1169  for(j = 0; selectedAlgo == NULL; j++)
1170  {
1171  //Algorithm names are separated by commas
1172  if(sshGetName(peerAlgoList, j, &name))
1173  {
1174  //Loop through the list of algorithms supported by the SSH server
1175  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1176  selectedAlgo == NULL; i++)
1177  {
1178  //Point to the current entry
1179  entry = &sshSupportedHostKeyAlgos[i];
1180 
1181  //Compare algorithm names
1182  if(sshCompareString(&name, entry->publicKeyAlgo))
1183  {
1184  //The chosen algorithm must be the first algorithm on the
1185  //client's name list that is also on the server's name list
1186  if(sshSelectHostKey(context, entry->publicKeyAlgo) >= 0)
1187  {
1188  //Select current host key algorithm
1189  selectedAlgo = entry->publicKeyAlgo;
1190  }
1191  }
1192  }
1193  }
1194  else
1195  {
1196  //The end of the list was reached
1197  break;
1198  }
1199  }
1200  }
1201 
1202  //Return the name of the chosen host key algorithm, if any
1203  return selectedAlgo;
1204 }
1205 
1206 
1207 /**
1208  * @brief Encryption algorithm negotiation
1209  * @param[in] context Pointer to the SSH context
1210  * @param[in] peerAlgoList List of algorithms supported by the peer
1211  * @return Name of the selected algorithm, if any
1212  **/
1213 
1215  const SshNameList *peerAlgoList)
1216 {
1217  //The chosen encryption algorithm to each direction must be the first
1218  //algorithm on the client's name-list that is also on the server's name-list
1219  return sshSelectAlgo(context, peerAlgoList, sshSupportedEncAlgos,
1220  arraysize(sshSupportedEncAlgos));
1221 }
1222 
1223 
1224 /**
1225  * @brief Integrity algorithm negotiation
1226  * @param[in] context Pointer to the SSH context
1227  * @param[in] encAlgo Selected encryption algorithm
1228  * @param[in] peerAlgoList List of algorithms supported by the peer
1229  * @return Name of the selected algorithm, if any
1230  **/
1231 
1232 const char_t *sshSelectMacAlgo(SshContext *context, const char_t *encAlgo,
1233  const SshNameList *peerAlgoList)
1234 {
1235  const char_t *selectedAlgo;
1236 
1237 #if (SSH_GCM_CIPHER_SUPPORT == ENABLED || SSH_CHACHA20_POLY1305_SUPPORT == ENABLED)
1238  //AES-GCM or ChaCha20Poly1305 encryption algorithm?
1239  if(sshCompareAlgo(encAlgo, "aes128-gcm") ||
1240  sshCompareAlgo(encAlgo, "aes128-gcm@openssh.com") ||
1241  sshCompareAlgo(encAlgo, "aes256-gcm") ||
1242  sshCompareAlgo(encAlgo, "aes256-gcm@openssh.com") ||
1243  sshCompareAlgo(encAlgo, "chacha20-poly1305") ||
1244  sshCompareAlgo(encAlgo, "chacha20-poly1305@openssh.com"))
1245  {
1246  //AEAD algorithms offer both encryption and authentication
1247  selectedAlgo = sshSupportedMacAlgos[arraysize(sshSupportedMacAlgos) - 1];
1248  }
1249  else
1250 #endif
1251 #if (SSH_RFC5647_SUPPORT == ENABLED)
1252  //AES-GCM or Camellia-GCM encryption algorithm?
1253  if(sshCompareAlgo(encAlgo, "AEAD_AES_128_GCM") ||
1254  sshCompareAlgo(encAlgo, "AEAD_AES_256_GCM") ||
1255  sshCompareAlgo(encAlgo, "AEAD_CAMELLIA_128_GCM") ||
1256  sshCompareAlgo(encAlgo, "AEAD_CAMELLIA_256_GCM"))
1257  {
1258  //If AES-GCM is selected as the encryption algorithm, it must also be
1259  //selected as the MAC algorithm (refer to RFC 5647, section 5.1)
1260  selectedAlgo = encAlgo;
1261  }
1262  else
1263 #endif
1264  //Non-AEAD encryption algorithm?
1265  {
1266  //The chosen MAC algorithm to each direction must be the first algorithm
1267  //on the client's name-list that is also on the server's name-list
1268  selectedAlgo = sshSelectAlgo(context, peerAlgoList, sshSupportedMacAlgos,
1269  arraysize(sshSupportedMacAlgos) - 1);
1270  }
1271 
1272  //Return the name of the chosen algorithm, if any
1273  return selectedAlgo;
1274 }
1275 
1276 
1277 /**
1278  * @brief Compression algorithm negotiation
1279  * @param[in] context Pointer to the SSH context
1280  * @param[in] peerAlgoList List of algorithms supported by the peer
1281  * @return Name of the selected algorithm, if any
1282  **/
1283 
1285  const SshNameList *peerAlgoList)
1286 {
1287  //The chosen compression algorithm to each direction must be the first
1288  //algorithm on the client's name-list that is also on the server's name-list
1289  return sshSelectAlgo(context, peerAlgoList, sshSupportedCompressionAlgos,
1290  arraysize(sshSupportedCompressionAlgos));
1291 }
1292 
1293 
1294 /**
1295  * @brief Public key algorithm selection
1296  * @param[in] context Pointer to the SSH context
1297  * @param[in] keyFormatId Key format identifier
1298  * @param[in] peerAlgoList List of public key algorithms supported by the
1299  * peer (optional parameter)
1300  * @return Name of the selected algorithm, if any
1301  **/
1302 
1304  const char_t *keyFormatId, const SshNameList *peerAlgoList)
1305 {
1306  uint_t i;
1307  uint_t j;
1308  SshString name;
1309  const char_t *selectedAlgo;
1310  const SshHostKeyAlgo *entry;
1311 
1312  //Name of the chosen public key algorithm
1313  selectedAlgo = NULL;
1314 
1315  //Loop through the list of supported algorithms
1316  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1317  selectedAlgo == NULL; i++)
1318  {
1319  //Point to the current entry
1320  entry = &sshSupportedHostKeyAlgos[i];
1321 
1322  //Check key format identifier
1323  if(sshCompareAlgo(entry->keyFormatId, keyFormatId))
1324  {
1325  //The parameter is optional
1326  if(peerAlgoList != NULL)
1327  {
1328  //Loop through the list of algorithms supported by the peer
1329  for(j = 0; selectedAlgo == NULL; j++)
1330  {
1331  //Algorithm names are separated by commas
1332  if(sshGetName(peerAlgoList, j, &name))
1333  {
1334  //Compare algorithm names
1335  if(sshCompareString(&name, entry->publicKeyAlgo))
1336  {
1337  //Select current public key algorithm
1338  selectedAlgo = entry->publicKeyAlgo;
1339  }
1340  }
1341  else
1342  {
1343  //The end of the list was reached
1344  break;
1345  }
1346  }
1347  }
1348  else
1349  {
1350  //Select current public key algorithm
1351  selectedAlgo = entry->publicKeyAlgo;
1352  }
1353  }
1354  }
1355 
1356  //Return the name of the chosen public key algorithm, if any
1357  return selectedAlgo;
1358 }
1359 
1360 
1361 /**
1362  * @brief Get the key format identifier used by a given public key algorithm
1363  * @param[in] publicKeyAlgo Public key algorithm
1364  * @return Key format identifier
1365  **/
1366 
1367 const char_t *sshGetKeyFormatId(const SshString *publicKeyAlgo)
1368 {
1369  uint_t i;
1370  const char_t *keyFormatId;
1371  const SshHostKeyAlgo *entry;
1372 
1373  //Initialize key format identifier
1374  keyFormatId = NULL;
1375 
1376  //Loop through the list of supported algorithms
1377  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1378  keyFormatId == NULL; i++)
1379  {
1380  //Point to the current entry
1381  entry = &sshSupportedHostKeyAlgos[i];
1382 
1383  //Matching entry?
1384  if(sshCompareString(publicKeyAlgo, entry->publicKeyAlgo))
1385  {
1386  keyFormatId = entry->keyFormatId;
1387  }
1388  }
1389 
1390  //Return the matching key format identifier
1391  return keyFormatId;
1392 }
1393 
1394 
1395 /**
1396  * @brief Get the signature format identifier used by a given public key algorithm
1397  * @param[in] publicKeyAlgo Public key algorithm
1398  * @return Signature format identifier
1399  **/
1400 
1401 const char_t *sshGetSignFormatId(const SshString *publicKeyAlgo)
1402 {
1403  uint_t i;
1404  const char_t *signFormatId;
1405  const SshHostKeyAlgo *entry;
1406 
1407  //Initialize signature format identifier
1408  signFormatId = NULL;
1409 
1410  //Loop through the list of supported algorithms
1411  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1412  signFormatId == NULL; i++)
1413  {
1414  //Point to the current entry
1415  entry = &sshSupportedHostKeyAlgos[i];
1416 
1417  //Matching entry?
1418  if(sshCompareString(publicKeyAlgo, entry->publicKeyAlgo))
1419  {
1420  signFormatId = entry->signFormatId;
1421  }
1422  }
1423 
1424  //Return the matching signature format identifier
1425  return signFormatId;
1426 }
1427 
1428 
1429 /**
1430  * @brief Check whether the other party's guess is correct
1431  * @param[in] context Pointer to the SSH context
1432  * @param[in] kexAlgoList List of key exchange algorithms advertised by the
1433  * other party
1434  * @param[in] hostKeyAlgoList List of host key algorithms advertised by the
1435  * other party
1436  * @return TRUE if the guess is correct else FALSE
1437  **/
1438 
1439 bool_t sshIsGuessCorrect(SshContext *context, const SshNameList *kexAlgoList,
1440  const SshNameList *hostKeyAlgoList)
1441 {
1442  bool_t correct;
1443  SshString preferredKexAlgo;
1444  SshString preferredHostKeyAlgo;
1445 
1446  //The first key exchange algorithm of the list is the preferred algorithm
1447  correct = sshGetName(kexAlgoList, 0, &preferredKexAlgo);
1448 
1449  //Each name-list must contain at least one algorithm name
1450  if(correct)
1451  {
1452  //The first host key algorithm of the list is the preferred algorithm
1453  correct = sshGetName(hostKeyAlgoList, 0, &preferredHostKeyAlgo);
1454  }
1455 
1456  //Each name-list must contain at least one algorithm name
1457  if(correct)
1458  {
1459  //The guess is considered wrong if the key exchange algorithm or the
1460  //host key algorithm is guessed wrong (server and client have different
1461  //preferred algorithm)
1462  if(!sshCompareString(&preferredKexAlgo, sshSupportedKexAlgos[0]) ||
1463  !sshCompareString(&preferredHostKeyAlgo, sshSupportedHostKeyAlgos[0].publicKeyAlgo))
1464  {
1465  correct = FALSE;
1466  }
1467  }
1468 
1469  //Return TRUE if the guess is correct
1470  return correct;
1471 }
1472 
1473 
1474 /**
1475  * @brief Test if the specified algorithm is an RSA key exchange algorithm
1476  * @param[in] kexAlgo Key exchange algorithm name
1477  * @return TRUE if RSA key exchange algorithm, else FALSE
1478  **/
1479 
1481 {
1482  //RSA key exchange algorithm?
1483  if(sshCompareAlgo(kexAlgo, "rsa1024-sha1") ||
1484  sshCompareAlgo(kexAlgo, "rsa2048-sha256"))
1485  {
1486  return TRUE;
1487  }
1488  else
1489  {
1490  return FALSE;
1491  }
1492 }
1493 
1494 
1495 /**
1496  * @brief Test if the specified algorithm is a Diffie-Hellman key exchange algorithm
1497  * @param[in] kexAlgo Key exchange algorithm name
1498  * @return TRUE if Diffie-Hellman key exchange algorithm, else FALSE
1499  **/
1500 
1502 {
1503  //Diffie-Hellman key exchange algorithm?
1504  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group1-sha1") ||
1505  sshCompareAlgo(kexAlgo, "diffie-hellman-group14-sha1") ||
1506  sshCompareAlgo(kexAlgo, "diffie-hellman-group14-sha256") ||
1507  sshCompareAlgo(kexAlgo, "diffie-hellman-group15-sha512") ||
1508  sshCompareAlgo(kexAlgo, "diffie-hellman-group16-sha512") ||
1509  sshCompareAlgo(kexAlgo, "diffie-hellman-group17-sha512") ||
1510  sshCompareAlgo(kexAlgo, "diffie-hellman-group18-sha512"))
1511  {
1512  return TRUE;
1513  }
1514  else
1515  {
1516  return FALSE;
1517  }
1518 }
1519 
1520 
1521 /**
1522  * @brief Test if the specified algorithm is a DH GEX key exchange algorithm
1523  * @param[in] kexAlgo Key exchange algorithm name
1524  * @return TRUE if DH GEX key exchange algorithm, else FALSE
1525  **/
1526 
1528 {
1529  //DH GEX key exchange algorithm?
1530  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha1") ||
1531  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha256") ||
1532  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha224@ssh.com") ||
1533  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha384@ssh.com") ||
1534  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha512@ssh.com"))
1535  {
1536  return TRUE;
1537  }
1538  else
1539  {
1540  return FALSE;
1541  }
1542 }
1543 
1544 
1545 /**
1546  * @brief Test if the specified algorithm is an ECDH key exchange algorithm
1547  * @param[in] kexAlgo Key exchange algorithm name
1548  * @return TRUE if ECDH key exchange algorithm, else FALSE
1549  **/
1550 
1552 {
1553  //ECDH key exchange algorithm?
1554  if(sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp256") ||
1555  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp384") ||
1556  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp521") ||
1557  sshCompareAlgo(kexAlgo, "curve25519-sha256") ||
1558  sshCompareAlgo(kexAlgo, "curve25519-sha256@libssh.org") ||
1559  sshCompareAlgo(kexAlgo, "curve448-sha512"))
1560  {
1561  return TRUE;
1562  }
1563  else
1564  {
1565  return FALSE;
1566  }
1567 }
1568 
1569 
1570 /**
1571  * @brief Test if the specified algorithm is an ML-KEM key exchange algorithm
1572  * @param[in] kexAlgo Key exchange algorithm name
1573  * @return TRUE if ML-KEM key exchange algorithm, else FALSE
1574  **/
1575 
1577 {
1578  //ML-KEM key exchange algorithm?
1579  if(sshCompareAlgo(kexAlgo, "mlkem512-sha256") ||
1580  sshCompareAlgo(kexAlgo, "mlkem768-sha256") ||
1581  sshCompareAlgo(kexAlgo, "mlkem1024-sha384"))
1582  {
1583  return TRUE;
1584  }
1585  else
1586  {
1587  return FALSE;
1588  }
1589 }
1590 
1591 
1592 /**
1593  * @brief Test if the specified algorithm is a PQ-hybrid key exchange algorithm
1594  * @param[in] kexAlgo Key exchange algorithm name
1595  * @return TRUE if PQ-hybrid key exchange algorithm, else FALSE
1596  **/
1597 
1599 {
1600  //Post-quantum hybrid key exchange algorithm?
1601  if(sshCompareAlgo(kexAlgo, "mlkem768nistp256-sha256") ||
1602  sshCompareAlgo(kexAlgo, "mlkem1024nistp384-sha384") ||
1603  sshCompareAlgo(kexAlgo, "mlkem768x25519-sha256") ||
1604  sshCompareAlgo(kexAlgo, "sntrup761x25519-sha512") ||
1605  sshCompareAlgo(kexAlgo, "sntrup761x25519-sha512@openssh.com"))
1606  {
1607  return TRUE;
1608  }
1609  else
1610  {
1611  return FALSE;
1612  }
1613 }
1614 
1615 
1616 /**
1617  * @brief Test if the specified public key algorithm is using certificates
1618  * @param[in] publicKeyAlgo Public key algorithm name
1619  * @return TRUE if the public key algorithm is using certificates, else FALSE
1620  **/
1621 
1623 {
1624  //Check public key algorithm name
1625  if(sshCompareString(publicKeyAlgo, "ssh-rsa-cert") ||
1626  sshCompareString(publicKeyAlgo, "ssh-rsa-cert-v01@openssh.com") ||
1627  sshCompareString(publicKeyAlgo, "rsa-sha2-256-cert-v01@openssh.com") ||
1628  sshCompareString(publicKeyAlgo, "rsa-sha2-512-cert-v01@openssh.com") ||
1629  sshCompareString(publicKeyAlgo, "ssh-dss-cert") ||
1630  sshCompareString(publicKeyAlgo, "ssh-dss-cert-v01@openssh.com") ||
1631  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp256-cert") ||
1632  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp384-cert") ||
1633  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp521-cert") ||
1634  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp256-cert-v01@openssh.com") ||
1635  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp384-cert-v01@openssh.com") ||
1636  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp521-cert-v01@openssh.com") ||
1637  sshCompareString(publicKeyAlgo, "ssh-ed25519-cert") ||
1638  sshCompareString(publicKeyAlgo, "ssh-ed25519-cert-v01@openssh.com") ||
1639  sshCompareString(publicKeyAlgo, "ssh-ed448-cert"))
1640  {
1641  return TRUE;
1642  }
1643  else
1644  {
1645  return FALSE;
1646  }
1647 }
1648 
1649 
1650 /**
1651  * @brief Test if the specified public key algorithm is using X.509 certificates
1652  * @param[in] publicKeyAlgo Public key algorithm name
1653  * @return TRUE if the public key algorithm is using X.509 certificates, else FALSE
1654  **/
1655 
1657 {
1658  //Check public key algorithm name
1659  if(sshCompareString(publicKeyAlgo, "x509v3-ssh-dss") ||
1660  sshCompareString(publicKeyAlgo, "x509v3-ssh-rsa") ||
1661  sshCompareString(publicKeyAlgo, "x509v3-rsa2048-sha256") ||
1662  sshCompareString(publicKeyAlgo, "x509v3-ecdsa-sha2-nistp256") ||
1663  sshCompareString(publicKeyAlgo, "x509v3-ecdsa-sha2-nistp384") ||
1664  sshCompareString(publicKeyAlgo, "x509v3-ecdsa-sha2-nistp521"))
1665  {
1666  return TRUE;
1667  }
1668  else
1669  {
1670  return FALSE;
1671  }
1672 }
1673 
1674 #endif
const char_t * sshSelectEncAlgo(SshContext *context, const SshNameList *peerAlgoList)
Encryption algorithm negotiation.
int bool_t
Definition: compiler_port.h:61
const char_t * publicKeyAlgo
Public key algorithm.
Definition: ssh.h:1162
uint8_t p
Definition: ndp.h:300
#define TRUE
Definition: os_port.h:50
error_t sshFormatKexAlgoList(SshConnection *connection, uint8_t *p, size_t *written)
Format the list of key exchange algorithms.
bool_t sshGetName(const SshNameList *nameList, uint_t index, SshString *name)
Get the element at specified index.
Definition: ssh_misc.c:1353
char_t name[]
error_t sshFormatNameList(const char_t *const nameList[], uint_t nameListLen, uint8_t *p, size_t *written)
Format a comma-separated list of names.
Definition: ssh_misc.c:1462
#define osStrlen(s)
Definition: os_port.h:168
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.
bool_t sshIsDhKexAlgo(const char_t *kexAlgo)
Test if the specified algorithm is a Diffie-Hellman key exchange algorithm.
error_t sshFormatMacAlgoList(SshContext *context, uint8_t *p, size_t *written)
Format the list of integrity algorithms.
bool_t sshCompareString(const SshString *string, const char_t *value)
Compare a binary string against the supplied value.
Definition: ssh_misc.c:1680
const char_t * sshSelectPublicKeyAlgo(SshContext *context, const char_t *keyFormatId, const SshNameList *peerAlgoList)
Public key algorithm selection.
bool_t sshIsCertPublicKeyAlgo(const SshString *publicKeyAlgo)
Test if the specified public key algorithm is using certificates.
error_t sshFormatPublicKeyAlgoList(SshContext *context, uint8_t *p, size_t *written)
Format the list of public key algorithms.
#define FALSE
Definition: os_port.h:46
RSA key exchange.
const char_t * sshGetKeyFormatId(const SshString *publicKeyAlgo)
Get the key format identifier used by a given public key algorithm.
bool_t sshIsMlkemKexAlgo(const char_t *kexAlgo)
Test if the specified algorithm is an ML-KEM key exchange algorithm.
#define SshContext
Definition: ssh.h:870
DH GEX (Diffie-Hellman Group Exchange) key exchange.
bool_t sshIsHybridKexAlgo(const char_t *kexAlgo)
Test if the specified algorithm is a PQ-hybrid key exchange algorithm.
bool_t sshIsDhGexKexAlgo(const char_t *kexAlgo)
Test if the specified algorithm is a DH GEX key exchange algorithm.
error_t
Error codes.
Definition: error.h:43
bool_t sshCompareAlgo(const char_t *name1, const char_t *name2)
Compare algorithm names.
Definition: ssh_misc.c:1747
String containing a comma-separated list of names.
Definition: ssh_types.h:78
@ SSH_OPERATION_MODE_SERVER
Definition: ssh.h:893
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:892
bool_t sshIsRsaKexAlgo(const char_t *kexAlgo)
Test if the specified algorithm is an RSA key exchange algorithm.
error_t sshFormatHostKeyAlgoList(SshContext *context, uint8_t *p, size_t *written)
Format the list of host key algorithms.
const char_t * sshSelectCompressionAlgo(SshContext *context, const SshNameList *peerAlgoList)
Compression algorithm negotiation.
Host key algorithm.
Definition: ssh.h:1161
String.
Definition: ssh_types.h:56
const char_t * sshSelectKexAlgo(SshConnection *connection, const SshNameList *peerAlgoList)
Key exchange algorithm negotiation.
const char_t * keyFormatId
Key format identifier.
Definition: ssh.h:1163
const char_t * sshSelectAlgo(SshContext *context, const SshNameList *peerAlgoList, const char_t *const *supportedAlgoList, uint_t supportedAlgoListLen)
Generic algorithm negotiation.
error_t sshFormatEncAlgoList(SshContext *context, uint8_t *p, size_t *written)
Format the list of encryption algorithms.
char char_t
Definition: compiler_port.h:55
uint8_t n
#define SshConnection
Definition: ssh.h:874
#define SSH_MAX_DH_MODULUS_SIZE
Definition: ssh.h:689
const char_t * sshSelectHostKeyAlgo(SshContext *context, const SshNameList *peerAlgoList)
Host key algorithm negotiation.
#define SSH_MIN_DH_MODULUS_SIZE
Definition: ssh.h:675
bool_t sshIsX509CertPublicKeyAlgo(const SshString *publicKeyAlgo)
Test if the specified public key algorithm is using X.509 certificates.
SSH helper functions.
int_t sshSelectHostKey(SshContext *context, const char_t *hostKeyAlgo)
Select a host key that matches then specified algorithm.
Definition: ssh_misc.c:758
int_t sshSelectTransientRsaKey(SshContext *context, const char_t *kexAlgo)
Select a transient RSA key.
Definition: ssh_kex_rsa.c:744
bool_t sshIsEcdhKexAlgo(const char_t *kexAlgo)
Test if the specified algorithm is an ECDH key exchange algorithm.
unsigned int uint_t
Definition: compiler_port.h:57
Secure Shell (SSH)
int_t sshFindName(const SshNameList *nameList, const char_t *name)
Search a name list for a given name.
Definition: ssh_misc.c:1302
SSH algorithm negotiation.
#define osStrcpy(s1, s2)
Definition: os_port.h:210
bool_t sshIsGuessCorrect(SshContext *context, const SshNameList *kexAlgoList, const SshNameList *hostKeyAlgoList)
Check whether the other party's guess is correct.
const char_t * sshSelectMacAlgo(SshContext *context, const char_t *encAlgo, const SshNameList *peerAlgoList)
Integrity algorithm negotiation.
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
error_t sshFormatCompressionAlgoList(SshContext *context, uint8_t *p, size_t *written)
Format the list of compression algorithms.
const char_t * signFormatId
Signature format identifier.
Definition: ssh.h:1164
@ NO_ERROR
Success.
Definition: error.h:44
#define SSH_PREFERRED_DH_MODULUS_SIZE
Definition: ssh.h:682
Debugging facilities.
#define arraysize(a)
Definition: os_port.h:71
const char_t * sshGetSignFormatId(const SshString *publicKeyAlgo)
Get the signature format identifier used by a given public key algorithm.