ssh_kex_rsa.c
Go to the documentation of this file.
1 /**
2  * @file ssh_kex_rsa.c
3  * @brief RSA key exchange
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2026 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.6.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_transport.h"
38 #include "ssh/ssh_kex.h"
39 #include "ssh/ssh_kex_rsa.h"
40 #include "ssh/ssh_packet.h"
41 #include "ssh/ssh_key_material.h"
42 #include "ssh/ssh_exchange_hash.h"
43 #include "ssh/ssh_key_import.h"
44 #include "ssh/ssh_key_format.h"
45 #include "ssh/ssh_key_verify.h"
46 #include "ssh/ssh_cert_verify.h"
47 #include "ssh/ssh_misc.h"
48 #include "pkix/pem_key_import.h"
49 #include "debug.h"
50 
51 //Check SSH stack configuration
52 #if (SSH_SUPPORT == ENABLED && SSH_RSA_KEX_SUPPORT == ENABLED)
53 
54 
55 /**
56  * @brief Send SSH_MSG_KEXRSA_PUBKEY message
57  * @param[in] connection Pointer to the SSH connection
58  * @return Error code
59  **/
60 
62 {
63 #if (SSH_SERVER_SUPPORT == ENABLED)
64  error_t error;
65  size_t length;
66  uint8_t *message;
67 
68  //Point to the buffer where to format the message
69  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
70 
71  //Select a transient RSA key (K_T) that matches MINKLEN requirement
72  connection->rsaKeyIndex = sshSelectTransientRsaKey(connection->context,
73  connection->kexAlgo);
74 
75  //Acceptable RSA key found?
76  if(connection->rsaKeyIndex >= 0)
77  {
78  //Format SSH_MSG_KEXRSA_PUBKEY message
79  error = sshFormatKexRsaPubKey(connection, message, &length);
80  }
81  else
82  {
83  //Report an error
84  error = ERROR_INVALID_KEY;
85  }
86 
87  //Check status code
88  if(!error)
89  {
90  //Debug message
91  TRACE_INFO("Sending SSH_MSG_KEXRSA_PUBKEY message (%" PRIuSIZE " bytes)...\r\n", length);
93 
94  //Send message
95  error = sshSendPacket(connection, message, length);
96  }
97 
98  //Check status code
99  if(!error)
100  {
101  //The client responds with an SSH_MSG_KEXRSA_SECRET message
102  connection->state = SSH_CONN_STATE_KEX_RSA_SECRET;
103  }
104 
105  //Return status code
106  return error;
107 #else
108  //Server operation mode is not implemented
109  return ERROR_NOT_IMPLEMENTED;
110 #endif
111 }
112 
113 
114 /**
115  * @brief Send SSH_MSG_KEXRSA_SECRET message
116  * @param[in] connection Pointer to the SSH connection
117  * @param[in] transientRsaPublicKey Transient RSA public key (K_T)
118  * @return Error code
119  **/
120 
122  const SshBinaryString *transientRsaPublicKey)
123 {
124 #if (SSH_CLIENT_SUPPORT == ENABLED)
125  error_t error;
126  size_t length;
127  uint8_t *message;
128 
129  //Point to the buffer where to format the message
130  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
131 
132  //Format SSH_MSG_KEXRSA_SECRET message
133  error = sshFormatKexRsaSecret(connection, transientRsaPublicKey,
134  message, &length);
135 
136  //Check status code
137  if(!error)
138  {
139  //Debug message
140  TRACE_INFO("Sending SSH_MSG_KEXRSA_SECRET message (%" PRIuSIZE " bytes)...\r\n", length);
142 
143  //Send message
144  error = sshSendPacket(connection, message, length);
145  }
146 
147  //Check status code
148  if(!error)
149  {
150  //The server responds with an SSH_MSG_KEXRSA_DONE message
151  connection->state = SSH_CONN_STATE_KEX_RSA_DONE;
152  }
153 
154  //Return status code
155  return error;
156 #else
157  //Client operation mode is not implemented
158  return ERROR_NOT_IMPLEMENTED;
159 #endif
160 }
161 
162 
163 /**
164  * @brief Send SSH_MSG_KEXRSA_DONE message
165  * @param[in] connection Pointer to the SSH connection
166  * @return Error code
167  **/
168 
170 {
171 #if (SSH_SERVER_SUPPORT == ENABLED)
172  error_t error;
173  size_t length;
174  uint8_t *message;
175 
176  //Point to the buffer where to format the message
177  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
178 
179  //Format SSH_MSG_KEXRSA_DONE message
180  error = sshFormatKexRsaDone(connection, message, &length);
181 
182  //Check status code
183  if(!error)
184  {
185  //Debug message
186  TRACE_INFO("Sending SSH_MSG_KEXRSA_DONE message (%" PRIuSIZE " bytes)...\r\n", length);
188 
189  //Send message
190  error = sshSendPacket(connection, message, length);
191  }
192 
193  //Check status code
194  if(!error)
195  {
196  //Key exchange ends by each side sending an SSH_MSG_NEWKEYS message
197  connection->state = SSH_CONN_STATE_SERVER_NEW_KEYS;
198  }
199 
200  //Return status code
201  return error;
202 #else
203  //Server operation mode is not implemented
204  return ERROR_NOT_IMPLEMENTED;
205 #endif
206 }
207 
208 
209 /**
210  * @brief Format SSH_MSG_KEXRSA_PUBKEY message
211  * @param[in] connection Pointer to the SSH connection
212  * @param[out] p Buffer where to format the message
213  * @param[out] length Length of the resulting message, in bytes
214  * @return Error code
215  **/
216 
218  size_t *length)
219 {
220 #if (SSH_SERVER_SUPPORT == ENABLED)
221  error_t error;
222  size_t n;
223 
224  //Total length of the message
225  *length = 0;
226 
227  //Set message type
229 
230  //Point to the first field of the message
231  p += sizeof(uint8_t);
232  *length += sizeof(uint8_t);
233 
234  //Format server's public host key (K_S)
235  error = sshFormatHostKey(connection, p + sizeof(uint32_t), &n);
236  //Any error to report?
237  if(error)
238  return error;
239 
240  //The octet string value is preceded by a uint32 containing its length
241  STORE32BE(n, p);
242 
243  //Point to the next field
244  p += sizeof(uint32_t) + n;
245  *length += sizeof(uint32_t) + n;
246 
247  //Format transient RSA public key (K_T)
248  error = sshFormatTransientRsaPublicKey(connection, p + sizeof(uint32_t), &n);
249  //Any error to report?
250  if(error)
251  return error;
252 
253  //Update exchange hash H with K_T (transient RSA public key)
254  error = sshUpdateExchangeHash(connection, p + sizeof(uint32_t), n);
255  //Any error to report?
256  if(error)
257  return error;
258 
259  //The octet string value is preceded by a uint32 containing its length
260  STORE32BE(n, p);
261 
262  //Total length of the message
263  *length += sizeof(uint32_t) + n;
264 
265  //Successful processing
266  return NO_ERROR;
267 #else
268  //Server operation mode is not implemented
269  return ERROR_NOT_IMPLEMENTED;
270 #endif
271 }
272 
273 
274 /**
275  * @brief Format SSH_MSG_KEXRSA_SECRET message
276  * @param[in] connection Pointer to the SSH connection
277  * @param[in] transientRsaPublicKey Transient RSA public key (K_T)
278  * @param[out] p Buffer where to format the message
279  * @param[out] length Length of the resulting message, in bytes
280  * @return Error code
281  **/
282 
284  const SshBinaryString *transientRsaPublicKey, uint8_t *p, size_t *length)
285 {
286 #if (SSH_CLIENT_SUPPORT == ENABLED)
287  error_t error;
288  size_t n;
289 
290  //Total length of the message
291  *length = 0;
292 
293  //Set message type
295 
296  //Point to the first field of the message
297  p += sizeof(uint8_t);
298  *length += sizeof(uint8_t);
299 
300  //The client uses K_T to encrypt K using RSAES-OAEP
301  error = sshEncryptSharedSecret(connection, transientRsaPublicKey,
302  p + sizeof(uint32_t), &n);
303  //Any error to report?
304  if(error)
305  return error;
306 
307  //Update exchange hash H with the encrypted secret
308  error = sshUpdateExchangeHash(connection, p + sizeof(uint32_t), n);
309  //Any error to report?
310  if(error)
311  return error;
312 
313  //The octet string value is preceded by a uint32 containing its length
314  STORE32BE(n, p);
315 
316  //Total length of the message
317  *length += sizeof(uint32_t) + n;
318 
319  //Successful processing
320  return NO_ERROR;
321 #else
322  //Client operation mode is not implemented
323  return ERROR_NOT_IMPLEMENTED;
324 #endif
325 }
326 
327 
328 /**
329  * @brief Format SSH_MSG_KEXRSA_DONE message
330  * @param[in] connection Pointer to the SSH connection
331  * @param[out] p Buffer where to format the message
332  * @param[out] length Length of the resulting message, in bytes
333  * @return Error code
334  **/
335 
337  size_t *length)
338 {
339 #if (SSH_SERVER_SUPPORT == ENABLED)
340  error_t error;
341  size_t n;
342 
343  //Total length of the message
344  *length = 0;
345 
346  //Set message type
347  p[0] = SSH_MSG_KEXRSA_DONE;
348 
349  //Point to the first field of the message
350  p += sizeof(uint8_t);
351  *length += sizeof(uint8_t);
352 
353  //Update exchange hash H with K (shared secret)
354  error = sshUpdateExchangeHashRaw(connection, connection->k,
355  connection->kLen);
356  //Any error to report?
357  if(error)
358  return error;
359 
360  //Compute the signature on the exchange hash
361  error = sshGenerateExchangeHashSignature(connection, p + sizeof(uint32_t),
362  &n);
363  //Any error to report?
364  if(error)
365  return error;
366 
367  //The octet string value is preceded by a uint32 containing its length
368  STORE32BE(n, p);
369 
370  //Total length of the message
371  *length += sizeof(uint32_t) + n;
372 
373  //Successful processing
374  return NO_ERROR;
375 #else
376  //Server operation mode is not implemented
377  return ERROR_NOT_IMPLEMENTED;
378 #endif
379 }
380 
381 
382 /**
383  * @brief Parse SSH_MSG_KEXRSA_PUBKEY message
384  * @param[in] connection Pointer to the SSH connection
385  * @param[in] message Pointer to message
386  * @param[in] length Length of the message, in bytes
387  * @return Error code
388  **/
389 
390 error_t sshParseKexRsaPubKey(SshConnection *connection, const uint8_t *message,
391  size_t length)
392 {
393 #if (SSH_CLIENT_SUPPORT == ENABLED)
394  error_t error;
395  const uint8_t *p;
396  SshString hostKeyAlgo;
397  SshBinaryString hostKey;
398  SshBinaryString transientRsaPublicKey;
399 
400  //Debug message
401  TRACE_INFO("SSH_MSG_KEXRSA_PUBKEY message received (%" PRIuSIZE " bytes)...\r\n", length);
403 
404  //Check operation mode
405  if(connection->context->mode != SSH_OPERATION_MODE_CLIENT)
407 
408  //Check connection state
409  if(connection->state != SSH_CONN_STATE_KEX_RSA_PUB_KEY)
411 
412  //Sanity check
413  if(length < sizeof(uint8_t))
414  return ERROR_INVALID_MESSAGE;
415 
416  //Point to the first field of the message
417  p = message + sizeof(uint8_t);
418  //Remaining bytes to process
419  length -= sizeof(uint8_t);
420 
421  //Decode server's public host key (K_S)
422  error = sshParseBinaryString(p, length, &hostKey);
423  //Any error to report?
424  if(error)
425  return error;
426 
427  //Point to the next field
428  p += sizeof(uint32_t) + hostKey.length;
429  length -= sizeof(uint32_t) + hostKey.length;
430 
431  //Decode transient RSA public key (K_T)
432  error = sshParseBinaryString(p, length, &transientRsaPublicKey);
433  //Any error to report?
434  if(error)
435  return error;
436 
437  //Point to the next field
438  p += sizeof(uint32_t) + transientRsaPublicKey.length;
439  length -= sizeof(uint32_t) + transientRsaPublicKey.length;
440 
441  //Malformed message?
442  if(length != 0)
443  return ERROR_INVALID_MESSAGE;
444 
445  //Get the selected server's host key algorithm
446  hostKeyAlgo.value = connection->serverHostKeyAlgo;
447  hostKeyAlgo.length = osStrlen(connection->serverHostKeyAlgo);
448 
449 #if (SSH_CERT_SUPPORT == ENABLED)
450  //Certificate-based authentication?
451  if(sshIsCertPublicKeyAlgo(&hostKeyAlgo))
452  {
453  //Verify server's certificate
454  error = sshVerifyServerCertificate(connection, &hostKeyAlgo, &hostKey);
455  }
456  else
457 #endif
458  {
459  //Verify server's host key
460  error = sshVerifyServerHostKey(connection, &hostKeyAlgo, &hostKey);
461  }
462 
463  //If the client fails to verify the server's host key, it should disconnect
464  //from the server by sending an SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE
465  //message
466  if(error)
467  return ERROR_INVALID_KEY;
468 
469  //Allocate a buffer to store the server's host key
470  connection->serverHostKey = sshAllocMem(hostKey.length);
471  //Failed to allocate memory?
472  if(connection->serverHostKey == NULL)
473  return ERROR_OUT_OF_MEMORY;
474 
475  //The server's host key will be used to verify the signature in the
476  //SSH_MSG_KEXRSA_DONE message
477  osMemcpy(connection->serverHostKey, hostKey.value, hostKey.length);
478  connection->serverHostKeyLen = hostKey.length;
479 
480  //Update exchange hash H with K_S (server's public host key)
481  error = sshUpdateExchangeHash(connection, hostKey.value, hostKey.length);
482  //Any error to report?
483  if(error)
484  return error;
485 
486  //Update exchange hash H with K_T (transient RSA public key)
487  error = sshUpdateExchangeHash(connection, transientRsaPublicKey.value,
488  transientRsaPublicKey.length);
489  //Any error to report?
490  if(error)
491  return error;
492 
493  //The client responds with an SSH_MSG_KEXRSA_SECRET message
494  return sshSendKexRsaSecret(connection, &transientRsaPublicKey);
495 #else
496  //Client operation mode is not implemented
498 #endif
499 }
500 
501 
502 /**
503  * @brief Parse SSH_MSG_KEXRSA_SECRET message
504  * @param[in] connection Pointer to the SSH connection
505  * @param[in] message Pointer to message
506  * @param[in] length Length of the message, in bytes
507  * @return Error code
508  **/
509 
510 error_t sshParseKexRsaSecret(SshConnection *connection, const uint8_t *message,
511  size_t length)
512 {
513 #if (SSH_SERVER_SUPPORT == ENABLED)
514  error_t error;
515  const uint8_t *p;
516  SshBinaryString encryptedSecret;
517 
518  //Debug message
519  TRACE_INFO("SSH_MSG_KEXRSA_SECRET message received (%" PRIuSIZE " bytes)...\r\n", length);
521 
522  //Check operation mode
523  if(connection->context->mode != SSH_OPERATION_MODE_SERVER)
525 
526  //Check connection state
527  if(connection->state != SSH_CONN_STATE_KEX_RSA_SECRET)
529 
530  //Sanity check
531  if(length < sizeof(uint8_t))
532  return ERROR_INVALID_MESSAGE;
533 
534  //Point to the first field of the message
535  p = message + sizeof(uint8_t);
536  //Remaining bytes to process
537  length -= sizeof(uint8_t);
538 
539  //Decode encrypted secret
540  error = sshParseBinaryString(p, length, &encryptedSecret);
541  //Any error to report?
542  if(error)
543  return error;
544 
545  //Point to the next field
546  p += sizeof(uint32_t) + encryptedSecret.length;
547  length -= sizeof(uint32_t) + encryptedSecret.length;
548 
549  //Malformed message?
550  if(length != 0)
551  return ERROR_INVALID_MESSAGE;
552 
553  //Update exchange hash H with the encrypted secret
554  error = sshUpdateExchangeHash(connection, encryptedSecret.value,
555  encryptedSecret.length);
556  //Any error to report?
557  if(error)
558  return error;
559 
560  //The server decrypts K using RSAES-OAEP
561  error = sshDecryptSharedSecret(connection, encryptedSecret.value,
562  encryptedSecret.length);
563 
564  //Any decryption error?
565  if(error)
566  {
567  //The server should send SSH_MESSAGE_DISCONNECT with a reason code of
568  //SSH_DISCONNECT_KEY_EXCHANGE_FAILED and must disconnect (refer to
569  //RFC 4432, section 4)
570  error = ERROR_KEY_EXCH_FAILED;
571  }
572  else
573  {
574  //Otherwise, the server responds with an SSH_MSG_KEXRSA_DONE message
575  error = sshSendKexRsaDone(connection);
576  }
577 
578  //Return status code
579  return error;
580 #else
581  //Server operation mode is not implemented
583 #endif
584 }
585 
586 
587 /**
588  * @brief Parse SSH_MSG_KEXRSA_DONE message
589  * @param[in] connection Pointer to the SSH connection
590  * @param[in] message Pointer to message
591  * @param[in] length Length of the message, in bytes
592  * @return Error code
593  **/
594 
595 error_t sshParseKexRsaDone(SshConnection *connection, const uint8_t *message,
596  size_t length)
597 {
598 #if (SSH_CLIENT_SUPPORT == ENABLED)
599  error_t error;
600  const uint8_t *p;
601  SshBinaryString hostKey;
602  SshBinaryString signature;
603 
604  //Debug message
605  TRACE_INFO("SSH_MSG_KEXRSA_DONE message received (%" PRIuSIZE " bytes)...\r\n", length);
607 
608  //Check operation mode
609  if(connection->context->mode != SSH_OPERATION_MODE_CLIENT)
611 
612  //Check connection state
613  if(connection->state != SSH_CONN_STATE_KEX_RSA_DONE)
615 
616  //Sanity check
617  if(length < sizeof(uint8_t))
618  return ERROR_INVALID_MESSAGE;
619 
620  //Point to the first field of the message
621  p = message + sizeof(uint8_t);
622  //Remaining bytes to process
623  length -= sizeof(uint8_t);
624 
625  //Decode the signature field
626  error = sshParseBinaryString(p, length, &signature);
627  //Any error to report?
628  if(error)
629  return error;
630 
631  //Point to the next field
632  p += sizeof(uint32_t) + signature.length;
633  length -= sizeof(uint32_t) + signature.length;
634 
635  //Malformed message?
636  if(length != 0)
637  return ERROR_INVALID_MESSAGE;
638 
639  //Update exchange hash H with K (shared secret)
640  error = sshUpdateExchangeHashRaw(connection, connection->k,
641  connection->kLen);
642  //Any error to report?
643  if(error)
644  return error;
645 
646  //Get server's host key
647  hostKey.value = connection->serverHostKey;
648  hostKey.length = connection->serverHostKeyLen;
649 
650  //Verify the signature on the exchange hash
651  error = sshVerifyExchangeHashSignature(connection, &hostKey, &signature);
652  //Any error to report?
653  if(error)
654  return error;
655 
656  //Release server's host key
657  sshFreeMem(connection->serverHostKey);
658  connection->serverHostKey = NULL;
659  connection->serverHostKeyLen = 0;
660 
661  //Key exchange ends by each side sending an SSH_MSG_NEWKEYS message
662  return sshSendNewKeys(connection);
663 #else
664  //Client operation mode is not implemented
666 #endif
667 }
668 
669 
670 /**
671  * @brief Parse Diffie-Hellman specific messages
672  * @param[in] connection Pointer to the SSH connection
673  * @param[in] type SSH message type
674  * @param[in] message Pointer to message
675  * @param[in] length Length of the message, in bytes
676  * @return Error code
677  **/
678 
680  const uint8_t *message, size_t length)
681 {
682  error_t error;
683 
684 #if (SSH_CLIENT_SUPPORT == ENABLED)
685  //Client operation mode?
686  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
687  {
688  //Check message type
690  {
691  //Parse SSH_MSG_KEXRSA_PUBKEY message
692  error = sshParseKexRsaPubKey(connection, message, length);
693  }
694  else if(type == SSH_MSG_KEXRSA_DONE)
695  {
696  //Parse SSH_MSG_KEXRSA_DONE message
697  error = sshParseKexRsaDone(connection, message, length);
698  }
699  else
700  {
701  //Unknown message type
702  error = ERROR_INVALID_TYPE;
703  }
704  }
705  else
706 #endif
707 #if (SSH_SERVER_SUPPORT == ENABLED)
708  //Server operation mode?
709  if(connection->context->mode == SSH_OPERATION_MODE_SERVER)
710  {
711  //Check message type
713  {
714  //Parse SSH_MSG_KEXRSA_SECRET message
715  error = sshParseKexRsaSecret(connection, message, length);
716  }
717  else
718  {
719  //Unknown message type
720  error = ERROR_INVALID_TYPE;
721  }
722  }
723  else
724 #endif
725  //Invalid operation mode?
726  {
727  //Report an error
728  error = ERROR_INVALID_TYPE;
729  }
730 
731  //Return status code
732  return error;
733 }
734 
735 
736 /**
737  * @brief Select a transient RSA key
738  * @param[in] context Pointer to the SSH context
739  * @param[in] kexAlgo Key exchange algorithm name
740  * @return Index of the selected transient RSA key, if any
741  **/
742 
744 {
745 #if (SSH_SERVER_SUPPORT == ENABLED)
746  uint_t i;
747  int_t index;
748  const SshRsaKey *key;
749 
750  //Initialize index
751  index = -1;
752 
753  //Loop through the transient RSA keys
754  for(i = 0; i < SSH_MAX_RSA_KEYS && index < 0; i++)
755  {
756  //Point to the current RSA key
757  key = &context->rsaKeys[i];
758 
759  //Valid RSA modulus?
762  {
763  //The modulus of K_T must be at least MINKLEN bits long
764  if(sshCompareAlgo(kexAlgo, "rsa1024-sha1") &&
765  key->modulusSize >= 1024)
766  {
767  //The "rsa1024-sha1" method specifies a minimum RSA modulus length
768  //of 1024 bits (refer to RFC 4432, section 5)
769  index = i;
770  }
771  else if(sshCompareAlgo(kexAlgo, "rsa2048-sha256") &&
772  key->modulusSize >= 2048)
773  {
774  //The "rsa2048-sha256" method specifies a minimum RSA modulus length
775  //of 2048 bits (refer to RFC 4432, section 6)
776  index = i;
777  }
778  else
779  {
780  //Just for sanity
781  }
782  }
783  }
784 
785  //Return the index of the transient RSA key
786  return index;
787 #else
788  //Server operation mode is not implemented
789  return -1;
790 #endif
791 }
792 
793 
794 /**
795  * @brief Format transient RSA public key
796  * @param[in] connection Pointer to the SSH connection
797  * @param[out] p Output stream where to write the RSA public key
798  * @param[out] written Total number of bytes that have been written
799  * @return Error code
800  **/
801 
803  size_t *written)
804 {
805 #if (SSH_SERVER_SUPPORT == ENABLED)
806  error_t error;
807  int_t i;
808  SshContext *context;
809  RsaPublicKey rsaPublicKey;
810 
811  //Point to the SSH context
812  context = connection->context;
813 
814  //Initialize RSA public key
815  rsaInitPublicKey(&rsaPublicKey);
816 
817  //The RSA public key may be a transient key generated solely for this
818  //SSH connection, or it may be re-used for several connections (refer to
819  //RFC 4432, section3)
820  i = connection->rsaKeyIndex;
821 
822  //Valid index?
823  if(i >= 0 && i < SSH_MAX_RSA_KEYS)
824  {
825  //Load the transient RSA public key
826  error = sshImportRsaPublicKey(&rsaPublicKey,
827  context->rsaKeys[i].publicKey, context->rsaKeys[i].publicKeyLen);
828 
829  //Check status code
830  if(!error)
831  {
832  //The key K_T is encoded according to the "ssh-rsa" scheme (refer to
833  //RFC 4432, section 4)
834  error = sshFormatRsaPublicKey(&rsaPublicKey, p, written);
835  }
836  }
837  else
838  {
839  //Report an error
840  error = ERROR_INVALID_KEY;
841  }
842 
843  //Free previously allocated resources
844  rsaFreePublicKey(&rsaPublicKey);
845 
846  //Return status code
847  return error;
848 #else
849  //Server operation mode is not implemented
850  return ERROR_NOT_IMPLEMENTED;
851 #endif
852 }
853 
854 
855 /**
856  * @brief Encrypt shared secret using RSAES-OAEP
857  * @param[in] connection Pointer to the SSH connection
858  * @param[in] transientRsaPublicKey Transient RSA public key (K_T)
859  * @param[out] encryptedSecret Ciphertext resulting from the encryption
860  * operation
861  * @param[out] encryptedSecretLen Length of the resulting ciphertext
862  * @return Error code
863  **/
864 
866  const SshBinaryString *transientRsaPublicKey, uint8_t *encryptedSecret,
867  size_t *encryptedSecretLen)
868 {
869 #if (SSH_CLIENT_SUPPORT == ENABLED)
870  error_t error;
871  uint8_t n;
872  uint_t kLen;
873  SshContext *context;
874  SshRsaHostKey rsaHostKey;
875  RsaPublicKey rsaPublicKey;
876 
877  //Point to the SSH context
878  context = connection->context;
879 
880  //Initialize RSA public key
881  rsaInitPublicKey(&rsaPublicKey);
882 
883  //The key K_T is encoded according to the "ssh-rsa" scheme (refer to
884  //RFC 4432, section 4)
885  error = sshParseRsaHostKey(transientRsaPublicKey->value,
886  transientRsaPublicKey->length, &rsaHostKey);
887 
888  //Check status code
889  if(!error)
890  {
891  //Load the transient RSA public key
892  error = sshImportRsaHostKey(&rsaPublicKey, &rsaHostKey);
893  }
894 
895  //Check status code
896  if(!error)
897  {
898  //Let KLEN be the length of the modulus of K_T, in bits
899  kLen = mpiGetBitLength(&rsaPublicKey.n);
900 
901  //Make sure the length of the RSA modulus is acceptable
903  {
904  //Determine the length of the shared secret
905  connection->kLen = (kLen + 7) / 8;
906  connection->kLen -= (2 * connection->hashAlgo->digestSize) + 6;
907 
908  //Generate a random integer K
909  error = context->prngAlgo->generate(context->prngContext,
910  connection->k, connection->kLen);
911 
912  //Check status code
913  if(!error)
914  {
915  //The mpint encoding of K requires a leading zero bit and padding
916  //to a whole number of bytes
917  n = (kLen + 7) % 8;
918 
919  //Ensure K is in the range 0 <= K < 2^(KLEN-2*HLEN-49)
920  if(n != 0)
921  {
922  connection->k[0] &= (1 << n) - 1;
923  }
924 
925  //Log shared secret (for debugging purpose only)
926  sshDumpKey(connection, "SHARED_SECRET", connection->k,
927  connection->kLen);
928 
929  //Convert the shared secret K to mpint representation
930  error = sshConvertArrayToMpint(connection->k, connection->kLen,
931  connection->k, &connection->kLen);
932  }
933 
934  //Check status code
935  if(!error)
936  {
937  //Perform RSAES-OAEP encryption
938  error = rsaesOaepEncrypt(context->prngAlgo, context->prngContext,
939  &rsaPublicKey, connection->hashAlgo, "", connection->k,
940  connection->kLen, encryptedSecret, encryptedSecretLen);
941  }
942  }
943  else
944  {
945  //Report an error
946  error = ERROR_INVALID_KEY;
947  }
948  }
949 
950  //Free previously allocated resources
951  rsaFreePublicKey(&rsaPublicKey);
952 
953  //Return status code
954  return error;
955 #else
956  //Client operation mode is not implemented
957  return ERROR_NOT_IMPLEMENTED;
958 #endif
959 }
960 
961 
962 /**
963  * @brief Decrypt shared secret using RSAES-OAEP
964  * @param[in] connection Pointer to the SSH connection
965  * @param[in] encryptedSecret Ciphertext to be decrypted
966  * @param[in] encryptedSecretLen Length of the ciphertext to be decrypted
967  * @return Error code
968  **/
969 
971  const uint8_t *encryptedSecret, size_t encryptedSecretLen)
972 {
973 #if (SSH_SERVER_SUPPORT == ENABLED)
974  error_t error;
975  int_t i;
976  SshBinaryString k;
977  SshContext *context;
978  RsaPrivateKey rsaPrivateKey;
979 
980  //Point to the SSH context
981  context = connection->context;
982 
983  //Initialize RSA private key
984  rsaInitPrivateKey(&rsaPrivateKey);
985 
986  //Index of the transient RSA private key
987  i = connection->rsaKeyIndex;
988 
989  //Valid index?
990  if(i >= 0 && i < SSH_MAX_RSA_KEYS)
991  {
992  //Load the transient RSA private key
993  error = pemImportRsaPrivateKey(&rsaPrivateKey,
994  context->rsaKeys[i].privateKey,
995  context->rsaKeys[i].privateKeyLen, context->rsaKeys[i].password);
996 
997  //Check status code
998  if(!error)
999  {
1000  //Perform RSAES-OAEP decryption
1001  error = rsaesOaepDecrypt(&rsaPrivateKey, connection->hashAlgo, "",
1002  encryptedSecret, encryptedSecretLen, connection->k,
1003  SSH_MAX_SHARED_SECRET_LEN, &connection->kLen);
1004  }
1005 
1006  //Check status code
1007  if(!error)
1008  {
1009  //The shared secret K must be encoded as a mpint
1010  error = sshParseBinaryString(connection->k, connection->kLen, &k);
1011  }
1012 
1013  //Check status code
1014  if(!error)
1015  {
1016  //Check the length of the shared secret
1017  if(connection->kLen == (sizeof(uint32_t) + k.length))
1018  {
1019  //Log shared secret (for debugging purpose only)
1020  sshDumpKey(connection, "SHARED_SECRET", k.value, k.length);
1021  }
1022  else
1023  {
1024  //Report an error
1025  error = ERROR_DECRYPTION_FAILED;
1026  }
1027  }
1028  }
1029  else
1030  {
1031  //Report an error
1032  error = ERROR_INVALID_KEY;
1033  }
1034 
1035  //Free previously allocated resources
1036  rsaFreePrivateKey(&rsaPrivateKey);
1037 
1038  //Return status code
1039  return error;
1040 #else
1041  //Server operation mode is not implemented
1042  return ERROR_NOT_IMPLEMENTED;
1043 #endif
1044 }
1045 
1046 #endif
error_t sshGenerateExchangeHashSignature(SshConnection *connection, uint8_t *p, size_t *written)
Compute the signature on the exchange hash.
void rsaFreePublicKey(RsaPublicKey *key)
Release an RSA public key.
Definition: rsa.c:113
signed int int_t
Definition: compiler_port.h:56
SSH host key verification.
Binary string.
Definition: ssh_types.h:67
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
@ SSH_CONN_STATE_KEX_RSA_SECRET
Definition: ssh.h:1063
@ ERROR_DECRYPTION_FAILED
Definition: error.h:243
@ ERROR_UNEXPECTED_MESSAGE
Definition: error.h:195
void sshDumpKey(SshConnection *connection, const char_t *label, const uint8_t *key, size_t keyLen)
Dump secret key (for debugging purpose only)
uint8_t p
Definition: ndp.h:300
uint8_t message[]
Definition: chap.h:154
#define SSH_MIN_RSA_MODULUS_SIZE
Definition: ssh.h:703
error_t sshVerifyServerHostKey(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *hostKey)
Verify server's host key.
error_t sshVerifyExchangeHashSignature(SshConnection *connection, const SshBinaryString *serverHostKey, const SshBinaryString *signature)
Verify the signature on the exchange hash.
SSH transport layer protocol.
error_t sshUpdateExchangeHashRaw(SshConnection *connection, const void *data, size_t length)
Update exchange hash calculation (raw data)
uint8_t type
Definition: coap_common.h:176
SSH certificate verification.
Transient RSA key (for RSA key exchange)
Definition: ssh.h:1142
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
PEM key file import functions.
size_t length
Definition: ssh_types.h:58
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
#define SSH_MAX_SHARED_SECRET_LEN
Definition: ssh.h:857
#define osStrlen(s)
Definition: os_port.h:168
error_t sshParseRsaHostKey(const uint8_t *data, size_t length, SshRsaHostKey *hostKey)
Parse an RSA host key structure.
SSH key file import functions.
error_t sshParseKexRsaDone(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEXRSA_DONE message.
Definition: ssh_kex_rsa.c:595
#define SSH_PACKET_HEADER_SIZE
Definition: ssh_packet.h:38
void rsaInitPrivateKey(RsaPrivateKey *key)
Initialize an RSA private key.
Definition: rsa.c:126
Mpi n
Modulus.
Definition: rsa.h:58
error_t sshSendPacket(SshConnection *connection, uint8_t *payload, size_t payloadLen)
Send SSH packet.
Definition: ssh_packet.c:57
bool_t sshIsCertPublicKeyAlgo(const SshString *publicKeyAlgo)
Test if the specified public key algorithm is using certificates.
error_t sshFormatRsaPublicKey(const RsaPublicKey *publicKey, uint8_t *p, size_t *written)
Format an RSA public host key.
error_t sshSendNewKeys(SshConnection *connection)
Send SSH_MSG_NEWKEYS message.
Definition: ssh_kex.c:204
size_t length
Definition: ssh_types.h:69
error_t rsaesOaepEncrypt(const PrngAlgo *prngAlgo, void *prngContext, const RsaPublicKey *key, const HashAlgo *hash, const char_t *label, const uint8_t *message, size_t messageLen, uint8_t *ciphertext, size_t *ciphertextLen)
RSAES-OAEP encryption operation.
Key material generation.
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:1636
error_t pemImportRsaPrivateKey(RsaPrivateKey *privateKey, const char_t *input, size_t length, const char_t *password)
Decode a PEM file containing an RSA private key.
error_t sshFormatTransientRsaPublicKey(SshConnection *connection, uint8_t *p, size_t *written)
Format transient RSA public key.
Definition: ssh_kex_rsa.c:802
#define SshContext
Definition: ssh.h:892
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
const char_t * value
Definition: ssh_types.h:57
error_t
Error codes.
Definition: error.h:43
void rsaFreePrivateKey(RsaPrivateKey *key)
Release an RSA private key.
Definition: rsa.c:148
bool_t sshCompareAlgo(const char_t *name1, const char_t *name2)
Compare algorithm names.
Definition: ssh_misc.c:1758
@ SSH_OPERATION_MODE_SERVER
Definition: ssh.h:915
#define SSH_MAX_RSA_KEYS
Definition: ssh.h:668
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:914
@ SSH_MSG_KEXRSA_DONE
Definition: ssh.h:971
error_t sshParseKexRsaSecret(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEXRSA_SECRET message.
Definition: ssh_kex_rsa.c:510
error_t sshVerifyServerCertificate(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *hostKey)
Verify server's certificate.
RSA public key.
Definition: rsa.h:57
error_t sshParseKexRsaMessage(SshConnection *connection, uint8_t type, const uint8_t *message, size_t length)
Parse Diffie-Hellman specific messages.
Definition: ssh_kex_rsa.c:679
error_t sshFormatKexRsaDone(SshConnection *connection, uint8_t *p, size_t *length)
Format SSH_MSG_KEXRSA_DONE message.
Definition: ssh_kex_rsa.c:336
error_t sshEncryptSharedSecret(SshConnection *connection, const SshBinaryString *transientRsaPublicKey, uint8_t *encryptedSecret, size_t *encryptedSecretLen)
Encrypt shared secret using RSAES-OAEP.
Definition: ssh_kex_rsa.c:865
error_t sshParseKexRsaPubKey(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEXRSA_PUBKEY message.
Definition: ssh_kex_rsa.c:390
@ SSH_CONN_STATE_KEX_RSA_PUB_KEY
Definition: ssh.h:1062
@ ERROR_INVALID_TYPE
Definition: error.h:115
SSH key formatting.
error_t sshDecryptSharedSecret(SshConnection *connection, const uint8_t *encryptedSecret, size_t encryptedSecretLen)
Decrypt shared secret using RSAES-OAEP.
Definition: ssh_kex_rsa.c:970
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
@ SSH_CONN_STATE_SERVER_NEW_KEYS
Definition: ssh.h:1078
String.
Definition: ssh_types.h:56
SSH key exchange.
#define SSH_MAX_RSA_MODULUS_SIZE
Definition: ssh.h:710
uint_t mpiGetBitLength(const Mpi *a)
Get the actual length in bits.
Definition: mpi.c:255
const uint8_t * value
Definition: ssh_types.h:68
error_t sshSendKexRsaDone(SshConnection *connection)
Send SSH_MSG_KEXRSA_DONE message.
Definition: ssh_kex_rsa.c:169
char char_t
Definition: compiler_port.h:55
#define sshFreeMem(p)
Definition: ssh.h:736
error_t sshImportRsaHostKey(RsaPublicKey *publicKey, const SshRsaHostKey *hostKey)
Import an RSA host key.
uint8_t n
RSA private key.
Definition: rsa.h:68
Exchange hash calculation.
@ SSH_CONN_STATE_KEX_RSA_DONE
Definition: ssh.h:1064
#define SshConnection
Definition: ssh.h:896
error_t rsaesOaepDecrypt(const RsaPrivateKey *key, const HashAlgo *hash, const char_t *label, const uint8_t *ciphertext, size_t ciphertextLen, uint8_t *message, size_t messageSize, size_t *messageLen)
RSAES-OAEP decryption operation.
@ SSH_MSG_KEXRSA_PUBKEY
Definition: ssh.h:969
error_t sshFormatKexRsaSecret(SshConnection *connection, const SshBinaryString *transientRsaPublicKey, uint8_t *p, size_t *length)
Format SSH_MSG_KEXRSA_SECRET message.
Definition: ssh_kex_rsa.c:283
SSH helper functions.
RSA host key.
Definition: ssh_key_parse.h:52
error_t sshFormatHostKey(SshConnection *connection, uint8_t *p, size_t *written)
Format host key structure.
Definition: ssh_misc.c:875
error_t sshFormatKexRsaPubKey(SshConnection *connection, uint8_t *p, size_t *length)
Format SSH_MSG_KEXRSA_PUBKEY message.
Definition: ssh_kex_rsa.c:217
SSH packet encryption/decryption.
@ SSH_MSG_KEXRSA_SECRET
Definition: ssh.h:970
error_t sshUpdateExchangeHash(SshConnection *connection, const void *data, size_t length)
Update exchange hash calculation.
@ ERROR_KEY_EXCH_FAILED
Definition: error.h:313
error_t sshParseBinaryString(const uint8_t *p, size_t length, SshBinaryString *string)
Parse a binary string.
Definition: ssh_misc.c:1215
#define sshAllocMem(size)
Definition: ssh.h:731
error_t sshImportRsaPublicKey(RsaPublicKey *publicKey, const char_t *input, size_t length)
Decode an SSH public key file containing an RSA public key.
int_t sshSelectTransientRsaKey(SshContext *context, const char_t *kexAlgo)
Select a transient RSA key.
Definition: ssh_kex_rsa.c:743
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:57
Secure Shell (SSH)
SSH algorithm negotiation.
uint_t modulusSize
Length of the modulus, in bits.
Definition: ssh.h:1143
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
@ ERROR_INVALID_KEY
Definition: error.h:106
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
void rsaInitPublicKey(RsaPublicKey *key)
Initialize an RSA public key.
Definition: rsa.c:100
error_t sshSendKexRsaSecret(SshConnection *connection, const SshBinaryString *transientRsaPublicKey)
Send SSH_MSG_KEXRSA_SECRET message.
Definition: ssh_kex_rsa.c:121
error_t sshSendKexRsaPubKey(SshConnection *connection)
Send SSH_MSG_KEXRSA_PUBKEY message.
Definition: ssh_kex_rsa.c:61
#define TRACE_VERBOSE_ARRAY(p, a, n)
Definition: debug.h:140