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-2024 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.4.4
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 loaded
980  if(sshSelectDhGexGroup(connection->context,
983  {
984  selectedAlgo = sshSupportedKexAlgos[i];
985  }
986  }
987  else
988 #endif
989  //Diffie-Hellman or ECDH key exchange algorithm?
990  {
991  //Select current host key algorithm
992  selectedAlgo = sshSupportedKexAlgos[i];
993  }
994  }
995  }
996  }
997  else
998  {
999  //The end of the list was reached
1000  break;
1001  }
1002  }
1003  }
1004 
1005 #if (SSH_EXT_INFO_SUPPORT == ENABLED)
1006  //Applications implementing the extension negotiation mechanism must add an
1007  //indicator name to the field kex_algorithms in the SSH_MSG_KEXINIT message
1008  //sent by the application in the first key exchange (refer to RFC 8308,
1009  //section 2.1)
1010  if(!connection->newKeysSent)
1011  {
1012  const char_t *indicatorName;
1013 
1014  //The indicator names inserted by the client and server are different
1015  //to ensure these names will not produce a match and therefore not
1016  //affect the algorithm chosen in key exchange algorithm negotiation
1017  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
1018  {
1019  indicatorName = "ext-info-s";
1020  }
1021  else
1022  {
1023  indicatorName = "ext-info-c";
1024  }
1025 
1026  //The indicator name may be added at any position in the name-list
1027  if(sshFindName(peerAlgoList, indicatorName) >= 0)
1028  {
1029  connection->extInfoReceived = TRUE;
1030  }
1031  else
1032  {
1033  connection->extInfoReceived = FALSE;
1034  }
1035  }
1036 #endif
1037 
1038 #if (SSH_KEX_STRICT_SUPPORT == ENABLED)
1039  //The strict key exchange extension is signaled by including a additional
1040  //algorithm in the initial kex_algorithms field
1041  if(!connection->newKeysSent)
1042  {
1043  const char_t *indicatorName;
1044 
1045  //The indicator names inserted by the client and server are different
1046  //to ensure these names will not produce a match and therefore not
1047  //affect the algorithm chosen in key exchange algorithm negotiation
1048  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
1049  {
1050  indicatorName = "kex-strict-s-v00@openssh.com";
1051  }
1052  else
1053  {
1054  indicatorName = "kex-strict-c-v00@openssh.com";
1055  }
1056 
1057  //The indicator name may be added at any position in the name-list
1058  if(sshFindName(peerAlgoList, indicatorName) >= 0)
1059  {
1060  connection->kexStrictReceived = TRUE;
1061  }
1062  else
1063  {
1064  connection->kexStrictReceived = FALSE;
1065  }
1066  }
1067 #endif
1068 
1069  //Return the name of the chosen host key algorithm, if any
1070  return selectedAlgo;
1071 }
1072 
1073 
1074 /**
1075  * @brief Host key algorithm negotiation
1076  * @param[in] context Pointer to the SSH context
1077  * @param[in] peerAlgoList List of algorithms supported by the peer
1078  * @return Name of the selected algorithm, if any
1079  **/
1080 
1082  const SshNameList *peerAlgoList)
1083 {
1084  uint_t i;
1085  uint_t j;
1086  SshString name;
1087  const char_t *selectedAlgo;
1088  const SshHostKeyAlgo *entry;
1089 
1090  //Name of the chosen host key algorithm
1091  selectedAlgo = NULL;
1092 
1093  //Check whether SSH operates as a client or a server
1094  if(context->mode == SSH_OPERATION_MODE_CLIENT)
1095  {
1096  //Loop through the list of algorithms supported by the SSH client
1097  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1098  selectedAlgo == NULL; i++)
1099  {
1100  //Point to the current entry
1101  entry = &sshSupportedHostKeyAlgos[i];
1102 
1103  //Loop through the list of algorithms offered by the SSH server
1104  for(j = 0; selectedAlgo == NULL; j++)
1105  {
1106  //Algorithm names are separated by commas
1107  if(sshGetName(peerAlgoList, j, &name))
1108  {
1109  //Compare algorithm names
1110  if(sshCompareString(&name, entry->publicKeyAlgo))
1111  {
1112  //The chosen algorithm must be the first algorithm on the
1113  //client's name list that is also on the server's name list
1114  selectedAlgo = entry->publicKeyAlgo;
1115  }
1116  }
1117  else
1118  {
1119  //The end of the list was reached
1120  break;
1121  }
1122  }
1123  }
1124  }
1125  else
1126  {
1127  //Loop through the list of algorithms offered by the SSH client
1128  for(j = 0; selectedAlgo == NULL; j++)
1129  {
1130  //Algorithm names are separated by commas
1131  if(sshGetName(peerAlgoList, j, &name))
1132  {
1133  //Loop through the list of algorithms supported by the SSH server
1134  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1135  selectedAlgo == NULL; i++)
1136  {
1137  //Point to the current entry
1138  entry = &sshSupportedHostKeyAlgos[i];
1139 
1140  //Compare algorithm names
1141  if(sshCompareString(&name, entry->publicKeyAlgo))
1142  {
1143  //The chosen algorithm must be the first algorithm on the
1144  //client's name list that is also on the server's name list
1145  if(sshSelectHostKey(context, entry->publicKeyAlgo) >= 0)
1146  {
1147  //Select current host key algorithm
1148  selectedAlgo = entry->publicKeyAlgo;
1149  }
1150  }
1151  }
1152  }
1153  else
1154  {
1155  //The end of the list was reached
1156  break;
1157  }
1158  }
1159  }
1160 
1161  //Return the name of the chosen host key algorithm, if any
1162  return selectedAlgo;
1163 }
1164 
1165 
1166 /**
1167  * @brief Encryption algorithm negotiation
1168  * @param[in] context Pointer to the SSH context
1169  * @param[in] peerAlgoList List of algorithms supported by the peer
1170  * @return Name of the selected algorithm, if any
1171  **/
1172 
1174  const SshNameList *peerAlgoList)
1175 {
1176  //The chosen encryption algorithm to each direction must be the first
1177  //algorithm on the client's name-list that is also on the server's name-list
1178  return sshSelectAlgo(context, peerAlgoList, sshSupportedEncAlgos,
1179  arraysize(sshSupportedEncAlgos));
1180 }
1181 
1182 
1183 /**
1184  * @brief Integrity algorithm negotiation
1185  * @param[in] context Pointer to the SSH context
1186  * @param[in] encAlgo Selected encryption algorithm
1187  * @param[in] peerAlgoList List of algorithms supported by the peer
1188  * @return Name of the selected algorithm, if any
1189  **/
1190 
1191 const char_t *sshSelectMacAlgo(SshContext *context, const char_t *encAlgo,
1192  const SshNameList *peerAlgoList)
1193 {
1194  const char_t *selectedAlgo;
1195 
1196 #if (SSH_GCM_CIPHER_SUPPORT == ENABLED || SSH_CHACHA20_POLY1305_SUPPORT == ENABLED)
1197  //AES-GCM or ChaCha20Poly1305 encryption algorithm?
1198  if(sshCompareAlgo(encAlgo, "aes128-gcm@openssh.com") ||
1199  sshCompareAlgo(encAlgo, "aes256-gcm@openssh.com") ||
1200  sshCompareAlgo(encAlgo, "chacha20-poly1305@openssh.com"))
1201  {
1202  //AEAD algorithms offer both encryption and authentication
1203  selectedAlgo = sshSupportedMacAlgos[arraysize(sshSupportedMacAlgos) - 1];
1204  }
1205  else
1206 #endif
1207 #if (SSH_RFC5647_SUPPORT == ENABLED)
1208  //AES-GCM or Camellia-GCM encryption algorithm?
1209  if(sshCompareAlgo(encAlgo, "AEAD_AES_128_GCM") ||
1210  sshCompareAlgo(encAlgo, "AEAD_AES_256_GCM") ||
1211  sshCompareAlgo(encAlgo, "AEAD_CAMELLIA_128_GCM") ||
1212  sshCompareAlgo(encAlgo, "AEAD_CAMELLIA_256_GCM"))
1213  {
1214  //If AES-GCM is selected as the encryption algorithm, it must also be
1215  //selected as the MAC algorithm (refer to RFC 5647, section 5.1)
1216  selectedAlgo = encAlgo;
1217  }
1218  else
1219 #endif
1220  //Non-AEAD encryption algorithm?
1221  {
1222  //The chosen MAC algorithm to each direction must be the first algorithm
1223  //on the client's name-list that is also on the server's name-list
1224  selectedAlgo = sshSelectAlgo(context, peerAlgoList, sshSupportedMacAlgos,
1225  arraysize(sshSupportedMacAlgos) - 1);
1226  }
1227 
1228  //Return the name of the chosen algorithm, if any
1229  return selectedAlgo;
1230 }
1231 
1232 
1233 /**
1234  * @brief Compression algorithm negotiation
1235  * @param[in] context Pointer to the SSH context
1236  * @param[in] peerAlgoList List of algorithms supported by the peer
1237  * @return Name of the selected algorithm, if any
1238  **/
1239 
1241  const SshNameList *peerAlgoList)
1242 {
1243  //The chosen compression algorithm to each direction must be the first
1244  //algorithm on the client's name-list that is also on the server's name-list
1245  return sshSelectAlgo(context, peerAlgoList, sshSupportedCompressionAlgos,
1246  arraysize(sshSupportedCompressionAlgos));
1247 }
1248 
1249 
1250 /**
1251  * @brief Public key algorithm selection
1252  * @param[in] context Pointer to the SSH context
1253  * @param[in] keyFormatId Key format identifier
1254  * @param[in] peerAlgoList List of public key algorithms supported by the
1255  * peer (optional parameter)
1256  * @return Name of the selected algorithm, if any
1257  **/
1258 
1260  const char_t *keyFormatId, const SshNameList *peerAlgoList)
1261 {
1262  uint_t i;
1263  uint_t j;
1264  SshString name;
1265  const char_t *selectedAlgo;
1266  const SshHostKeyAlgo *entry;
1267 
1268  //Name of the chosen public key algorithm
1269  selectedAlgo = NULL;
1270 
1271  //Loop through the list of supported algorithms
1272  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1273  selectedAlgo == NULL; i++)
1274  {
1275  //Point to the current entry
1276  entry = &sshSupportedHostKeyAlgos[i];
1277 
1278  //Check key format identifier
1279  if(sshCompareAlgo(entry->keyFormatId, keyFormatId))
1280  {
1281  //The parameter is optional
1282  if(peerAlgoList != NULL)
1283  {
1284  //Loop through the list of algorithms supported by the peer
1285  for(j = 0; selectedAlgo == NULL; j++)
1286  {
1287  //Algorithm names are separated by commas
1288  if(sshGetName(peerAlgoList, j, &name))
1289  {
1290  //Compare algorithm names
1291  if(sshCompareString(&name, entry->publicKeyAlgo))
1292  {
1293  //Select current public key algorithm
1294  selectedAlgo = entry->publicKeyAlgo;
1295  }
1296  }
1297  else
1298  {
1299  //The end of the list was reached
1300  break;
1301  }
1302  }
1303  }
1304  else
1305  {
1306  //Select current public key algorithm
1307  selectedAlgo = entry->publicKeyAlgo;
1308  }
1309  }
1310  }
1311 
1312  //Return the name of the chosen public key algorithm, if any
1313  return selectedAlgo;
1314 }
1315 
1316 
1317 /**
1318  * @brief Get the key format identifier used by a given public key algorithm
1319  * @param[in] publicKeyAlgo Public key algorithm
1320  * @return Key format identifier
1321  **/
1322 
1323 const char_t *sshGetKeyFormatId(const SshString *publicKeyAlgo)
1324 {
1325  uint_t i;
1326  const char_t *keyFormatId;
1327  const SshHostKeyAlgo *entry;
1328 
1329  //Initialize key format identifier
1330  keyFormatId = NULL;
1331 
1332  //Loop through the list of supported algorithms
1333  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1334  keyFormatId == NULL; i++)
1335  {
1336  //Point to the current entry
1337  entry = &sshSupportedHostKeyAlgos[i];
1338 
1339  //Matching entry?
1340  if(sshCompareString(publicKeyAlgo, entry->publicKeyAlgo))
1341  {
1342  keyFormatId = entry->keyFormatId;
1343  }
1344  }
1345 
1346  //Return the matching key format identifier
1347  return keyFormatId;
1348 }
1349 
1350 
1351 /**
1352  * @brief Get the signature format identifier used by a given public key algorithm
1353  * @param[in] publicKeyAlgo Public key algorithm
1354  * @return Signature format identifier
1355  **/
1356 
1357 const char_t *sshGetSignFormatId(const SshString *publicKeyAlgo)
1358 {
1359  uint_t i;
1360  const char_t *signFormatId;
1361  const SshHostKeyAlgo *entry;
1362 
1363  //Initialize signature format identifier
1364  signFormatId = NULL;
1365 
1366  //Loop through the list of supported algorithms
1367  for(i = 0; i < arraysize(sshSupportedHostKeyAlgos) &&
1368  signFormatId == NULL; i++)
1369  {
1370  //Point to the current entry
1371  entry = &sshSupportedHostKeyAlgos[i];
1372 
1373  //Matching entry?
1374  if(sshCompareString(publicKeyAlgo, entry->publicKeyAlgo))
1375  {
1376  signFormatId = entry->signFormatId;
1377  }
1378  }
1379 
1380  //Return the matching signature format identifier
1381  return signFormatId;
1382 }
1383 
1384 
1385 /**
1386  * @brief Check whether the other party's guess is correct
1387  * @param[in] context Pointer to the SSH context
1388  * @param[in] kexAlgoList List of key exchange algorithms advertised by the
1389  * other party
1390  * @param[in] hostKeyAlgoList List of host key algorithms advertised by the
1391  * other party
1392  * @return TRUE if the guess is correct else FALSE
1393  **/
1394 
1395 bool_t sshIsGuessCorrect(SshContext *context, const SshNameList *kexAlgoList,
1396  const SshNameList *hostKeyAlgoList)
1397 {
1398  bool_t correct;
1399  SshString preferredKexAlgo;
1400  SshString preferredHostKeyAlgo;
1401 
1402  //The first key exchange algorithm of the list is the preferred algorithm
1403  correct = sshGetName(kexAlgoList, 0, &preferredKexAlgo);
1404 
1405  //Each name-list must contain at least one algorithm name
1406  if(correct)
1407  {
1408  //The first host key algorithm of the list is the preferred algorithm
1409  correct = sshGetName(hostKeyAlgoList, 0, &preferredHostKeyAlgo);
1410  }
1411 
1412  //Each name-list must contain at least one algorithm name
1413  if(correct)
1414  {
1415  //The guess is considered wrong if the key exchange algorithm or the
1416  //host key algorithm is guessed wrong (server and client have different
1417  //preferred algorithm)
1418  if(!sshCompareString(&preferredKexAlgo, sshSupportedKexAlgos[0]) ||
1419  !sshCompareString(&preferredHostKeyAlgo, sshSupportedHostKeyAlgos[0].publicKeyAlgo))
1420  {
1421  correct = FALSE;
1422  }
1423  }
1424 
1425  //Return TRUE if the guess is correct
1426  return correct;
1427 }
1428 
1429 
1430 /**
1431  * @brief Test if the specified algorithm is an RSA key exchange algorithm
1432  * @param[in] kexAlgo Key exchange algorithm name
1433  * @return TRUE if RSA key exchange algorithm, else FALSE
1434  **/
1435 
1437 {
1438  //RSA key exchange algorithm?
1439  if(sshCompareAlgo(kexAlgo, "rsa1024-sha1") ||
1440  sshCompareAlgo(kexAlgo, "rsa2048-sha256"))
1441  {
1442  return TRUE;
1443  }
1444  else
1445  {
1446  return FALSE;
1447  }
1448 }
1449 
1450 
1451 /**
1452  * @brief Test if the specified algorithm is a Diffie-Hellman key exchange algorithm
1453  * @param[in] kexAlgo Key exchange algorithm name
1454  * @return TRUE if Diffie-Hellman key exchange algorithm, else FALSE
1455  **/
1456 
1458 {
1459  //Diffie-Hellman key exchange algorithm?
1460  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group1-sha1") ||
1461  sshCompareAlgo(kexAlgo, "diffie-hellman-group14-sha1") ||
1462  sshCompareAlgo(kexAlgo, "diffie-hellman-group14-sha256") ||
1463  sshCompareAlgo(kexAlgo, "diffie-hellman-group15-sha512") ||
1464  sshCompareAlgo(kexAlgo, "diffie-hellman-group16-sha512") ||
1465  sshCompareAlgo(kexAlgo, "diffie-hellman-group17-sha512") ||
1466  sshCompareAlgo(kexAlgo, "diffie-hellman-group18-sha512"))
1467  {
1468  return TRUE;
1469  }
1470  else
1471  {
1472  return FALSE;
1473  }
1474 }
1475 
1476 
1477 /**
1478  * @brief Test if the specified algorithm is a DH GEX key exchange algorithm
1479  * @param[in] kexAlgo Key exchange algorithm name
1480  * @return TRUE if DH GEX key exchange algorithm, else FALSE
1481  **/
1482 
1484 {
1485  //DH GEX key exchange algorithm?
1486  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha1") ||
1487  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha256") ||
1488  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha224@ssh.com") ||
1489  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha384@ssh.com") ||
1490  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha512@ssh.com"))
1491  {
1492  return TRUE;
1493  }
1494  else
1495  {
1496  return FALSE;
1497  }
1498 }
1499 
1500 
1501 /**
1502  * @brief Test if the specified algorithm is an ECDH key exchange algorithm
1503  * @param[in] kexAlgo Key exchange algorithm name
1504  * @return TRUE if ECDH key exchange algorithm, else FALSE
1505  **/
1506 
1508 {
1509  //ECDH key exchange algorithm?
1510  if(sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp256") ||
1511  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp384") ||
1512  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp521") ||
1513  sshCompareAlgo(kexAlgo, "curve25519-sha256") ||
1514  sshCompareAlgo(kexAlgo, "curve25519-sha256@libssh.org") ||
1515  sshCompareAlgo(kexAlgo, "curve448-sha512"))
1516  {
1517  return TRUE;
1518  }
1519  else
1520  {
1521  return FALSE;
1522  }
1523 }
1524 
1525 
1526 /**
1527  * @brief Test if the specified algorithm is a PQ-hybrid key exchange algorithm
1528  * @param[in] kexAlgo Key exchange algorithm name
1529  * @return TRUE if PQ-hybrid key exchange algorithm, else FALSE
1530  **/
1531 
1533 {
1534  //Post-quantum hybrid key exchange algorithm?
1535  if(sshCompareAlgo(kexAlgo, "mlkem768nistp256-sha256") ||
1536  sshCompareAlgo(kexAlgo, "mlkem1024nistp384-sha384") ||
1537  sshCompareAlgo(kexAlgo, "mlkem768x25519-sha256") ||
1538  sshCompareAlgo(kexAlgo, "sntrup761x25519-sha512") ||
1539  sshCompareAlgo(kexAlgo, "sntrup761x25519-sha512@openssh.com"))
1540  {
1541  return TRUE;
1542  }
1543  else
1544  {
1545  return FALSE;
1546  }
1547 }
1548 
1549 
1550 /**
1551  * @brief Test if the specified public key algorithm is using certificates
1552  * @param[in] publicKeyAlgo Public key algorithm name
1553  * @return TRUE if the public key algorithm is using certificates, else FALSE
1554  **/
1555 
1557 {
1558  //Check public key algorithm name
1559  if(sshCompareString(publicKeyAlgo, "ssh-dss-cert-v01@openssh.com") ||
1560  sshCompareString(publicKeyAlgo, "ssh-rsa-cert-v01@openssh.com") ||
1561  sshCompareString(publicKeyAlgo, "rsa-sha2-256-cert-v01@openssh.com") ||
1562  sshCompareString(publicKeyAlgo, "rsa-sha2-512-cert-v01@openssh.com") ||
1563  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp256-cert-v01@openssh.com") ||
1564  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp384-cert-v01@openssh.com") ||
1565  sshCompareString(publicKeyAlgo, "ecdsa-sha2-nistp521-cert-v01@openssh.com") ||
1566  sshCompareString(publicKeyAlgo, "ssh-ed25519-cert-v01@openssh.com"))
1567  {
1568  return TRUE;
1569  }
1570  else
1571  {
1572  return FALSE;
1573  }
1574 }
1575 
1576 
1577 /**
1578  * @brief Test if the specified public key algorithm is using X.509 certificates
1579  * @param[in] publicKeyAlgo Public key algorithm name
1580  * @return TRUE if the public key algorithm is using X.509 certificates, else FALSE
1581  **/
1582 
1584 {
1585  //Check public key algorithm name
1586  if(sshCompareString(publicKeyAlgo, "x509v3-ssh-dss") ||
1587  sshCompareString(publicKeyAlgo, "x509v3-ssh-rsa") ||
1588  sshCompareString(publicKeyAlgo, "x509v3-rsa2048-sha256") ||
1589  sshCompareString(publicKeyAlgo, "x509v3-ecdsa-sha2-nistp256") ||
1590  sshCompareString(publicKeyAlgo, "x509v3-ecdsa-sha2-nistp384") ||
1591  sshCompareString(publicKeyAlgo, "x509v3-ecdsa-sha2-nistp521"))
1592  {
1593  return TRUE;
1594  }
1595  else
1596  {
1597  return FALSE;
1598  }
1599 }
1600 
1601 #endif
const char_t * sshSelectEncAlgo(SshContext *context, const SshNameList *peerAlgoList)
Encryption algorithm negotiation.
int bool_t
Definition: compiler_port.h:53
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:1338
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:1442
#define osStrlen(s)
Definition: os_port.h:165
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:1586
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:1653
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:48
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:757
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:50
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:1287
SSH algorithm negotiation.
#define osStrcpy(s1, s2)
Definition: os_port.h:207
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.