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-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSSH Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SSH_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "ssh/ssh_algorithms.h"
37 #include "ssh/ssh_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_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)
571  "Key exchanged failed");
572  }
573  else
574  {
575  //Otherwise, the server responds with an SSH_MSG_KEXRSA_DONE message
576  error = sshSendKexRsaDone(connection);
577  }
578 
579  //Return status code
580  return error;
581 #else
582  //Server operation mode is not implemented
584 #endif
585 }
586 
587 
588 /**
589  * @brief Parse SSH_MSG_KEXRSA_DONE message
590  * @param[in] connection Pointer to the SSH connection
591  * @param[in] message Pointer to message
592  * @param[in] length Length of the message, in bytes
593  * @return Error code
594  **/
595 
596 error_t sshParseKexRsaDone(SshConnection *connection, const uint8_t *message,
597  size_t length)
598 {
599 #if (SSH_CLIENT_SUPPORT == ENABLED)
600  error_t error;
601  const uint8_t *p;
602  SshBinaryString hostKey;
603  SshBinaryString signature;
604 
605  //Debug message
606  TRACE_INFO("SSH_MSG_KEXRSA_DONE message received (%" PRIuSIZE " bytes)...\r\n", length);
608 
609  //Check operation mode
610  if(connection->context->mode != SSH_OPERATION_MODE_CLIENT)
612 
613  //Check connection state
614  if(connection->state != SSH_CONN_STATE_KEX_RSA_DONE)
616 
617  //Sanity check
618  if(length < sizeof(uint8_t))
619  return ERROR_INVALID_MESSAGE;
620 
621  //Point to the first field of the message
622  p = message + sizeof(uint8_t);
623  //Remaining bytes to process
624  length -= sizeof(uint8_t);
625 
626  //Decode the signature field
627  error = sshParseBinaryString(p, length, &signature);
628  //Any error to report?
629  if(error)
630  return error;
631 
632  //Point to the next field
633  p += sizeof(uint32_t) + signature.length;
634  length -= sizeof(uint32_t) + signature.length;
635 
636  //Malformed message?
637  if(length != 0)
638  return ERROR_INVALID_MESSAGE;
639 
640  //Update exchange hash H with K (shared secret)
641  error = sshUpdateExchangeHashRaw(connection, connection->k,
642  connection->kLen);
643  //Any error to report?
644  if(error)
645  return error;
646 
647  //Get server's host key
648  hostKey.value = connection->serverHostKey;
649  hostKey.length = connection->serverHostKeyLen;
650 
651  //Verify the signature on the exchange hash
652  error = sshVerifyExchangeHashSignature(connection, &hostKey, &signature);
653  //Any error to report?
654  if(error)
655  return error;
656 
657  //Release server's host key
658  sshFreeMem(connection->serverHostKey);
659  connection->serverHostKey = NULL;
660  connection->serverHostKeyLen = 0;
661 
662  //Key exchange ends by each side sending an SSH_MSG_NEWKEYS message
663  return sshSendNewKeys(connection);
664 #else
665  //Client operation mode is not implemented
667 #endif
668 }
669 
670 
671 /**
672  * @brief Parse Diffie-Hellman specific messages
673  * @param[in] connection Pointer to the SSH connection
674  * @param[in] type SSH message type
675  * @param[in] message Pointer to message
676  * @param[in] length Length of the message, in bytes
677  * @return Error code
678  **/
679 
681  const uint8_t *message, size_t length)
682 {
683  error_t error;
684 
685 #if (SSH_CLIENT_SUPPORT == ENABLED)
686  //Client operation mode?
687  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
688  {
689  //Check message type
691  {
692  //Parse SSH_MSG_KEXRSA_PUBKEY message
693  error = sshParseKexRsaPubKey(connection, message, length);
694  }
695  else if(type == SSH_MSG_KEXRSA_DONE)
696  {
697  //Parse SSH_MSG_KEXRSA_DONE message
698  error = sshParseKexRsaDone(connection, message, length);
699  }
700  else
701  {
702  //Unknown message type
703  error = ERROR_INVALID_TYPE;
704  }
705  }
706  else
707 #endif
708 #if (SSH_SERVER_SUPPORT == ENABLED)
709  //Server operation mode?
710  if(connection->context->mode == SSH_OPERATION_MODE_SERVER)
711  {
712  //Check message type
714  {
715  //Parse SSH_MSG_KEXRSA_SECRET message
716  error = sshParseKexRsaSecret(connection, message, length);
717  }
718  else
719  {
720  //Unknown message type
721  error = ERROR_INVALID_TYPE;
722  }
723  }
724  else
725 #endif
726  //Invalid operation mode?
727  {
728  //Report an error
729  error = ERROR_INVALID_TYPE;
730  }
731 
732  //Return status code
733  return error;
734 }
735 
736 
737 /**
738  * @brief Select a transient RSA key
739  * @param[in] context Pointer to the SSH context
740  * @param[in] kexAlgo Key exchange algorithm name
741  * @return Index of the selected transient RSA key, if any
742  **/
743 
745 {
746 #if (SSH_SERVER_SUPPORT == ENABLED)
747  uint_t i;
748  int_t index;
749  const SshRsaKey *key;
750 
751  //Initialize index
752  index = -1;
753 
754  //Loop through the transient RSA keys
755  for(i = 0; i < SSH_MAX_RSA_KEYS && index < 0; i++)
756  {
757  //Point to the current RSA key
758  key = &context->rsaKeys[i];
759 
760  //Valid RSA modulus?
763  {
764  //The modulus of K_T must be at least MINKLEN bits long
765  if(sshCompareAlgo(kexAlgo, "rsa1024-sha1") &&
766  key->modulusSize >= 1024)
767  {
768  //The "rsa1024-sha1" method specifies a minimum RSA modulus length
769  //of 1024 bits (refer to RFC 4432, section 5)
770  index = i;
771  }
772  else if(sshCompareAlgo(kexAlgo, "rsa2048-sha256") &&
773  key->modulusSize >= 2048)
774  {
775  //The "rsa2048-sha256" method specifies a minimum RSA modulus length
776  //of 2048 bits (refer to RFC 4432, section 6)
777  index = i;
778  }
779  else
780  {
781  //Just for sanity
782  }
783  }
784  }
785 
786  //Return the index of the transient RSA key
787  return index;
788 #else
789  //Server operation mode is not implemented
790  return -1;
791 #endif
792 }
793 
794 
795 /**
796  * @brief Format transient RSA public key
797  * @param[in] connection Pointer to the SSH connection
798  * @param[out] p Output stream where to write the RSA public key
799  * @param[out] written Total number of bytes that have been written
800  * @return Error code
801  **/
802 
804  size_t *written)
805 {
806 #if (SSH_SERVER_SUPPORT == ENABLED)
807  error_t error;
808  int_t i;
809  SshContext *context;
810  RsaPublicKey rsaPublicKey;
811 
812  //Point to the SSH context
813  context = connection->context;
814 
815  //Initialize RSA public key
816  rsaInitPublicKey(&rsaPublicKey);
817 
818  //The RSA public key may be a transient key generated solely for this
819  //SSH connection, or it may be re-used for several connections (refer to
820  //RFC 4432, section3)
821  i = connection->rsaKeyIndex;
822 
823  //Valid index?
824  if(i >= 0 && i < SSH_MAX_RSA_KEYS)
825  {
826  //Load the transient RSA public key
827  error = sshImportRsaPublicKey(context->rsaKeys[i].publicKey,
828  context->rsaKeys[i].publicKeyLen, &rsaPublicKey);
829 
830  //Check status code
831  if(!error)
832  {
833  //The key K_T is encoded according to the "ssh-rsa" scheme (refer to
834  //RFC 4432, section 4)
835  error = sshFormatRsaPublicKey(&rsaPublicKey, p, written);
836  }
837  }
838  else
839  {
840  //Report an error
841  error = ERROR_INVALID_KEY;
842  }
843 
844  //Free previously allocated resources
845  rsaFreePublicKey(&rsaPublicKey);
846 
847  //Return status code
848  return error;
849 #else
850  //Server operation mode is not implemented
851  return ERROR_NOT_IMPLEMENTED;
852 #endif
853 }
854 
855 
856 /**
857  * @brief Encrypt shared secret using RSAES-OAEP
858  * @param[in] connection Pointer to the SSH connection
859  * @param[in] transientRsaPublicKey Transient RSA public key (K_T)
860  * @param[out] encryptedSecret Ciphertext resulting from the encryption
861  * operation
862  * @param[out] encryptedSecretLen Length of the resulting ciphertext
863  * @return Error code
864  **/
865 
867  const SshBinaryString *transientRsaPublicKey, uint8_t *encryptedSecret,
868  size_t *encryptedSecretLen)
869 {
870 #if (SSH_CLIENT_SUPPORT == ENABLED)
871  error_t error;
872  uint8_t n;
873  uint_t kLen;
874  SshContext *context;
875  SshRsaHostKey rsaHostKey;
876  RsaPublicKey rsaPublicKey;
877 
878  //Point to the SSH context
879  context = connection->context;
880 
881  //Initialize RSA public key
882  rsaInitPublicKey(&rsaPublicKey);
883 
884  //The key K_T is encoded according to the "ssh-rsa" scheme (refer to
885  //RFC 4432, section 4)
886  error = sshParseRsaHostKey(transientRsaPublicKey->value,
887  transientRsaPublicKey->length, &rsaHostKey);
888 
889  //Check status code
890  if(!error)
891  {
892  //Load the transient RSA public key
893  error = sshImportRsaHostKey(&rsaHostKey, &rsaPublicKey);
894  }
895 
896  //Check status code
897  if(!error)
898  {
899  //Let KLEN be the length of the modulus of K_T, in bits
900  kLen = mpiGetBitLength(&rsaPublicKey.n);
901 
902  //Make sure the length of the RSA modulus is acceptable
904  {
905  //Determine the length of the shared secret
906  connection->kLen = (kLen + 7) / 8;
907  connection->kLen -= (2 * connection->hashAlgo->digestSize) + 6;
908 
909  //Generate a random integer K
910  error = context->prngAlgo->read(context->prngContext, connection->k,
911  connection->kLen);
912 
913  //Check status code
914  if(!error)
915  {
916  //The mpint encoding of K requires a leading zero bit and padding
917  //to a whole number of bytes
918  n = (kLen + 7) % 8;
919 
920  //Ensure K is in the range 0 <= K < 2^(KLEN-2*HLEN-49)
921  if(n != 0)
922  {
923  connection->k[0] &= (1 << n) - 1;
924  }
925 
926  //Log shared secret (for debugging purpose only)
927  sshDumpKey(connection, "SHARED_SECRET", connection->k,
928  connection->kLen);
929 
930  //Convert the shared secret K to mpint representation
931  error = sshConvertArrayToMpint(connection->k, connection->kLen,
932  connection->k, &connection->kLen);
933  }
934 
935  //Check status code
936  if(!error)
937  {
938  //Perform RSAES-OAEP encryption
939  error = rsaesOaepEncrypt(context->prngAlgo, context->prngContext,
940  &rsaPublicKey, connection->hashAlgo, "", connection->k,
941  connection->kLen, encryptedSecret, encryptedSecretLen);
942  }
943  }
944  else
945  {
946  //Report an error
947  error = ERROR_INVALID_KEY;
948  }
949  }
950 
951  //Free previously allocated resources
952  rsaFreePublicKey(&rsaPublicKey);
953 
954  //Return status code
955  return error;
956 #else
957  //Client operation mode is not implemented
958  return ERROR_NOT_IMPLEMENTED;
959 #endif
960 }
961 
962 
963 /**
964  * @brief Decrypt shared secret using RSAES-OAEP
965  * @param[in] connection Pointer to the SSH connection
966  * @param[in] encryptedSecret Ciphertext to be decrypted
967  * @param[in] encryptedSecretLen Length of the ciphertext to be decrypted
968  * @return Error code
969  **/
970 
972  const uint8_t *encryptedSecret, size_t encryptedSecretLen)
973 {
974 #if (SSH_SERVER_SUPPORT == ENABLED)
975  error_t error;
976  int_t i;
977  SshBinaryString k;
978  SshContext *context;
979  RsaPrivateKey rsaPrivateKey;
980 
981  //Point to the SSH context
982  context = connection->context;
983 
984  //Initialize RSA private key
985  rsaInitPrivateKey(&rsaPrivateKey);
986 
987  //Index of the transient RSA private key
988  i = connection->rsaKeyIndex;
989 
990  //Valid index?
991  if(i >= 0 && i < SSH_MAX_RSA_KEYS)
992  {
993  //Load the transient RSA private key
994  error = pemImportRsaPrivateKey(context->rsaKeys[i].privateKey,
995  context->rsaKeys[i].privateKeyLen, context->rsaKeys[i].password,
996  &rsaPrivateKey);
997 
998  //Check status code
999  if(!error)
1000  {
1001  //Perform RSAES-OAEP decryption
1002  error = rsaesOaepDecrypt(&rsaPrivateKey, connection->hashAlgo, "",
1003  encryptedSecret, encryptedSecretLen, connection->k,
1004  SSH_MAX_SHARED_SECRET_LEN, &connection->kLen);
1005  }
1006 
1007  //Check status code
1008  if(!error)
1009  {
1010  //The shared secret K must be encoded as a mpint
1011  error = sshParseBinaryString(connection->k, connection->kLen, &k);
1012  }
1013 
1014  //Check status code
1015  if(!error)
1016  {
1017  //Check the length of the shared secret
1018  if(connection->kLen == (sizeof(uint32_t) + k.length))
1019  {
1020  //Log shared secret (for debugging purpose only)
1021  sshDumpKey(connection, "SHARED_SECRET", k.value, k.length);
1022  }
1023  else
1024  {
1025  //Report an error
1026  error = ERROR_DECRYPTION_FAILED;
1027  }
1028  }
1029  }
1030  else
1031  {
1032  //Report an error
1033  error = ERROR_INVALID_KEY;
1034  }
1035 
1036  //Free previously allocated resources
1037  rsaFreePrivateKey(&rsaPrivateKey);
1038 
1039  //Return status code
1040  return error;
1041 #else
1042  //Server operation mode is not implemented
1043  return ERROR_NOT_IMPLEMENTED;
1044 #endif
1045 }
1046 
1047 #endif
uint8_t message[]
Definition: chap.h:154
uint8_t type
Definition: coap_common.h:176
signed int int_t
Definition: compiler_port.h:49
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
char char_t
Definition: compiler_port.h:48
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
Debugging facilities.
#define TRACE_INFO(...)
Definition: debug.h:95
#define TRACE_VERBOSE_ARRAY(p, a, n)
Definition: debug.h:125
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_TYPE
Definition: error.h:115
@ ERROR_INVALID_KEY
Definition: error.h:106
@ ERROR_DECRYPTION_FAILED
Definition: error.h:241
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
@ ERROR_UNEXPECTED_MESSAGE
Definition: error.h:194
uint_t mpiGetBitLength(const Mpi *a)
Get the actual length in bits.
Definition: mpi.c:234
uint8_t p
Definition: ndp.h:300
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define osStrlen(s)
Definition: os_port.h:165
error_t pemImportRsaPrivateKey(const char_t *input, size_t length, const char_t *password, RsaPrivateKey *privateKey)
Decode a PEM file containing an RSA private key.
Definition: pem_import.c:389
PEM file import functions.
void rsaFreePrivateKey(RsaPrivateKey *key)
Release an RSA private key.
Definition: rsa.c:153
void rsaFreePublicKey(RsaPublicKey *key)
Release an RSA public key.
Definition: rsa.c:118
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.
Definition: rsa.c:438
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.
Definition: rsa.c:544
void rsaInitPrivateKey(RsaPrivateKey *key)
Initialize an RSA private key.
Definition: rsa.c:131
void rsaInitPublicKey(RsaPublicKey *key)
Initialize an RSA public key.
Definition: rsa.c:105
Secure Shell (SSH)
#define sshFreeMem(p)
Definition: ssh.h:736
@ SSH_CONN_STATE_KEX_RSA_PUB_KEY
Definition: ssh.h:1047
@ SSH_CONN_STATE_KEX_RSA_DONE
Definition: ssh.h:1049
@ SSH_CONN_STATE_KEX_RSA_SECRET
Definition: ssh.h:1048
@ SSH_CONN_STATE_SERVER_NEW_KEYS
Definition: ssh.h:1061
#define SSH_MAX_RSA_MODULUS_SIZE
Definition: ssh.h:710
@ SSH_OPERATION_MODE_SERVER
Definition: ssh.h:902
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:901
#define sshAllocMem(size)
Definition: ssh.h:731
#define SSH_MAX_SHARED_SECRET_LEN
Definition: ssh.h:851
#define SshConnection
Definition: ssh.h:883
#define SSH_MIN_RSA_MODULUS_SIZE
Definition: ssh.h:703
#define SshContext
Definition: ssh.h:879
@ SSH_DISCONNECT_KEY_EXCHANGE_FAILED
Definition: ssh.h:1007
#define SSH_MAX_RSA_KEYS
Definition: ssh.h:668
@ SSH_MSG_KEXRSA_SECRET
Definition: ssh.h:957
@ SSH_MSG_KEXRSA_PUBKEY
Definition: ssh.h:956
@ SSH_MSG_KEXRSA_DONE
Definition: ssh.h:958
bool_t sshIsCertPublicKeyAlgo(const SshString *publicKeyAlgo)
Test if the specified public key algorithm is using certificates.
SSH algorithm negotiation.
error_t sshVerifyServerCertificate(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *hostKey)
Verify server's certificate.
SSH certificate verification.
error_t sshUpdateExchangeHash(SshConnection *connection, const void *data, size_t length)
Update exchange hash calculation.
error_t sshUpdateExchangeHashRaw(SshConnection *connection, const void *data, size_t length)
Update exchange hash calculation (raw data)
error_t sshGenerateExchangeHashSignature(SshConnection *connection, uint8_t *p, size_t *written)
Compute the signature on the exchange hash.
error_t sshVerifyExchangeHashSignature(SshConnection *connection, const SshBinaryString *serverHostKey, const SshBinaryString *signature)
Verify the signature on the exchange hash.
Exchange hash calculation.
error_t sshSendNewKeys(SshConnection *connection)
Send SSH_MSG_NEWKEYS message.
Definition: ssh_kex.c:194
SSH key exchange.
error_t sshSendKexRsaDone(SshConnection *connection)
Send SSH_MSG_KEXRSA_DONE message.
Definition: ssh_kex_rsa.c:169
error_t sshParseKexRsaDone(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEXRSA_DONE message.
Definition: ssh_kex_rsa.c:596
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:866
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:680
error_t sshSendKexRsaPubKey(SshConnection *connection)
Send SSH_MSG_KEXRSA_PUBKEY message.
Definition: ssh_kex_rsa.c:61
error_t sshSendKexRsaSecret(SshConnection *connection, const SshBinaryString *transientRsaPublicKey)
Send SSH_MSG_KEXRSA_SECRET message.
Definition: ssh_kex_rsa.c:121
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 sshDecryptSharedSecret(SshConnection *connection, const uint8_t *encryptedSecret, size_t encryptedSecretLen)
Decrypt shared secret using RSAES-OAEP.
Definition: ssh_kex_rsa.c:971
int_t sshSelectTransientRsaKey(SshContext *context, const char_t *kexAlgo)
Select a transient RSA key.
Definition: ssh_kex_rsa.c:744
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 sshParseKexRsaPubKey(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_KEXRSA_PUBKEY message.
Definition: ssh_kex_rsa.c:390
error_t sshFormatKexRsaPubKey(SshConnection *connection, uint8_t *p, size_t *length)
Format SSH_MSG_KEXRSA_PUBKEY message.
Definition: ssh_kex_rsa.c:217
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
error_t sshFormatTransientRsaPublicKey(SshConnection *connection, uint8_t *p, size_t *written)
Format transient RSA public key.
Definition: ssh_kex_rsa.c:803
RSA key exchange.
error_t sshFormatRsaPublicKey(const RsaPublicKey *publicKey, uint8_t *p, size_t *written)
Format an RSA public host key.
SSH key formatting.
error_t sshImportRsaPublicKey(const char_t *input, size_t length, RsaPublicKey *publicKey)
Decode an SSH public key file containing an RSA public key.
error_t sshImportRsaHostKey(const SshRsaHostKey *hostKey, RsaPublicKey *publicKey)
Import an RSA host key.
SSH key file import functions.
void sshDumpKey(SshConnection *connection, const char_t *label, const uint8_t *key, size_t keyLen)
Dump secret key (for debugging purpose only)
Key material generation.
error_t sshParseRsaHostKey(const uint8_t *data, size_t length, SshRsaHostKey *hostKey)
Parse an RSA host key structure.
error_t sshVerifyServerHostKey(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *hostKey)
Verify server's host key.
SSH host key verification.
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
error_t sshParseBinaryString(const uint8_t *p, size_t length, SshBinaryString *string)
Parse a binary string.
Definition: ssh_misc.c:1189
bool_t sshCompareAlgo(const char_t *name1, const char_t *name2)
Compare algorithm names.
Definition: ssh_misc.c:1653
error_t sshFormatHostKey(SshConnection *connection, uint8_t *p, size_t *written)
Format host key structure.
Definition: ssh_misc.c:863
SSH helper functions.
error_t sshSendPacket(SshConnection *connection, uint8_t *payload, size_t payloadLen)
Send SSH packet.
Definition: ssh_packet.c:57
SSH packet encryption/decryption.
#define SSH_PACKET_HEADER_SIZE
Definition: ssh_packet.h:38
error_t sshSendDisconnect(SshConnection *connection, uint32_t reasonCode, const char_t *description)
Send SSH_MSG_DISCONNECT message.
SSH transport layer protocol.
RSA private key.
Definition: rsa.h:68
RSA public key.
Definition: rsa.h:57
Mpi n
Modulus.
Definition: rsa.h:58
Binary string.
Definition: ssh_types.h:67
const uint8_t * value
Definition: ssh_types.h:68
size_t length
Definition: ssh_types.h:69
RSA host key.
Definition: ssh_key_parse.h:52
Transient RSA key (for RSA key exchange)
Definition: ssh.h:1125
uint_t modulusSize
Length of the modulus, in bits.
Definition: ssh.h:1126
String.
Definition: ssh_types.h:56
const char_t * value
Definition: ssh_types.h:57
size_t length
Definition: ssh_types.h:58
uint8_t length
Definition: tcp.h:368