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