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