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