ssh_misc.c
Go to the documentation of this file.
1 /**
2  * @file ssh_misc.c
3  * @brief SSH helper functions
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSSH Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SSH_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "ssh/ssh_algorithms.h"
37 #include "ssh/ssh_extensions.h"
38 #include "ssh/ssh_transport.h"
39 #include "ssh/ssh_kex.h"
40 #include "ssh/ssh_kex_rsa.h"
41 #include "ssh/ssh_kex_dh.h"
42 #include "ssh/ssh_kex_dh_gex.h"
43 #include "ssh/ssh_kex_ecdh.h"
44 #include "ssh/ssh_kex_hybrid.h"
45 #include "ssh/ssh_auth.h"
46 #include "ssh/ssh_channel.h"
47 #include "ssh/ssh_packet.h"
48 #include "ssh/ssh_key_material.h"
49 #include "ssh/ssh_key_import.h"
50 #include "ssh/ssh_key_format.h"
51 #include "ssh/ssh_cert_import.h"
52 #include "ssh/ssh_misc.h"
53 #include "ecc/ec_misc.h"
54 #include "debug.h"
55 
56 //Check SSH stack configuration
57 #if (SSH_SUPPORT == ENABLED)
58 
59 
60 /**
61  * @brief Open a new SSH connection
62  * @param[in] context Pointer to the SSH context
63  * @param[in] socket Handle that identifies a socket
64  * @return Handle referencing the newly created SSH connection
65  **/
66 
68 {
69  error_t error;
70  uint_t i;
71  SshConnection *connection;
72 
73  //Initialize handle
74  connection = NULL;
75 
76  //Acquire exclusive access to the SSH context
77  osAcquireMutex(&context->mutex);
78 
79  //Loop through the connection table
80  for(i = 0; i < context->numConnections; i++)
81  {
82  //Unused SSH connection?
83  if(context->connections[i].state == SSH_CONN_STATE_CLOSED)
84  {
85  connection = &context->connections[i];
86  break;
87  }
88  }
89 
90  //Valid connection handle?
91  if(connection != NULL)
92  {
93  //Clear the structure describing the connection
94  osMemset(connection, 0, sizeof(SshConnection));
95 
96  //Attach SSH context
97  connection->context = context;
98  //Attach socket handle
99  connection->socket = socket;
100  //Index of the selected host key
101  connection->hostKeyIndex = -1;
102  //Initialize time stamp
103  connection->timestamp = osGetSystemTime();
104 
105  //Initialize status code
106  error = NO_ERROR;
107 
108  //Multiple callbacks may be registered
109  for(i = 0; i < SSH_MAX_CONN_OPEN_CALLBACKS && !error; i++)
110  {
111  //Valid callback function?
112  if(context->connectionOpenCallback[i] != NULL)
113  {
114  //Invoke callback function
115  error = context->connectionOpenCallback[i](connection,
116  context->connectionOpenParam[i]);
117  }
118  }
119 
120  //Check status code
121  if(!error)
122  {
123 #if (SSH_DH_KEX_SUPPORT == ENABLED || SSH_DH_GEX_KEX_SUPPORT == ENABLED)
124  //Initialize Diffie-Hellman context
125  dhInit(&connection->dhContext);
126 #endif
127 #if (SSH_ECDH_KEX_SUPPORT == ENABLED || SSH_HYBRID_KEX_SUPPORT == ENABLED)
128  //Initialize ECDH context
129  ecdhInit(&connection->ecdhContext);
130 #endif
131 #if (SSH_HYBRID_KEX_SUPPORT == ENABLED)
132  //Initialize KEM context
133  kemInit(&connection->kemContext, NULL);
134 #endif
135  //The sequence number is initialized to zero for the first packet (refer
136  //to RFC 4253, section 6.4)
137  osMemset(connection->encryptionEngine.seqNum, 0, 4);
138  osMemset(connection->decryptionEngine.seqNum, 0, 4);
139 
140  //When the connection has been established, both sides must send an
141  //identification string (refer to RFC 4253, section 4.2)
142  if(context->mode == SSH_OPERATION_MODE_CLIENT)
143  {
144  connection->state = SSH_CONN_STATE_CLIENT_ID;
145  }
146  else
147  {
148  connection->state = SSH_CONN_STATE_SERVER_ID;
149  }
150  }
151  else
152  {
153  //Clean up side effects
154  connection->socket = NULL;
155  //Return an invalid handle
156  connection = NULL;
157  }
158  }
159 
160  //Release exclusive access to the SSH context
161  osReleaseMutex(&context->mutex);
162 
163  //Return a handle to the newly created SSH connection
164  return connection;
165 }
166 
167 
168 /**
169  * @brief Close SSH connection
170  * @param[in] connection Pointer to the SSH connection
171  **/
172 
174 {
175  uint_t i;
176  SshContext *context;
177  SshChannel *channel;
178 
179  //Debug message
180  TRACE_INFO("Closing SSH connection...\r\n");
181 
182  //Point to the SSH context
183  context = connection->context;
184 
185  //Acquire exclusive access to the SSH context
186  osAcquireMutex(&context->mutex);
187 
188  //Loop through SSH channels
189  for(i = 0; i < context->numChannels; i++)
190  {
191  //Point to the current SSH channel
192  channel = &context->channels[i];
193 
194  //Multiple channels can be multiplexed into a single connection
195  if(channel->state != SSH_CHANNEL_STATE_UNUSED &&
196  channel->connection == connection)
197  {
198  //Check channel state
199  if(connection->context->mode == SSH_OPERATION_MODE_SERVER &&
200  (channel->closeRequest || !channel->channelSuccessSent))
201  {
202  //Release SSH channel
203  channel->state = SSH_CHANNEL_STATE_UNUSED;
204  }
205  else
206  {
207  //Close SSH channel
208  channel->state = SSH_CHANNEL_STATE_CLOSED;
209  //Update channel related events
210  sshUpdateChannelEvents(&context->channels[i]);
211  }
212  }
213  }
214 
215  //Valid socket handle?
216  if(connection->socket != NULL)
217  {
218  //Close TCP socket
219  socketClose(connection->socket);
220  connection->socket = NULL;
221  }
222 
223  //Deselect the host key
224  connection->hostKeyIndex = -1;
225 
226 #if (SSH_RSA_KEX_SUPPORT == ENABLED)
227  //Release server's host key
228  if(connection->serverHostKey != NULL)
229  {
230  sshFreeMem(connection->serverHostKey);
231  connection->serverHostKey = NULL;
232  connection->serverHostKeyLen = 0;
233  }
234 #endif
235 
236 #if (SSH_DH_KEX_SUPPORT == ENABLED || SSH_DH_GEX_KEX_SUPPORT == ENABLED)
237  //Release Diffie-Hellman context
238  dhFree(&connection->dhContext);
239 #endif
240 #if (SSH_ECDH_KEX_SUPPORT == ENABLED || SSH_HYBRID_KEX_SUPPORT == ENABLED)
241  //Release ECDH context
242  ecdhFree(&connection->ecdhContext);
243 #endif
244 #if (SSH_HYBRID_KEX_SUPPORT == ENABLED)
245  //Release KEM context
246  kemFree(&connection->kemContext);
247 #endif
248 
249  //Release encryption engine
250  sshFreeEncryptionEngine(&connection->encryptionEngine);
251  //Release decryption engine
252  sshFreeEncryptionEngine(&connection->decryptionEngine);
253 
254  //Multiple callbacks may be registered
255  for(i = 0; i < SSH_MAX_CONN_CLOSE_CALLBACKS; i++)
256  {
257  //Valid callback function?
258  if(context->connectionCloseCallback[i] != NULL)
259  {
260  //Invoke callback function
261  context->connectionCloseCallback[i](connection,
262  context->connectionCloseParam[i]);
263  }
264  }
265 
266  //Mark the connection as closed
267  connection->state = SSH_CONN_STATE_CLOSED;
268 
269  //Release exclusive access to the SSH context
270  osReleaseMutex(&context->mutex);
271 }
272 
273 
274 /**
275  * @brief Register connection events
276  * @param[in] context Pointer to the SSH context
277  * @param[in] connection Pointer to the SSH connection
278  * @param[in] eventDesc Socket events to be registered
279  **/
280 
282  SocketEventDesc *eventDesc)
283 {
284  uint_t i;
285 
286  //Register socket handle
287  eventDesc->socket = connection->socket;
288 
289  //On-going packet transfer?
290  if(connection->txBufferPos < connection->txBufferLen)
291  {
292  //Wait until there is more room in the send buffer
293  eventDesc->eventMask = SOCKET_EVENT_TX_READY;
294  }
295  else if(connection->rxBufferLen > 0)
296  {
297  //Wait for data to be available for reading
298  eventDesc->eventMask = SOCKET_EVENT_RX_READY;
299  }
300  else
301  {
302  //Wait for data to be available for reading
303  eventDesc->eventMask = SOCKET_EVENT_RX_READY;
304 
305  //Check the state of the connection
306  if(connection->state == SSH_CONN_STATE_CLIENT_ID ||
307  connection->state == SSH_CONN_STATE_CLIENT_KEX_INIT ||
308  connection->state == SSH_CONN_STATE_KEX_DH_INIT ||
309  connection->state == SSH_CONN_STATE_KEX_DH_GEX_REQUEST ||
310  connection->state == SSH_CONN_STATE_KEX_ECDH_INIT ||
311  connection->state == SSH_CONN_STATE_KEX_HYBRID_INIT ||
312  connection->state == SSH_CONN_STATE_CLIENT_NEW_KEYS ||
313  connection->state == SSH_CONN_STATE_CLIENT_EXT_INFO ||
314  connection->state == SSH_CONN_STATE_SERVICE_REQUEST ||
315  connection->state == SSH_CONN_STATE_USER_AUTH_REQUEST)
316  {
317  //Client operation mode?
318  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
319  {
320  //Wait until there is more room in the send buffer
321  eventDesc->eventMask = SOCKET_EVENT_TX_READY;
322  }
323  }
324  else if(connection->state == SSH_CONN_STATE_SERVER_ID ||
325  connection->state == SSH_CONN_STATE_SERVER_KEX_INIT ||
326  connection->state == SSH_CONN_STATE_KEX_RSA_PUB_KEY ||
327  connection->state == SSH_CONN_STATE_SERVER_NEW_KEYS ||
328  connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_1 ||
329  connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_2 ||
330  connection->state == SSH_CONN_STATE_USER_AUTH_SUCCESS)
331  {
332  //Server operation mode?
333  if(connection->context->mode == SSH_OPERATION_MODE_SERVER)
334  {
335  //Wait until there is more room in the send buffer
336  eventDesc->eventMask = SOCKET_EVENT_TX_READY;
337  }
338  }
339  else if(connection->state == SSH_CONN_STATE_OPEN)
340  {
341  //Loop through SSH channels
342  for(i = 0; i < context->numChannels; i++)
343  {
344  //Multiple channels can be multiplexed into a single connection
345  if(context->channels[i].state != SSH_CHANNEL_STATE_UNUSED &&
346  context->channels[i].connection == connection)
347  {
348  //Register the events related to the current SSH channel
349  sshRegisterChannelEvents(&context->channels[i], eventDesc);
350  }
351  }
352  }
353  else if(connection->state == SSH_CONN_STATE_DISCONNECT)
354  {
355  //Wait until there is more room in the send buffer
356  eventDesc->eventMask = SOCKET_EVENT_TX_READY;
357  }
358  else
359  {
360  //Just for sanity
361  }
362  }
363 }
364 
365 
366 /**
367  * @brief Connection event handler
368  * @param[in] context Pointer to the SSH context
369  * @param[in] connection Pointer to the SSH connection
370  * @return Error code
371  **/
372 
374  SshConnection *connection)
375 {
376  error_t error;
377  uint_t i;
378  size_t n;
379 
380  //Initialize status code
381  error = NO_ERROR;
382 
383  //Update time stamp
384  connection->timestamp = osGetSystemTime();
385 
386  //The SSH Connection Protocol has been designed to run on top of the SSH
387  //transport layer and user authentication protocols
388  if(connection->state == SSH_CONN_STATE_OPEN)
389  {
390  //Loop through SSH channels
391  for(i = 0; i < context->numChannels && !error; i++)
392  {
393  //Multiple channels can be multiplexed into a single connection
394  if(context->channels[i].state != SSH_CHANNEL_STATE_UNUSED &&
395  context->channels[i].connection == connection)
396  {
397  //Check whether the connection is ready for transmission
398  if(connection->txBufferLen == 0 && connection->rxBufferLen == 0)
399  {
400  //Process channel related events
401  error = sshProcessChannelEvents(&context->channels[i]);
402  }
403  }
404  }
405  }
406 
407  //Check status code
408  if(!error)
409  {
410  //On-going packet transmission?
411  if(connection->txBufferPos < connection->txBufferLen)
412  {
413  //Send more data
414  error = socketSend(connection->socket,
415  connection->buffer + connection->txBufferPos,
416  connection->txBufferLen - connection->txBufferPos, &n, 0);
417 
418  //Check status code
419  if(error == NO_ERROR || error == ERROR_TIMEOUT)
420  {
421  //Advance data pointer
422  connection->txBufferPos += n;
423 
424  //Check whether the transmission is complete
425  if(connection->txBufferPos >= connection->txBufferLen)
426  {
427  //Flush transmit buffer
428  connection->txBufferLen = 0;
429  connection->txBufferPos = 0;
430  }
431  }
432  }
433  else
434  {
435 #if (SSH_CLIENT_SUPPORT == ENABLED)
436  //Client operation mode?
437  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
438  {
439  //Check the state of the connection
440  if(connection->state == SSH_CONN_STATE_CLIENT_ID)
441  {
442  //Send client's identification string
443  error = sshSendIdString(connection);
444  }
445  else if(connection->state == SSH_CONN_STATE_CLIENT_KEX_INIT)
446  {
447  //Send SSH_MSG_KEXINIT message
448  error = sshSendKexInit(connection);
449  }
450 #if (SSH_DH_KEX_SUPPORT == ENABLED)
451  else if(connection->state == SSH_CONN_STATE_KEX_DH_INIT)
452  {
453  //Send SSH_MSG_KEX_DH_INIT message
454  error = sshSendKexDhInit(connection);
455  }
456 #endif
457 #if (SSH_DH_GEX_KEX_SUPPORT == ENABLED)
458  else if(connection->state == SSH_CONN_STATE_KEX_DH_GEX_REQUEST)
459  {
460  //Send SSH_MSG_KEY_DH_GEX_REQUEST message
461  error = sshSendKexDhGexRequest(connection);
462  }
463 #endif
464 #if (SSH_ECDH_KEX_SUPPORT == ENABLED)
465  else if(connection->state == SSH_CONN_STATE_KEX_ECDH_INIT)
466  {
467  //Send SSH_MSG_KEX_ECDH_INIT message
468  error = sshSendKexEcdhInit(connection);
469  }
470 #endif
471 #if (SSH_HYBRID_KEX_SUPPORT == ENABLED)
472  else if(connection->state == SSH_CONN_STATE_KEX_HYBRID_INIT)
473  {
474  //Send SSH_MSG_KEX_HYBRID_INIT message
475  error = sshSendKexHybridInit(connection);
476  }
477 #endif
478 #if (SSH_EXT_INFO_SUPPORT == ENABLED)
479  else if(connection->state == SSH_CONN_STATE_CLIENT_EXT_INFO)
480  {
481  //Send SSH_MSG_EXT_INFO message
482  error = sshSendExtInfo(connection);
483  }
484 #endif
485  else if(connection->state == SSH_CONN_STATE_SERVICE_REQUEST)
486  {
487  //Send SSH_MSG_SERVICE_REQUEST message
488  error = sshSendServiceRequest(connection);
489  }
490  else if(connection->state == SSH_CONN_STATE_USER_AUTH_REQUEST)
491  {
492  //Send SSH_MSG_USERAUTH_REQUEST message
493  error = sshSendUserAuthRequest(connection);
494  }
495  else if(connection->state == SSH_CONN_STATE_SERVER_ID ||
496  connection->state == SSH_CONN_STATE_SERVER_KEX_INIT ||
497  connection->state == SSH_CONN_STATE_KEX_RSA_PUB_KEY ||
498  connection->state == SSH_CONN_STATE_KEX_RSA_DONE ||
499  connection->state == SSH_CONN_STATE_KEX_DH_REPLY ||
500  connection->state == SSH_CONN_STATE_KEX_DH_GEX_GROUP ||
501  connection->state == SSH_CONN_STATE_KEX_DH_GEX_REPLY ||
502  connection->state == SSH_CONN_STATE_KEX_ECDH_REPLY ||
503  connection->state == SSH_CONN_STATE_KEX_HYBRID_REPLY ||
504  connection->state == SSH_CONN_STATE_SERVER_NEW_KEYS ||
505  connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_1 ||
506  connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_2 ||
507  connection->state == SSH_CONN_STATE_SERVICE_ACCEPT ||
508  connection->state == SSH_CONN_STATE_USER_AUTH_REPLY ||
509  connection->state == SSH_CONN_STATE_USER_AUTH_SUCCESS ||
510  connection->state == SSH_CONN_STATE_OPEN)
511  {
512  //Receive incoming packet
513  error = sshReceivePacket(connection);
514  }
515  else if(connection->state == SSH_CONN_STATE_DISCONNECT)
516  {
517  //The SSH_MSG_DISCONNECT message causes immediate termination of
518  //the connection
519  error = ERROR_CONNECTION_CLOSING;
520  }
521  else
522  {
523  //Invalid state
524  error = ERROR_WRONG_STATE;
525  }
526  }
527  else
528 #endif
529 #if (SSH_SERVER_SUPPORT == ENABLED)
530  //Server operation mode?
531  if(connection->context->mode == SSH_OPERATION_MODE_SERVER)
532  {
533  //Check the state of the connection
534  if(connection->state == SSH_CONN_STATE_SERVER_ID)
535  {
536  //Send server's identification string
537  error = sshSendIdString(connection);
538  }
539  else if(connection->state == SSH_CONN_STATE_SERVER_KEX_INIT)
540  {
541  //Send SSH_MSG_KEXINIT message
542  error = sshSendKexInit(connection);
543  }
544 #if (SSH_RSA_KEX_SUPPORT == ENABLED)
545  else if(connection->state == SSH_CONN_STATE_KEX_RSA_PUB_KEY)
546  {
547  //Send SSH_MSG_KEXRSA_PUBKEY message
548  error = sshSendKexRsaPubKey(connection);
549  }
550 #endif
551  else if(connection->state == SSH_CONN_STATE_SERVER_NEW_KEYS)
552  {
553  //Send SSH_MSG_NEWKEYS message
554  error = sshSendNewKeys(connection);
555  }
556 #if (SSH_EXT_INFO_SUPPORT == ENABLED)
557  else if(connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_1 ||
558  connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_2)
559  {
560  //Send SSH_MSG_EXT_INFO message
561  error = sshSendExtInfo(connection);
562  }
563 #endif
564  else if(connection->state == SSH_CONN_STATE_USER_AUTH_SUCCESS)
565  {
566  //Send SSH_MSG_USERAUTH_SUCCESS message
567  error = sshSendUserAuthSuccess(connection);
568  }
569  else if(connection->state == SSH_CONN_STATE_CLIENT_ID ||
570  connection->state == SSH_CONN_STATE_CLIENT_KEX_INIT ||
571  connection->state == SSH_CONN_STATE_KEX_RSA_SECRET ||
572  connection->state == SSH_CONN_STATE_KEX_DH_INIT ||
573  connection->state == SSH_CONN_STATE_KEX_DH_GEX_REQUEST ||
574  connection->state == SSH_CONN_STATE_KEX_DH_GEX_INIT ||
575  connection->state == SSH_CONN_STATE_KEX_ECDH_INIT ||
576  connection->state == SSH_CONN_STATE_KEX_HYBRID_INIT ||
577  connection->state == SSH_CONN_STATE_CLIENT_NEW_KEYS ||
578  connection->state == SSH_CONN_STATE_CLIENT_EXT_INFO ||
579  connection->state == SSH_CONN_STATE_SERVICE_REQUEST ||
580  connection->state == SSH_CONN_STATE_USER_AUTH_REQUEST ||
581  connection->state == SSH_CONN_STATE_OPEN)
582  {
583  //Receive incoming packet
584  error = sshReceivePacket(connection);
585  }
586  else if(connection->state == SSH_CONN_STATE_DISCONNECT)
587  {
588  //The SSH_MSG_DISCONNECT message causes immediate termination of
589  //the connection
590  error = ERROR_CONNECTION_CLOSING;
591  }
592  else
593  {
594  //Invalid state
595  error = ERROR_WRONG_STATE;
596  }
597  }
598  else
599 #endif
600  //Invalid operation mode?
601  {
602  //Report an error
603  error = ERROR_FAILURE;
604  }
605  }
606  }
607 
608  //Return status code
609  return error;
610 }
611 
612 
613 /**
614  * @brief Subscribe to the specified channel events
615  * @param[in] channel Handle referencing an SSH channel
616  * @param[in] event Event object used to receive notifications
617  * @param[in] eventMask Logic OR of the requested socket events
618  **/
619 
621  uint_t eventMask)
622 {
623  //Valid channel handle?
624  if(channel != NULL)
625  {
626  //Acquire exclusive access to the SSH context
627  osAcquireMutex(&channel->context->mutex);
628 
629  //An user event may have been previously registered...
630  if(channel->userEvent != NULL)
631  {
632  channel->eventMask |= eventMask;
633  }
634  else
635  {
636  channel->eventMask = eventMask;
637  }
638 
639  //Suscribe to get notified of events
640  channel->userEvent = event;
641  //Update channel related events
642  sshUpdateChannelEvents(channel);
643 
644  //Release exclusive access to the SSH context
645  osReleaseMutex(&channel->context->mutex);
646  }
647 }
648 
649 
650 /**
651  * @brief Unsubscribe previously registered events
652  * @param[in] channel Handle referencing an SSH channel
653  **/
654 
656 {
657  //Valid channel handle?
658  if(channel != NULL)
659  {
660  //Acquire exclusive access to the SSH context
661  osAcquireMutex(&channel->context->mutex);
662 
663  //Unsuscribe channel events
664  channel->userEvent = NULL;
665 
666  //Release exclusive access to the SSH context
667  osReleaseMutex(&channel->context->mutex);
668  }
669 }
670 
671 
672 /**
673  * @brief Retrieve event flags for a specified channel
674  * @param[in] channel Handle referencing an SSH channel
675  * @return Logic OR of events in the signaled state
676  **/
677 
679 {
680  uint_t eventFlags;
681 
682  //Valid channel handle?
683  if(channel != NULL)
684  {
685  //Acquire exclusive access to the SSH context
686  osAcquireMutex(&channel->context->mutex);
687 
688  //Read event flags for the specified socket
689  eventFlags = channel->eventFlags;
690 
691  //Release exclusive access to the SSH context
692  osReleaseMutex(&channel->context->mutex);
693  }
694  else
695  {
696  //The socket handle is not valid
697  eventFlags = 0;
698  }
699 
700  //Return the events in the signaled state
701  return eventFlags;
702 }
703 
704 
705 /**
706  * @brief Notify the SSH context that event is occurring
707  * @param[in] context Pointer to the SSH context
708  **/
709 
711 {
712  //Notify the SSH context that event is occurring
713  osSetEvent(&context->event);
714 }
715 
716 
717 /**
718  * @brief Get the currently selected host key
719  * @param[in] connection Pointer to the SSH connection
720  * @return Pointer to the selected host key
721  **/
722 
724 {
725  SshContext *context;
726  SshHostKey *hostKey;
727 
728  //Point to the SSH context
729  context = connection->context;
730 
731  //No host key is currently selected
732  hostKey = NULL;
733 
734  //Ensure the index is valid
735  if(connection->hostKeyIndex >= 0 &&
736  connection->hostKeyIndex < SSH_MAX_HOST_KEYS)
737  {
738  //Valid host key?
739  if(context->hostKeys[connection->hostKeyIndex].keyFormatId != NULL)
740  {
741  //Point to the selected host key
742  hostKey = &context->hostKeys[connection->hostKeyIndex];
743  }
744  }
745 
746  //Return the selected host key
747  return hostKey;
748 }
749 
750 
751 /**
752  * @brief Select a host key that matches then specified algorithm
753  * @param[in] context Pointer to the SSH context
754  * @param[in] hostKeyAlgo Selected host key algorithm name
755  * @return Index of the selected host key, if any
756  **/
757 
758 int_t sshSelectHostKey(SshContext *context, const char_t *hostKeyAlgo)
759 {
760  int_t i;
761  int_t index;
762  SshString name;
763  SshHostKey *hostKey;
764  const char_t *keyFormatId;
765 
766  //Initialize index
767  index = -1;
768 
769  //Get the name of the selected host key algorithm
770  name.value = hostKeyAlgo;
771  name.length = osStrlen(hostKeyAlgo);
772 
773  //Retrieve the corresponding key format identifier
774  keyFormatId = sshGetKeyFormatId(&name);
775 
776  //Valid key format identifier?
777  if(keyFormatId != NULL)
778  {
779  //Loop through the host keys
780  for(i = 0; i < SSH_MAX_HOST_KEYS && index < 0; i++)
781  {
782  //Point to the current host key
783  hostKey = &context->hostKeys[i];
784 
785  //Valid host key?
786  if(hostKey->keyFormatId != NULL)
787  {
788  //Compare key format identifiers
789  if(sshCompareAlgo(hostKey->keyFormatId, keyFormatId))
790  {
791  //The current host key is acceptable
792  index = i;
793  }
794  }
795  }
796  }
797 
798  //Return the index of the host key
799  return index;
800 }
801 
802 
803 /**
804  * @brief Select the next acceptable host key
805  * @param[in] connection Pointer to the SSH connection
806  * @return Index of the next acceptable host key, if any
807  **/
808 
810 {
811 #if (SSH_CLIENT_SUPPORT == ENABLED)
812  int_t index;
813  SshHostKey *hostKey;
814 
815  //Initialize index
816  index = -1;
817 
818  //Loop through the host keys
819  while(connection->hostKeyIndex < SSH_MAX_HOST_KEYS)
820  {
821  //Increment index
822  if(connection->hostKeyIndex < 0)
823  {
824  connection->hostKeyIndex = 0;
825  }
826  else
827  {
828  connection->hostKeyIndex++;
829  }
830 
831  //Point to the corresponding host key
832  hostKey = sshGetHostKey(connection);
833 
834  //Valid host key?
835  if(hostKey != NULL)
836  {
837  //Make sure the public key algorithm is valid
838  if(hostKey->publicKeyAlgo != NULL)
839  {
840  //The current host key is acceptable
841  index = connection->hostKeyIndex;
842  break;
843  }
844  }
845  }
846 
847  //Return the index of the next acceptable host key, if any
848  return index;
849 #else
850  //Client operation mode is not implemented
851  return -1;
852 #endif
853 }
854 
855 
856 /**
857  * @brief Format host key structure
858  * @param[in] connection Pointer to the SSH connection
859  * @param[out] p Output stream where to write the host key
860  * @param[out] written Total number of bytes that have been written
861  * @return Error code
862  **/
863 
864 error_t sshFormatHostKey(SshConnection *connection, uint8_t *p,
865  size_t *written)
866 {
867  error_t error;
868  SshHostKey *hostKey;
869 
870  //Get the currently selected host key
871  hostKey = sshGetHostKey(connection);
872 
873  //Valid host key?
874  if(hostKey != NULL)
875  {
876 #if (SSH_RSA_SIGN_SUPPORT == ENABLED)
877  //RSA host key?
878  if(sshCompareAlgo(hostKey->keyFormatId, "ssh-rsa"))
879  {
880  RsaPublicKey rsaPublicKey;
881 
882  //Initialize RSA public key
883  rsaInitPublicKey(&rsaPublicKey);
884 
885  //Load RSA public key
886  error = sshImportRsaPublicKey(&rsaPublicKey, hostKey->publicKey,
887  hostKey->publicKeyLen);
888 
889  //Check status code
890  if(!error)
891  {
892  //Format RSA host key structure
893  error = sshFormatRsaPublicKey(&rsaPublicKey, p, written);
894  }
895 
896  //Free previously allocated resources
897  rsaFreePublicKey(&rsaPublicKey);
898  }
899  else
900 #endif
901 #if (SSH_RSA_SIGN_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
902  //RSA certificate?
903  if(sshCompareAlgo(hostKey->keyFormatId, "ssh-rsa-cert-v01@openssh.com"))
904  {
905  //Extract RSA certificate
906  error = sshImportCertificate(hostKey->publicKey, hostKey->publicKeyLen,
907  p, written);
908  }
909  else
910 #endif
911 #if (SSH_DSA_SIGN_SUPPORT == ENABLED)
912  //DSA host key?
913  if(sshCompareAlgo(hostKey->keyFormatId, "ssh-dss"))
914  {
915  DsaPublicKey dsaPublicKey;
916 
917  //Initialize DSA public key
918  dsaInitPublicKey(&dsaPublicKey);
919 
920  //Load DSA public key
921  error = sshImportDsaPublicKey(&dsaPublicKey, hostKey->publicKey,
922  hostKey->publicKeyLen);
923 
924  //Check status code
925  if(!error)
926  {
927  //Format DSA host key structure
928  error = sshFormatDsaPublicKey(&dsaPublicKey, p, written);
929  }
930 
931  //Free previously allocated resources
932  dsaFreePublicKey(&dsaPublicKey);
933  }
934  else
935 #endif
936 #if (SSH_DSA_SIGN_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
937  //DSA certificate?
938  if(sshCompareAlgo(hostKey->keyFormatId, "ssh-dss-cert-v01@openssh.com"))
939  {
940  //Extract DSA certificate
941  error = sshImportCertificate(hostKey->publicKey, hostKey->publicKeyLen,
942  p, written);
943  }
944  else
945 #endif
946 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED)
947  //ECDSA host key?
948  if(sshCompareAlgo(hostKey->keyFormatId, "ecdsa-sha2-nistp256") ||
949  sshCompareAlgo(hostKey->keyFormatId, "ecdsa-sha2-nistp384") ||
950  sshCompareAlgo(hostKey->keyFormatId, "ecdsa-sha2-nistp521"))
951  {
952  EcPublicKey ecPublicKey;
953 
954  //Initialize ECDSA public key
955  ecInitPublicKey(&ecPublicKey);
956 
957  //Load ECDSA public key
958  error = sshImportEcdsaPublicKey(&ecPublicKey, hostKey->publicKey,
959  hostKey->publicKeyLen);
960 
961  //Check status code
962  if(!error)
963  {
964  //Format ECDSA host key structure
965  error = sshFormatEcdsaPublicKey(&ecPublicKey, p, written);
966  }
967 
968  //Free previously allocated resources
969  ecFreePublicKey(&ecPublicKey);
970  }
971  else
972 #endif
973 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
974  //ECDSA certificate?
975  if(sshCompareAlgo(hostKey->keyFormatId, "ecdsa-sha2-nistp256-cert-v01@openssh.com") ||
976  sshCompareAlgo(hostKey->keyFormatId, "ecdsa-sha2-nistp384-cert-v01@openssh.com") ||
977  sshCompareAlgo(hostKey->keyFormatId, "ecdsa-sha2-nistp521-cert-v01@openssh.com"))
978  {
979  //Extract ECDSA certificate
980  error = sshImportCertificate(hostKey->publicKey, hostKey->publicKeyLen,
981  p, written);
982  }
983  else
984 #endif
985 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
986  //Ed25519 host key?
987  if(sshCompareAlgo(hostKey->keyFormatId, "ssh-ed25519"))
988  {
989  EddsaPublicKey eddsaPublicKey;
990 
991  //Initialize EdDSA public key
992  eddsaInitPublicKey(&eddsaPublicKey);
993 
994  //Load EdDSA public key
995  error = sshImportEd25519PublicKey(&eddsaPublicKey, hostKey->publicKey,
996  hostKey->publicKeyLen);
997 
998  //Check status code
999  if(!error)
1000  {
1001  //Format Ed25519 host key structure
1002  error = sshFormatEd25519PublicKey(&eddsaPublicKey, p, written);
1003  }
1004 
1005  //Free previously allocated resources
1006  eddsaFreePublicKey(&eddsaPublicKey);
1007  }
1008  else
1009 #endif
1010 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
1011  //Ed25519 certificate?
1012  if(sshCompareAlgo(hostKey->keyFormatId, "ssh-ed25519-cert-v01@openssh.com"))
1013  {
1014  //Extract Ed25519 certificate
1015  error = sshImportCertificate(hostKey->publicKey, hostKey->publicKeyLen,
1016  p, written);
1017  }
1018  else
1019 #endif
1020 #if (SSH_ED448_SIGN_SUPPORT == ENABLED)
1021  //Ed448 host key?
1022  if(sshCompareAlgo(hostKey->keyFormatId, "ssh-ed448"))
1023  {
1024  EddsaPublicKey eddsaPublicKey;
1025 
1026  //Initialize EdDSA public key
1027  eddsaInitPublicKey(&eddsaPublicKey);
1028 
1029  //Load EdDSA public key
1030  error = sshImportEd448PublicKey(&eddsaPublicKey, hostKey->publicKey,
1031  hostKey->publicKeyLen);
1032 
1033  //Check status code
1034  if(!error)
1035  {
1036  //Format Ed448 host key structure
1037  error = sshFormatEd448PublicKey(&eddsaPublicKey, p, written);
1038  }
1039 
1040  //Free previously allocated resources
1041  eddsaFreePublicKey(&eddsaPublicKey);
1042  }
1043  else
1044 #endif
1045  //Unknown host key type?
1046  {
1047  //Report an error
1048  error = ERROR_INVALID_KEY;
1049  }
1050  }
1051  else
1052  {
1053  //No host key is currently selected
1054  error = ERROR_INVALID_KEY;
1055  }
1056 
1057  //Return status code
1058  return error;
1059 }
1060 
1061 
1062 /**
1063  * @brief Get the elliptic curve that matches the specified key format identifier
1064  * @param[in] keyFormatId Key format identifier
1065  * @param[in] curveName Curve name
1066  * @return Elliptic curve parameters
1067  **/
1068 
1069 const EcCurve *sshGetCurve(const SshString *keyFormatId,
1070  const SshString *curveName)
1071 {
1072  const EcCurve *curve;
1073 
1074 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED)
1075 #if (SSH_NISTP256_SUPPORT == ENABLED)
1076  //NIST P-256 elliptic curve?
1077  if(sshCompareString(keyFormatId, "ecdsa-sha2-nistp256") &&
1078  sshCompareString(curveName, "nistp256"))
1079  {
1080  curve = SECP256R1_CURVE;
1081  }
1082  else
1083 #endif
1084 #if (SSH_NISTP256_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
1085  //NIST P-256 elliptic curve?
1086  if(sshCompareString(keyFormatId, "ecdsa-sha2-nistp256-cert-v01@openssh.com") &&
1087  sshCompareString(curveName, "nistp256"))
1088  {
1089  curve = SECP256R1_CURVE;
1090  }
1091  else
1092 #endif
1093 #if (SSH_NISTP384_SUPPORT == ENABLED)
1094  //NIST P-384 elliptic curve?
1095  if(sshCompareString(keyFormatId, "ecdsa-sha2-nistp384") &&
1096  sshCompareString(curveName, "nistp384"))
1097  {
1098  curve = SECP384R1_CURVE;
1099  }
1100  else
1101 #endif
1102 #if (SSH_NISTP384_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
1103  //NIST P-384 elliptic curve?
1104  if(sshCompareString(keyFormatId, "ecdsa-sha2-nistp384-cert-v01@openssh.com") &&
1105  sshCompareString(curveName, "nistp384"))
1106  {
1107  curve = SECP384R1_CURVE;
1108  }
1109  else
1110 #endif
1111 #if (SSH_NISTP521_SUPPORT == ENABLED)
1112  //NIST P-521 elliptic curve?
1113  if(sshCompareString(keyFormatId, "ecdsa-sha2-nistp521") &&
1114  sshCompareString(curveName, "nistp521"))
1115  {
1116  curve = SECP521R1_CURVE;
1117  }
1118  else
1119 #endif
1120 #if (SSH_NISTP521_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
1121  //NIST P-521 elliptic curve?
1122  if(sshCompareString(keyFormatId, "ecdsa-sha2-nistp521-cert-v01@openssh.com") &&
1123  sshCompareString(curveName, "nistp521"))
1124  {
1125  curve = SECP521R1_CURVE;
1126  }
1127  else
1128 #endif
1129 #endif
1130  //Unknow elliptic curve?
1131  {
1132  curve = NULL;
1133  }
1134 
1135  //Return the elliptic curve parameters, if any
1136  return curve;
1137 }
1138 
1139 
1140 /**
1141  * @brief Parse a string
1142  * @param[in] p Input stream where to read the string
1143  * @param[in] length Number of bytes available in the input stream
1144  * @param[out] string String resulting from the parsing process
1145  * @return Error code
1146  **/
1147 
1148 error_t sshParseString(const uint8_t *p, size_t length, SshString *string)
1149 {
1150  size_t n;
1151 
1152  //Malformed data?
1153  if(length < sizeof(uint32_t))
1154  return ERROR_INVALID_SYNTAX;
1155 
1156  //A string is stored as a uint32 containing its length and zero or more
1157  //bytes that are the value of the string
1158  n = LOAD32BE(p);
1159 
1160  //Point to the value of the string
1161  p += sizeof(uint32_t);
1162  length -= sizeof(uint32_t);
1163 
1164  //Malformed data?
1165  if(length < n)
1166  return ERROR_INVALID_SYNTAX;
1167 
1168  //Save the value of the string
1169  string->value = (char_t *) p;
1170  string->length = n;
1171 
1172  //Successful processing
1173  return NO_ERROR;
1174 }
1175 
1176 
1177 /**
1178  * @brief Parse a binary string
1179  * @param[in] p Input stream where to read the string
1180  * @param[in] length Number of bytes available in the input stream
1181  * @param[out] string Binary string resulting from the parsing process
1182  * @return Error code
1183  **/
1184 
1185 error_t sshParseBinaryString(const uint8_t *p, size_t length,
1186  SshBinaryString *string)
1187 {
1188  size_t n;
1189 
1190  //Malformed data?
1191  if(length < sizeof(uint32_t))
1192  return ERROR_INVALID_SYNTAX;
1193 
1194  //A string is stored as a uint32 containing its length and zero or more
1195  //bytes that are the value of the string
1196  n = LOAD32BE(p);
1197 
1198  //Point to the value of the string
1199  p += sizeof(uint32_t);
1200  length -= sizeof(uint32_t);
1201 
1202  //Malformed data?
1203  if(length < n)
1204  return ERROR_INVALID_SYNTAX;
1205 
1206  //Save the value of the string
1207  string->value = p;
1208  string->length = n;
1209 
1210  //Successful processing
1211  return NO_ERROR;
1212 }
1213 
1214 
1215 /**
1216  * @brief Parse a comma-separated list of names
1217  * @param[in] p Input stream where to read the list
1218  * @param[in] length Number of bytes available in the input stream
1219  * @param[out] nameList Name list resulting from the parsing process
1220  * @return Error code
1221  **/
1222 
1223 error_t sshParseNameList(const uint8_t *p, size_t length,
1224  SshNameList *nameList)
1225 {
1226  size_t i;
1227  size_t n;
1228 
1229  //Malformed data?
1230  if(length < sizeof(uint32_t))
1231  return ERROR_INVALID_SYNTAX;
1232 
1233  //A name-list is represented as a uint32 containing its length followed by
1234  //a comma-separated list of zero or more names
1235  n = LOAD32BE(p);
1236 
1237  //Point to the list of names
1238  p += sizeof(uint32_t);
1239  length -= sizeof(uint32_t);
1240 
1241  //Malformed data?
1242  if(length < n)
1243  return ERROR_INVALID_SYNTAX;
1244 
1245  //Loop through the comma-separated list of names
1246  for(i = 0; i < n; i++)
1247  {
1248  //A name must have a non-zero length (refer to RFC 4251 section 5)
1249  if(i == 0 || i == (n - 1))
1250  {
1251  if(p[i] == ',')
1252  return ERROR_INVALID_SYNTAX;
1253  }
1254  else
1255  {
1256  if(p[i] == ',' && p[i - 1] == ',')
1257  return ERROR_INVALID_SYNTAX;
1258  }
1259 
1260  //Terminating null characters must not be used, neither for the
1261  //individual names, nor for the list as a whole
1262  if(p[i] == '\0')
1263  return ERROR_INVALID_SYNTAX;
1264  }
1265 
1266  //Save the list of names
1267  nameList->value = (char_t *) p;
1268  nameList->length = n;
1269 
1270  //Successful processing
1271  return NO_ERROR;
1272 }
1273 
1274 
1275 /**
1276  * @brief Search a name list for a given name
1277  * @param[in] nameList List of names
1278  * @param[in] name NULL-terminated string containing the name
1279  * @return The index of the name, or -1 if the name does not appear in the
1280  * name list
1281  **/
1282 
1283 int_t sshFindName(const SshNameList *nameList, const char_t *name)
1284 {
1285  size_t i;
1286  size_t j;
1287  uint_t index;
1288  size_t nameLen;
1289 
1290  //Retrieve the length of the name
1291  nameLen = osStrlen(name);
1292 
1293  //Initialize variables
1294  i = 0;
1295  index = 0;
1296 
1297  //Loop through the list of names
1298  for(j = 0; j <= nameList->length; j++)
1299  {
1300  //Names are separated by commas
1301  if(j == nameList->length || nameList->value[j] == ',')
1302  {
1303  //Check the length of the name
1304  if(nameLen == (j - i))
1305  {
1306  //Matching name?
1307  if(osMemcmp(nameList->value + i, name, nameLen) == 0)
1308  {
1309  //Return the index of the name
1310  return index;
1311  }
1312  }
1313 
1314  //Point to the next name of the list
1315  i = j + 1;
1316  //Increment index
1317  index++;
1318  }
1319  }
1320 
1321  //The name does not appear in the name list
1322  return -1;
1323 }
1324 
1325 
1326 /**
1327  * @brief Get the element at specified index
1328  * @param[in] nameList List of names
1329  * @param[in] index Zero-based index of the element to get
1330  * @param[out] name Value of the element
1331  * @return TRUE if the index is valid, else FALSE
1332  **/
1333 
1335 {
1336  size_t i;
1337  size_t j;
1338  uint_t n;
1339 
1340  //Initialize variables
1341  i = 0;
1342  n = 0;
1343 
1344  //Loop through the list of names
1345  for(j = 0; j <= nameList->length; j++)
1346  {
1347  //Names are separated by commas
1348  if(j == nameList->length || nameList->value[j] == ',')
1349  {
1350  //Matching index?
1351  if(n++ == index)
1352  {
1353  //Point to first character of the name
1354  name->value = nameList->value + i;
1355  //Determine the length of the name
1356  name->length = j - i;
1357 
1358  //The index is valid
1359  return TRUE;
1360  }
1361 
1362  //Point to the next name of the list
1363  i = j + 1;
1364  }
1365  }
1366 
1367  //The index is out of range
1368  return FALSE;
1369 }
1370 
1371 
1372 /**
1373  * @brief Format a string
1374  * @param[in] value NULL-terminating string
1375  * @param[out] p Output stream where to write the string
1376  * @param[out] written Total number of bytes that have been written
1377  * @return Error code
1378  **/
1379 
1380 error_t sshFormatString(const char_t *value, uint8_t *p, size_t *written)
1381 {
1382  size_t n;
1383 
1384  //Retrieve the length of the string
1385  n = osStrlen(value);
1386 
1387  //A string is stored as a uint32 containing its length and zero or more
1388  //bytes that are the value of the string
1389  STORE32BE(n, p);
1390 
1391  //Copy the value of the string
1392  osMemcpy(p + sizeof(uint32_t), value, n);
1393 
1394  //Total number of bytes that have been written
1395  *written = sizeof(uint32_t) + n;
1396 
1397  //Successful processing
1398  return NO_ERROR;
1399 }
1400 
1401 
1402 /**
1403  * @brief Format a binary string
1404  * @param[in] value Pointer to the binary string
1405  * @param[in] valueLen Length of the binary string, in bytes
1406  * @param[out] p Output stream where to write the binary string
1407  * @param[out] written Total number of bytes that have been written
1408  * @return Error code
1409  **/
1410 
1411 error_t sshFormatBinaryString(const void *value, size_t valueLen, uint8_t *p,
1412  size_t *written)
1413 {
1414  //A string is stored as a uint32 containing its length and zero or more
1415  //bytes that are the value of the string
1416  STORE32BE(valueLen, p);
1417 
1418  //Copy the value of the string
1419  osMemcpy(p + sizeof(uint32_t), value, valueLen);
1420 
1421  //Total number of bytes that have been written
1422  *written = sizeof(uint32_t) + valueLen;
1423 
1424  //Successful processing
1425  return NO_ERROR;
1426 }
1427 
1428 
1429 /**
1430  * @brief Format a comma-separated list of names
1431  * @param[in] nameList List of names
1432  * @param[in] nameListLen Number of items in the list
1433  * @param[out] p Output stream where to write the name list
1434  * @param[out] written Total number of bytes that have been written
1435  * @return Error code
1436  **/
1437 
1438 error_t sshFormatNameList(const char_t *const nameList[], uint_t nameListLen,
1439  uint8_t *p, size_t *written)
1440 {
1441  uint_t i;
1442  size_t n;
1443 
1444  //A name-list is represented as a uint32 containing its length followed
1445  //by a comma-separated list of zero or more names
1446  n = sizeof(uint32_t);
1447 
1448  //Loop through the list of names
1449  for(i = 0; i < nameListLen; i++)
1450  {
1451  //Names are separated by commas
1452  if(n != sizeof(uint32_t))
1453  {
1454  p[n++] = ',';
1455  }
1456 
1457  //A name must have a non-zero length and it must not contain a comma
1458  osStrcpy((char_t *) p + n, nameList[i]);
1459 
1460  //Update the length of the name list
1461  n += osStrlen(nameList[i]);
1462  }
1463 
1464  //The name list is preceded by a uint32 containing its length
1465  STORE32BE(n - sizeof(uint32_t), p);
1466 
1467  //Total number of bytes that have been written
1468  *written = n;
1469 
1470  //Successful processing
1471  return NO_ERROR;
1472 }
1473 
1474 
1475 /**
1476  * @brief Format a multiple precision integer
1477  * @param[in] value Pointer to a multiple precision integer
1478  * @param[out] p Output stream where to write the multiple precision integer
1479  * @param[out] written Total number of bytes that have been written
1480  * @return Error code
1481  **/
1482 
1483 error_t sshFormatMpint(const Mpi *value, uint8_t *p, size_t *written)
1484 {
1485  error_t error;
1486  size_t n;
1487 
1488  //Retrieve the length of the multiple precision integer
1489  n = mpiGetBitLength(value);
1490 
1491  //The value zero must be stored as a string with zero bytes of data
1492  if(n != 0)
1493  {
1494  //If the most significant bit would be set for a positive number, the
1495  //number must be preceded by a zero byte (refer to RFC 4251, section 5)
1496  n = (n / 8) + 1;
1497  }
1498 
1499  //The value of the multiple precision integer is encoded MSB first.
1500  //Unnecessary leading bytes with the value 0 must not be included
1501  error = mpiExport(value, p + 4, n, MPI_FORMAT_BIG_ENDIAN);
1502 
1503  //Check status code
1504  if(!error)
1505  {
1506  //The integer is preceded by a uint32 containing its length
1507  STORE32BE(n, p);
1508 
1509  //Total number of bytes that have been written
1510  *written = sizeof(uint32_t) + n;
1511  }
1512 
1513  //Return status code
1514  return error;
1515 }
1516 
1517 
1518 /**
1519  * @brief Convert a scalar to mpint representation
1520  * @param[in] value Pointer the integer
1521  * @param[in] length Length of the integer, in words
1522  * @param[out] p Output stream where to write the multiple precision integer
1523  * @param[out] written Total number of bytes that have been written
1524  * @return Error code
1525  **/
1526 
1528  uint8_t *p, size_t *written)
1529 {
1530  error_t error;
1531  size_t n;
1532 
1533  //Retrieve the length of the multiple precision integer
1535 
1536  //The value zero must be stored as a string with zero bytes of data
1537  if(n != 0)
1538  {
1539  //If the most significant bit would be set for a positive number, the
1540  //number must be preceded by a zero byte (refer to RFC 4251, section 5)
1541  n = (n / 8) + 1;
1542  }
1543 
1544  //The value of the multiple precision integer is encoded MSB first.
1545  //Unnecessary leading bytes with the value 0 must not be included
1547 
1548  //Check status code
1549  if(!error)
1550  {
1551  //The integer is preceded by a uint32 containing its length
1552  STORE32BE(n, p);
1553 
1554  //Total number of bytes that have been written
1555  *written = sizeof(uint32_t) + n;
1556  }
1557 
1558  //Return status code
1559  return error;
1560 }
1561 
1562 /**
1563  * @brief Convert a binary string to mpint representation
1564  * @param[in] value Pointer to the binary string (MSB first encoded)
1565  * @param[out] length Length of the binary string, in bytes
1566  * @param[out] p Output stream where to write the mpint representation
1567  * @param[out] written Total number of bytes that have been written
1568  * @return Error code
1569  **/
1570 
1571 error_t sshConvertArrayToMpint(const uint8_t *value, size_t length, uint8_t *p,
1572  size_t *written)
1573 {
1574  size_t n;
1575 
1576  //Unnecessary leading bytes with the value 0 must not be included. The value
1577  //zero must be stored as a string with zero bytes of data (refer to RFC 4251,
1578  //section 5)
1579  while(length > 0 && value[0] == 0)
1580  {
1581  value++;
1582  length--;
1583  }
1584 
1585  //Check whether the most significant bit is set
1586  if(length > 0 && (value[0] & 0x80) != 0)
1587  {
1588  n = 1;
1589  }
1590  else
1591  {
1592  n = 0;
1593  }
1594 
1595  //The value of the multiple precision integer is encoded MSB first
1596  osMemmove(p + 4 + n, value, length);
1597 
1598  //If the most significant bit would be set for a positive number, the
1599  //number must be preceded by a zero byte
1600  if(n != 0)
1601  {
1602  p[4] = 0;
1603  }
1604 
1605  //Update the length of the data
1606  n += length;
1607 
1608  //The integer is preceded by a uint32 containing its length
1609  STORE32BE(n, p);
1610 
1611  //Total number of bytes that have been written
1612  *written = sizeof(uint32_t) + n;
1613 
1614  //Successful processing
1615  return NO_ERROR;
1616 }
1617 
1618 
1619 /**
1620  * @brief Compare a binary string against the supplied value
1621  * @param[in] string Pointer to the binary string
1622  * @param[in] value NULL-terminated string
1623  * @return Comparison result
1624  **/
1625 
1627 {
1628  bool_t res;
1629  size_t n;
1630 
1631  //Initialize flag
1632  res = FALSE;
1633 
1634  //Valid NULL-terminated string?
1635  if(value != NULL)
1636  {
1637  //Determine the length of the string
1638  n = osStrlen(value);
1639 
1640  //Check the length of the binary string
1641  if(string->value != NULL && string->length == n)
1642  {
1643  //Perform string comparison
1644  if(osStrncmp(string->value, value, n) == 0)
1645  {
1646  res = TRUE;
1647  }
1648  }
1649  }
1650 
1651  //Return comparison result
1652  return res;
1653 }
1654 
1655 
1656 /**
1657  * @brief Compare binary strings
1658  * @param[in] string1 Pointer to the first binary string
1659  * @param[in] string2 Pointer to the second binary string
1660  * @return Comparison result
1661  **/
1662 
1663 bool_t sshCompareStrings(const SshString *string1, const SshString *string2)
1664 {
1665  bool_t res;
1666 
1667  //Initialize flag
1668  res = FALSE;
1669 
1670  //Check the length of the binary strings
1671  if(string1->value != NULL && string2->value != NULL &&
1672  string1->length == string2->length)
1673  {
1674  //Perform string comparison
1675  if(osMemcmp(string1->value, string2->value, string2->length) == 0)
1676  {
1677  res = TRUE;
1678  }
1679  }
1680 
1681  //Return comparison result
1682  return res;
1683 }
1684 
1685 
1686 /**
1687  * @brief Compare algorithm names
1688  * @param[in] name1 Name of the first algorithm
1689  * @param[in] name2 Name of the second algorithm
1690  * @return Comparison result
1691  **/
1692 
1693 bool_t sshCompareAlgo(const char_t *name1, const char_t *name2)
1694 {
1695  bool_t res;
1696 
1697  //Initialize flag
1698  res = FALSE;
1699 
1700  //Valid NULL-terminated strings?
1701  if(name1 != NULL && name2 != NULL)
1702  {
1703  //Perform string comparison
1704  if(osStrcmp(name1, name2) == 0)
1705  {
1706  res = TRUE;
1707  }
1708  }
1709 
1710  //Return comparison result
1711  return res;
1712 }
1713 
1714 #endif
error_t socketSend(Socket *socket, const void *data, size_t length, size_t *written, uint_t flags)
Send data to a connected socket.
Definition: socket.c:1491
SSH channel management.
void sshUnregisterUserEvents(SshChannel *channel)
Unsubscribe previously registered events.
Definition: ssh_misc.c:655
#define SSH_MAX_CONN_CLOSE_CALLBACKS
Definition: ssh.h:220
int bool_t
Definition: compiler_port.h:61
void rsaFreePublicKey(RsaPublicKey *key)
Release an RSA public key.
Definition: rsa.c:113
SSH user authentication protocol.
@ SSH_CONN_STATE_USER_AUTH_REPLY
Definition: ssh.h:1060
@ SSH_CONN_STATE_OPEN
Definition: ssh.h:1062
error_t sshConvertScalarToMpint(const uint32_t *value, uint_t length, uint8_t *p, size_t *written)
Convert a scalar to mpint representation.
Definition: ssh_misc.c:1527
#define SECP521R1_CURVE
Definition: ec_curves.h:53
void sshUpdateChannelEvents(SshChannel *channel)
Update SSH channel related events.
Definition: ssh_channel.c:386
error_t ecScalarExport(const uint32_t *a, uint_t n, uint8_t *output, size_t length, EcScalarFormat format)
Integer to octet string conversion.
Definition: ec_misc.c:150
void sshFreeEncryptionEngine(SshEncryptionEngine *encryptionEngine)
Release encryption engine.
Arbitrary precision integer.
Definition: mpi.h:102
signed int int_t
Definition: compiler_port.h:56
error_t sshSendKexDhInit(SshConnection *connection)
Send SSH_MSG_KEX_DH_INIT message.
Definition: ssh_kex_dh.c:59
#define LOAD32BE(p)
Definition: cpu_endian.h:210
@ SSH_CONN_STATE_KEX_HYBRID_INIT
Definition: ssh.h:1049
Binary string.
Definition: ssh_types.h:67
Diffie-Hellman key exchange.
@ SSH_CONN_STATE_KEX_DH_GEX_REQUEST
Definition: ssh.h:1043
@ SSH_CONN_STATE_SERVER_EXT_INFO_2
Definition: ssh.h:1055
@ SSH_CONN_STATE_KEX_RSA_SECRET
Definition: ssh.h:1039
ECDH key exchange.
#define SECP384R1_CURVE
Definition: ec_curves.h:52
uint8_t p
Definition: ndp.h:300
@ SSH_CONN_STATE_SERVICE_ACCEPT
Definition: ssh.h:1057
#define TRUE
Definition: os_port.h:50
#define SECP256R1_CURVE
Definition: ec_curves.h:51
SshConnection * sshOpenConnection(SshContext *context, Socket *socket)
Open a new SSH connection.
Definition: ssh_misc.c:67
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:2067
Event object.
uint_t ecScalarGetBitLength(const uint32_t *a, uint_t n)
Get the actual length in bits.
Definition: ec_misc.c:273
void ecdhFree(EcdhContext *context)
Release ECDH context.
Definition: ecdh.c:65
bool_t sshGetName(const SshNameList *nameList, uint_t index, SshString *name)
Get the element at specified index.
Definition: ssh_misc.c:1334
void kemInit(KemContext *context, const KemAlgo *kemAlgo)
Initialize KEM context.
Definition: kem.c:48
error_t sshParseString(const uint8_t *p, size_t length, SshString *string)
Parse a string.
Definition: ssh_misc.c:1148
SSH transport layer protocol.
#define osMemcmp(p1, p2, length)
Definition: os_port.h:156
char_t name[]
error_t sshFormatNameList(const char_t *const nameList[], uint_t nameListLen, uint8_t *p, size_t *written)
Format a comma-separated list of names.
Definition: ssh_misc.c:1438
size_t length
Definition: ssh_types.h:58
#define osStrcmp(s1, s2)
Definition: os_port.h:174
#define osStrlen(s)
Definition: os_port.h:168
SSH key file import functions.
error_t sshFormatEd448PublicKey(const EddsaPublicKey *publicKey, uint8_t *p, size_t *written)
Format an Ed448 public host key.
const uint8_t res[]
@ SSH_CONN_STATE_KEX_DH_GEX_GROUP
Definition: ssh.h:1044
void kemFree(KemContext *context)
Release KEM context.
Definition: kem.c:62
@ SSH_CONN_STATE_KEX_HYBRID_REPLY
Definition: ssh.h:1050
@ SSH_CONN_STATE_SERVER_EXT_INFO_1
Definition: ssh.h:1054
Structure describing socket events.
Definition: socket.h:432
bool_t sshCompareString(const SshString *string, const char_t *value)
Compare a binary string against the supplied value.
Definition: ssh_misc.c:1626
error_t sshFormatRsaPublicKey(const RsaPublicKey *publicKey, uint8_t *p, size_t *written)
Format an RSA public host key.
@ ERROR_WRONG_STATE
Definition: error.h:210
@ SSH_CONN_STATE_DISCONNECT
Definition: ssh.h:1063
error_t sshSendNewKeys(SshConnection *connection)
Send SSH_MSG_NEWKEYS message.
Definition: ssh_kex.c:194
error_t sshFormatBinaryString(const void *value, size_t valueLen, uint8_t *p, size_t *written)
Format a binary string.
Definition: ssh_misc.c:1411
Key material generation.
#define FALSE
Definition: os_port.h:46
RSA key exchange.
error_t sshConvertArrayToMpint(const uint8_t *value, size_t length, uint8_t *p, size_t *written)
Convert a binary string to mpint representation.
Definition: ssh_misc.c:1571
const char_t * sshGetKeyFormatId(const SshString *publicKeyAlgo)
Get the key format identifier used by a given public key algorithm.
error_t sshSendKexEcdhInit(SshConnection *connection)
Send SSH_MSG_KEX_ECDH_INIT message.
Definition: ssh_kex_ecdh.c:58
DSA public key.
Definition: dsa.h:61
#define SshContext
Definition: ssh.h:870
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
const char_t * keyFormatId
Key format identifier.
Definition: ssh.h:1144
DH GEX (Diffie-Hellman Group Exchange) key exchange.
const char_t * value
Definition: ssh_types.h:57
error_t
Error codes.
Definition: error.h:43
void ecInitPublicKey(EcPublicKey *key)
Initialize an EC public key.
Definition: ec.c:52
bool_t sshCompareAlgo(const char_t *name1, const char_t *name2)
Compare algorithm names.
Definition: ssh_misc.c:1693
String containing a comma-separated list of names.
Definition: ssh_types.h:78
EdDSA public key.
Definition: eddsa.h:64
int_t socket(int_t family, int_t type, int_t protocol)
Create a socket that is bound to a specific transport service provider.
Definition: bsd_socket.c:65
@ SOCKET_EVENT_TX_READY
Definition: socket.h:175
@ SSH_OPERATION_MODE_SERVER
Definition: ssh.h:893
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:892
const char_t * publicKey
Public key (PEM, SSH2 or OpenSSH format)
Definition: ssh.h:1145
const EcCurve * sshGetCurve(const SshString *keyFormatId, const SshString *curveName)
Get the elliptic curve that matches the specified key format identifier.
Definition: ssh_misc.c:1069
@ SSH_CONN_STATE_KEX_ECDH_INIT
Definition: ssh.h:1047
int_t sshSelectNextHostKey(SshConnection *connection)
Select the next acceptable host key.
Definition: ssh_misc.c:809
error_t sshImportDsaPublicKey(DsaPublicKey *publicKey, const char_t *input, size_t length)
Decode an SSH public key file containing a DSA public key.
Helper routines for ECC.
RSA public key.
Definition: rsa.h:57
error_t sshFormatEd25519PublicKey(const EddsaPublicKey *publicKey, uint8_t *p, size_t *written)
Format an Ed25519 public host key.
void dhFree(DhContext *context)
Release Diffie-Hellman context.
Definition: dh.c:71
Host key.
Definition: ssh.h:1143
error_t sshProcessConnectionEvents(SshContext *context, SshConnection *connection)
Connection event handler.
Definition: ssh_misc.c:373
void sshCloseConnection(SshConnection *connection)
Close SSH connection.
Definition: ssh_misc.c:173
@ SSH_CONN_STATE_KEX_ECDH_REPLY
Definition: ssh.h:1048
@ SSH_CONN_STATE_KEX_RSA_PUB_KEY
Definition: ssh.h:1038
@ SSH_CONN_STATE_CLIENT_ID
Definition: ssh.h:1034
size_t publicKeyLen
Length of the public key.
Definition: ssh.h:1146
error_t sshSendUserAuthSuccess(SshConnection *connection)
Send SSH_MSG_USERAUTH_SUCCESS message.
Definition: ssh_auth.c:171
SSH key formatting.
error_t mpiExport(const Mpi *a, uint8_t *output, size_t length, MpiFormat format)
Integer to octet string conversion.
Definition: mpi.c:809
error_t sshParseNameList(const uint8_t *p, size_t length, SshNameList *nameList)
Parse a comma-separated list of names.
Definition: ssh_misc.c:1223
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
#define SSH_MAX_HOST_KEYS
Definition: ssh.h:178
@ SSH_CONN_STATE_SERVER_NEW_KEYS
Definition: ssh.h:1052
String.
Definition: ssh_types.h:56
SSH key exchange.
error_t sshProcessChannelEvents(SshChannel *channel)
Channel event handler.
Definition: ssh_channel.c:232
uint_t sshGetUserEvents(SshChannel *channel)
Retrieve event flags for a specified channel.
Definition: ssh_misc.c:678
error_t sshSendExtInfo(SshConnection *connection)
Send SSH_MSG_EXT_INFO message.
error_t sshFormatDsaPublicKey(const DsaPublicKey *publicKey, uint8_t *p, size_t *written)
Format a DSA public host key.
error_t sshImportCertificate(const char_t *input, size_t inputLen, uint8_t *output, size_t *outputLen)
Import SSH certificate (OpenSSH format)
@ SSH_CONN_STATE_CLOSED
Definition: ssh.h:1033
error_t sshSendIdString(SshConnection *connection)
Send identification string.
Definition: ssh_transport.c:51
const char_t * value
Definition: ssh_types.h:79
@ ERROR_CONNECTION_CLOSING
Definition: error.h:78
uint_t mpiGetBitLength(const Mpi *a)
Get the actual length in bits.
Definition: mpi.c:254
@ SSH_CONN_STATE_USER_AUTH_SUCCESS
Definition: ssh.h:1061
error_t sshFormatMpint(const Mpi *value, uint8_t *p, size_t *written)
Format a multiple precision integer.
Definition: ssh_misc.c:1483
@ SSH_CONN_STATE_CLIENT_NEW_KEYS
Definition: ssh.h:1051
void sshNotifyEvent(SshContext *context)
Notify the SSH context that event is occurring.
Definition: ssh_misc.c:710
error_t sshSendKexDhGexRequest(SshConnection *connection)
Send SSH_MSG_KEX_DH_GEX_REQUEST message.
EC public key.
Definition: ec.h:421
void ecdhInit(EcdhContext *context)
Initialize ECDH context.
Definition: ecdh.c:49
@ ERROR_TIMEOUT
Definition: error.h:95
char char_t
Definition: compiler_port.h:55
const char_t * publicKeyAlgo
Public key algorithm to use during user authentication.
Definition: ssh.h:1151
#define sshFreeMem(p)
Definition: ssh.h:729
Post-quantum hybrid key exchange.
@ SSH_CONN_STATE_SERVER_KEX_INIT
Definition: ssh.h:1037
@ SSH_CONN_STATE_SERVER_ID
Definition: ssh.h:1035
@ SSH_CONN_STATE_KEX_DH_INIT
Definition: ssh.h:1041
@ SSH_CONN_STATE_CLIENT_EXT_INFO
Definition: ssh.h:1053
@ SOCKET_EVENT_RX_READY
Definition: socket.h:179
error_t sshSendServiceRequest(SshConnection *connection)
Send SSH_MSG_SERVICE_REQUEST message.
uint8_t n
@ EC_SCALAR_FORMAT_BIG_ENDIAN
Definition: ec_misc.h:51
void sshRegisterConnectionEvents(SshContext *context, SshConnection *connection, SocketEventDesc *eventDesc)
Register connection events.
Definition: ssh_misc.c:281
@ SSH_CONN_STATE_KEX_RSA_DONE
Definition: ssh.h:1040
@ SSH_CONN_STATE_SERVICE_REQUEST
Definition: ssh.h:1056
#define SshConnection
Definition: ssh.h:874
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
#define SSH_MAX_CONN_OPEN_CALLBACKS
Definition: ssh.h:213
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
SshHostKey * sshGetHostKey(SshConnection *connection)
Get the currently selected host key.
Definition: ssh_misc.c:723
@ SSH_CONN_STATE_USER_AUTH_REQUEST
Definition: ssh.h:1059
#define Socket
Definition: socket.h:36
@ SSH_CONN_STATE_CLIENT_KEX_INIT
Definition: ssh.h:1036
uint8_t value[]
Definition: tcp.h:376
@ SSH_CONN_STATE_KEX_DH_GEX_REPLY
Definition: ssh.h:1046
error_t sshImportEcdsaPublicKey(EcPublicKey *publicKey, const char_t *input, size_t length)
Decode an SSH public key file containing an ECDSA public key.
SSH certificate import functions.
SSH helper functions.
@ MPI_FORMAT_BIG_ENDIAN
Definition: mpi.h:93
SSH extension negotiation.
void sshRegisterUserEvents(SshChannel *channel, OsEvent *event, uint_t eventMask)
Subscribe to the specified channel events.
Definition: ssh_misc.c:620
size_t length
Definition: ssh_types.h:80
void eddsaFreePublicKey(EddsaPublicKey *key)
Release an EdDSA public key.
Definition: eddsa.c:63
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
error_t sshFormatHostKey(SshConnection *connection, uint8_t *p, size_t *written)
Format host key structure.
Definition: ssh_misc.c:864
error_t sshSendKexHybridInit(SshConnection *connection)
Send SSH_MSG_KEX_HYBRID_INIT message.
@ SSH_CONN_STATE_KEX_DH_REPLY
Definition: ssh.h:1042
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
#define osStrncmp(s1, s2, length)
Definition: os_port.h:180
error_t sshReceivePacket(SshConnection *connection)
Receive SSH packet.
Definition: ssh_packet.c:178
SSH packet encryption/decryption.
error_t sshSendKexInit(SshConnection *connection)
Send SSH_MSG_KEXINIT message.
Definition: ssh_kex.c:59
int_t sshSelectHostKey(SshContext *context, const char_t *hostKeyAlgo)
Select a host key that matches then specified algorithm.
Definition: ssh_misc.c:758
@ SSH_CONN_STATE_KEX_DH_GEX_INIT
Definition: ssh.h:1045
error_t sshParseBinaryString(const uint8_t *p, size_t length, SshBinaryString *string)
Parse a binary string.
Definition: ssh_misc.c:1185
#define EcCurve
Definition: ec.h:346
error_t sshFormatString(const char_t *value, uint8_t *p, size_t *written)
Format a string.
Definition: ssh_misc.c:1380
void sshRegisterChannelEvents(SshChannel *channel, SocketEventDesc *eventDesc)
Register channel events.
Definition: ssh_channel.c:185
error_t sshImportRsaPublicKey(RsaPublicKey *publicKey, const char_t *input, size_t length)
Decode an SSH public key file containing an RSA public key.
Socket * socket
Handle to a socket to monitor.
Definition: socket.h:433
unsigned int uint_t
Definition: compiler_port.h:57
@ SSH_CHANNEL_STATE_CLOSED
Definition: ssh.h:1076
#define osMemset(p, value, length)
Definition: os_port.h:138
Secure Shell (SSH)
int_t sshFindName(const SshNameList *nameList, const char_t *name)
Search a name list for a given name.
Definition: ssh_misc.c:1283
SSH algorithm negotiation.
void eddsaInitPublicKey(EddsaPublicKey *key)
Initialize an EdDSA public key.
Definition: eddsa.c:48
#define osStrcpy(s1, s2)
Definition: os_port.h:210
@ SSH_CHANNEL_STATE_UNUSED
Definition: ssh.h:1073
void dhInit(DhContext *context)
Initialize Diffie-Hellman context.
Definition: dh.c:54
void dsaFreePublicKey(DsaPublicKey *key)
Release a DSA public key.
Definition: dsa.c:119
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
error_t sshSendUserAuthRequest(SshConnection *connection)
Send SSH_MSG_USERAUTH_REQUEST message.
Definition: ssh_auth.c:109
uint_t eventMask
Requested events.
Definition: socket.h:434
void dsaInitPublicKey(DsaPublicKey *key)
Initialize a DSA public key.
Definition: dsa.c:105
@ ERROR_INVALID_KEY
Definition: error.h:106
error_t sshFormatEcdsaPublicKey(const EcPublicKey *publicKey, uint8_t *p, size_t *written)
Format an ECDSA public host key.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define osMemmove(dest, src, length)
Definition: os_port.h:150
void rsaInitPublicKey(RsaPublicKey *key)
Initialize an RSA public key.
Definition: rsa.c:100
error_t sshImportEd448PublicKey(EddsaPublicKey *publicKey, const char_t *input, size_t length)
Decode an SSH public key file containing an Ed448 public key.
#define SshChannel
Definition: ssh.h:878
void ecFreePublicKey(EcPublicKey *key)
Release an EC public key.
Definition: ec.c:68
bool_t sshCompareStrings(const SshString *string1, const SshString *string2)
Compare binary strings.
Definition: ssh_misc.c:1663
systime_t osGetSystemTime(void)
Retrieve system time.
error_t sshSendKexRsaPubKey(SshConnection *connection)
Send SSH_MSG_KEXRSA_PUBKEY message.
Definition: ssh_kex_rsa.c:61
error_t sshImportEd25519PublicKey(EddsaPublicKey *publicKey, const char_t *input, size_t length)
Decode an SSH public key file containing an Ed25519 public key.