ssh_key_parse.c
Go to the documentation of this file.
1 /**
2  * @file ssh_key_parse.c
3  * @brief SSH key parsing
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.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_key_parse.h"
37 #include "ssh/ssh_misc.h"
38 #include "ecc/eddsa.h"
39 #include "pqc/mldsa.h"
40 #include "debug.h"
41 
42 //Check SSH stack configuration
43 #if (SSH_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Parse host key structure
48  * @param[in] data Pointer to the host key structure
49  * @param[in] length Length of the host key structure, in bytes
50  * @param[out] keyFormatId Information resulting from the parsing process
51  * @return Error code
52  **/
53 
54 error_t sshParseHostKey(const uint8_t *data, size_t length,
55  SshString *keyFormatId)
56 {
57  error_t error;
58 
59  //Decode key format identifier
60  error = sshParseString(data, length, keyFormatId);
61 
62  //Check status code
63  if(!error)
64  {
65 #if (SSH_RSA_SIGN_SUPPORT == ENABLED)
66  //RSA public key?
67  if(sshCompareString(keyFormatId, "ssh-rsa"))
68  {
69  SshRsaHostKey hostKey;
70 
71  //Parse RSA host key structure
72  error = sshParseRsaHostKey(data, length, &hostKey);
73  }
74  else
75 #endif
76 #if (SSH_DSA_SIGN_SUPPORT == ENABLED)
77  //DSA public key?
78  if(sshCompareString(keyFormatId, "ssh-dss"))
79  {
80  SshDsaHostKey hostKey;
81 
82  //Parse DSA host key structure
83  error = sshParseDsaHostKey(data, length, &hostKey);
84  }
85  else
86 #endif
87 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED)
88  //ECDSA public key?
89  if(sshCompareString(keyFormatId, "ecdsa-sha2-nistp256") ||
90  sshCompareString(keyFormatId, "ecdsa-sha2-nistp384") ||
91  sshCompareString(keyFormatId, "ecdsa-sha2-nistp521"))
92  {
93  SshEcdsaHostKey hostKey;
94 
95  //Parse ECDSA host key structure
96  error = sshParseEcdsaHostKey(data, length, &hostKey);
97  }
98  else
99 #endif
100 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
101  //Ed25519 public key?
102  if(sshCompareString(keyFormatId, "ssh-ed25519"))
103  {
104  SshEddsaHostKey hostKey;
105 
106  //Parse Ed25519 host key structure
107  error = sshParseEd25519HostKey(data, length, &hostKey);
108  }
109  else
110 #endif
111 #if (SSH_ED448_SIGN_SUPPORT == ENABLED)
112  //Ed448 public key?
113  if(sshCompareString(keyFormatId, "ssh-ed448"))
114  {
115  SshEddsaHostKey hostKey;
116 
117  //Parse Ed448 host key structure
118  error = sshParseEd448HostKey(data, length, &hostKey);
119  }
120  else
121 #endif
122  //Unknown public key type?
123  {
124  //Report an error
125  error = ERROR_INVALID_KEY;
126  }
127  }
128 
129  //Return status code
130  return error;
131 }
132 
133 
134 /**
135  * @brief Parse an RSA host key structure
136  * @param[in] data Pointer to the host key structure
137  * @param[in] length Length of the host key structure, in bytes
138  * @param[out] hostKey Information resulting from the parsing process
139  * @return Error code
140  **/
141 
142 error_t sshParseRsaHostKey(const uint8_t *data, size_t length,
143  SshRsaHostKey *hostKey)
144 {
145 #if (SSH_RSA_SIGN_SUPPORT == ENABLED)
146  error_t error;
147 
148  //Decode key format identifier
149  error = sshParseString(data, length, &hostKey->keyFormatId);
150  //Any error to report?
151  if(error)
152  return error;
153 
154  //Unexpected key format identifier?
155  if(!sshCompareString(&hostKey->keyFormatId, "ssh-rsa"))
156  return ERROR_WRONG_IDENTIFIER;
157 
158  //Point to the next field
159  data += sizeof(uint32_t) + hostKey->keyFormatId.length;
160  length -= sizeof(uint32_t) + hostKey->keyFormatId.length;
161 
162  //Parse RSA public exponent
163  error = sshParseBinaryString(data, length, &hostKey->e);
164  //Any error to report?
165  if(error)
166  return error;
167 
168  //Point to the next field
169  data += sizeof(uint32_t) + hostKey->e.length;
170  length -= sizeof(uint32_t) + hostKey->e.length;
171 
172  //Parse RSA modulus
173  error = sshParseBinaryString(data, length, &hostKey->n);
174  //Any error to report?
175  if(error)
176  return error;
177 
178  //Point to the next field
179  data += sizeof(uint32_t) + hostKey->n.length;
180  length -= sizeof(uint32_t) + hostKey->n.length;
181 
182  //Malformed host key?
183  if(length != 0)
184  return ERROR_INVALID_SYNTAX;
185 
186  //Successful processing
187  return NO_ERROR;
188 #else
189  //Not implemented
190  return ERROR_NOT_IMPLEMENTED;
191 #endif
192 }
193 
194 
195 /**
196  * @brief Parse a DSA host key structure
197  * @param[in] data Pointer to the host key structure
198  * @param[in] length Length of the host key structure, in bytes
199  * @param[out] hostKey Information resulting from the parsing process
200  * @return Error code
201  **/
202 
203 error_t sshParseDsaHostKey(const uint8_t *data, size_t length,
204  SshDsaHostKey *hostKey)
205 {
206 #if (SSH_DSA_SIGN_SUPPORT == ENABLED)
207  error_t error;
208 
209  //Decode key format identifier
210  error = sshParseString(data, length, &hostKey->keyFormatId);
211  //Any error to report?
212  if(error)
213  return error;
214 
215  //Unexpected key format identifier?
216  if(!sshCompareString(&hostKey->keyFormatId, "ssh-dss"))
217  return ERROR_WRONG_IDENTIFIER;
218 
219  //Point to the next field
220  data += sizeof(uint32_t) + hostKey->keyFormatId.length;
221  length -= sizeof(uint32_t) + hostKey->keyFormatId.length;
222 
223  //Parse DSA prime modulus
224  error = sshParseBinaryString(data, length, &hostKey->p);
225  //Any error to report?
226  if(error)
227  return error;
228 
229  //Point to the next field
230  data += sizeof(uint32_t) + hostKey->p.length;
231  length -= sizeof(uint32_t) + hostKey->p.length;
232 
233  //Parse DSA group order
234  error = sshParseBinaryString(data, length, &hostKey->q);
235  //Any error to report?
236  if(error)
237  return error;
238 
239  //Point to the next field
240  data += sizeof(uint32_t) + hostKey->q.length;
241  length -= sizeof(uint32_t) + hostKey->q.length;
242 
243  //Parse DSA group generator
244  error = sshParseBinaryString(data, length, &hostKey->g);
245  //Any error to report?
246  if(error)
247  return error;
248 
249  //Point to the next field
250  data += sizeof(uint32_t) + hostKey->g.length;
251  length -= sizeof(uint32_t) + hostKey->g.length;
252 
253  //Parse DSA public key value
254  error = sshParseBinaryString(data, length, &hostKey->y);
255  //Any error to report?
256  if(error)
257  return error;
258 
259  //Point to the next field
260  data += sizeof(uint32_t) + hostKey->y.length;
261  length -= sizeof(uint32_t) + hostKey->y.length;
262 
263  //Malformed host key?
264  if(length != 0)
265  return ERROR_INVALID_SYNTAX;
266 
267  //Successful processing
268  return NO_ERROR;
269 #else
270  //Not implemented
271  return ERROR_NOT_IMPLEMENTED;
272 #endif
273 }
274 
275 
276 /**
277  * @brief Parse an ECDSA host key structure
278  * @param[in] data Pointer to the host key structure
279  * @param[in] length Length of the host key structure, in bytes
280  * @param[out] hostKey Information resulting from the parsing process
281  * @return Error code
282  **/
283 
284 error_t sshParseEcdsaHostKey(const uint8_t *data, size_t length,
285  SshEcdsaHostKey *hostKey)
286 {
287 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED)
288  error_t error;
289 
290  //Decode key format identifier
291  error = sshParseString(data, length, &hostKey->keyFormatId);
292  //Any error to report?
293  if(error)
294  return error;
295 
296  //Unexpected key format identifier?
297  if(!sshCompareString(&hostKey->keyFormatId, "ecdsa-sha2-nistp256") &&
298  !sshCompareString(&hostKey->keyFormatId, "ecdsa-sha2-nistp384") &&
299  !sshCompareString(&hostKey->keyFormatId, "ecdsa-sha2-nistp521"))
300  {
301  return ERROR_WRONG_IDENTIFIER;
302  }
303 
304  //Point to the next field
305  data += sizeof(uint32_t) + hostKey->keyFormatId.length;
306  length -= sizeof(uint32_t) + hostKey->keyFormatId.length;
307 
308  //Parse elliptic curve domain parameter identifier
309  error = sshParseString(data, length, &hostKey->curveName);
310  //Any error to report?
311  if(error)
312  return error;
313 
314  //Point to the next field
315  data += sizeof(uint32_t) + hostKey->curveName.length;
316  length -= sizeof(uint32_t) + hostKey->curveName.length;
317 
318  //Parse public key
319  error = sshParseBinaryString(data, length, &hostKey->q);
320  //Any error to report?
321  if(error)
322  return error;
323 
324  //Point to the next field
325  data += sizeof(uint32_t) + hostKey->q.length;
326  length -= sizeof(uint32_t) + hostKey->q.length;
327 
328  //Malformed message?
329  if(length != 0)
330  return ERROR_INVALID_MESSAGE;
331 
332  //Successful processing
333  return NO_ERROR;
334 #else
335  //Not implemented
336  return ERROR_NOT_IMPLEMENTED;
337 #endif
338 }
339 
340 
341 /**
342  * @brief Parse an Ed25519 host key structure
343  * @param[in] data Pointer to the host key structure
344  * @param[in] length Length of the host key structure, in bytes
345  * @param[out] hostKey Information resulting from the parsing process
346  * @return Error code
347  **/
348 
349 error_t sshParseEd25519HostKey(const uint8_t *data, size_t length,
350  SshEddsaHostKey *hostKey)
351 {
352 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
353  error_t error;
354 
355  //Decode key format identifier
356  error = sshParseString(data, length, &hostKey->keyFormatId);
357  //Any error to report?
358  if(error)
359  return error;
360 
361  //Unexpected key format identifier?
362  if(!sshCompareString(&hostKey->keyFormatId, "ssh-ed25519"))
363  return ERROR_WRONG_IDENTIFIER;
364 
365  //Point to the next field
366  data += sizeof(uint32_t) + hostKey->keyFormatId.length;
367  length -= sizeof(uint32_t) + hostKey->keyFormatId.length;
368 
369  //Parse Ed25519 public key
370  error = sshParseBinaryString(data, length, &hostKey->key);
371  //Any error to report?
372  if(error)
373  return error;
374 
375  //The public key shall consist of 32 octets
376  if(hostKey->key.length != ED25519_PUBLIC_KEY_LEN)
377  return ERROR_INVALID_SYNTAX;
378 
379  //Point to the next field
380  data += sizeof(uint32_t) + hostKey->key.length;
381  length -= sizeof(uint32_t) + hostKey->key.length;
382 
383  //Malformed host key?
384  if(length != 0)
385  return ERROR_INVALID_SYNTAX;
386 
387  //Successful processing
388  return NO_ERROR;
389 #else
390  //Not implemented
391  return ERROR_NOT_IMPLEMENTED;
392 #endif
393 }
394 
395 
396 /**
397  * @brief Parse an Ed448 host key structure
398  * @param[in] data Pointer to the host key structure
399  * @param[in] length Length of the host key structure, in bytes
400  * @param[out] hostKey Information resulting from the parsing process
401  * @return Error code
402  **/
403 
404 error_t sshParseEd448HostKey(const uint8_t *data, size_t length,
405  SshEddsaHostKey *hostKey)
406 {
407 #if (SSH_ED448_SIGN_SUPPORT == ENABLED)
408  error_t error;
409 
410  //Decode key format identifier
411  error = sshParseString(data, length, &hostKey->keyFormatId);
412  //Any error to report?
413  if(error)
414  return error;
415 
416  //Unexpected key format identifier?
417  if(!sshCompareString(&hostKey->keyFormatId, "ssh-ed448"))
418  return ERROR_WRONG_IDENTIFIER;
419 
420  //Point to the next field
421  data += sizeof(uint32_t) + hostKey->keyFormatId.length;
422  length -= sizeof(uint32_t) + hostKey->keyFormatId.length;
423 
424  //Parse Ed448 public key
425  error = sshParseBinaryString(data, length, &hostKey->key);
426  //Any error to report?
427  if(error)
428  return error;
429 
430  //The public key shall consist of 57 octets
431  if(hostKey->key.length != ED448_PUBLIC_KEY_LEN)
432  return ERROR_INVALID_SYNTAX;
433 
434  //Point to the next field
435  data += sizeof(uint32_t) + hostKey->key.length;
436  length -= sizeof(uint32_t) + hostKey->key.length;
437 
438  //Malformed host key?
439  if(length != 0)
440  return ERROR_INVALID_SYNTAX;
441 
442  //Successful processing
443  return NO_ERROR;
444 #else
445  //Not implemented
446  return ERROR_NOT_IMPLEMENTED;
447 #endif
448 }
449 
450 
451 /**
452  * @brief Parse an ML-DSA host key structure
453  * @param[in] data Pointer to the host key structure
454  * @param[in] length Length of the host key structure, in bytes
455  * @param[out] hostKey Information resulting from the parsing process
456  * @return Error code
457  **/
458 
459 error_t sshParseMldsaHostKey(const uint8_t *data, size_t length,
460  SshMldsaHostKey *hostKey)
461 {
462 #if (SSH_MLDSA44_SIGN_SUPPORT == ENABLED || SSH_MLDSA65_SIGN_SUPPORT == ENABLED || \
463  SSH_MLDSA87_SIGN_SUPPORT == ENABLED)
464  error_t error;
465  size_t n;
466 
467  //Decode key format identifier
468  error = sshParseString(data, length, &hostKey->keyFormatId);
469  //Any error to report?
470  if(error)
471  return error;
472 
473  //Check key format identifier
474  if(sshCompareString(&hostKey->keyFormatId, "ssh-mldsa-44"))
475  {
476  //The public key is a 1312-byte octet string
478  }
479  else if(sshCompareString(&hostKey->keyFormatId, "ssh-mldsa-65"))
480  {
481  //The public key is a 1952-byte octet string
483  }
484  else if(sshCompareString(&hostKey->keyFormatId, "ssh-mldsa-87"))
485  {
486  //The public key is a 2592-byte octet string
488  }
489  else
490  {
491  //Unexpected key format identifier
492  return ERROR_WRONG_IDENTIFIER;
493  }
494 
495  //Point to the next field
496  data += sizeof(uint32_t) + hostKey->keyFormatId.length;
497  length -= sizeof(uint32_t) + hostKey->keyFormatId.length;
498 
499  //Parse ML-DSA public key
500  error = sshParseBinaryString(data, length, &hostKey->key);
501  //Any error to report?
502  if(error)
503  return error;
504 
505  //Check the length of the public key
506  if(hostKey->key.length != n)
507  return ERROR_INVALID_SYNTAX;
508 
509  //Point to the next field
510  data += sizeof(uint32_t) + hostKey->key.length;
511  length -= sizeof(uint32_t) + hostKey->key.length;
512 
513  //Malformed host key?
514  if(length != 0)
515  return ERROR_INVALID_SYNTAX;
516 
517  //Successful processing
518  return NO_ERROR;
519 #else
520  //Not implemented
521  return ERROR_NOT_IMPLEMENTED;
522 #endif
523 }
524 
525 
526 /**
527  * @brief Parse private key header (OpenSSH format)
528  * @param[in] data Pointer to the private key structure
529  * @param[in] length Length of the private key structure, in bytes
530  * @param[out] privateKeyHeader Information resulting from the parsing process
531  * @return Error code
532  **/
533 
535  SshPrivateKeyHeader *privateKeyHeader)
536 {
537  error_t error;
538 
539  //Malformed private key?
541  return ERROR_INVALID_SYNTAX;
542 
543  //Check magic identifier
544  if(osMemcmp(data, "openssh-key-v1", SSH_AUTH_MAGIC_SIZE))
545  return ERROR_WRONG_IDENTIFIER;
546 
547  //Point to the next field
550 
551  //Parse 'ciphername' field
552  error = sshParseString(data, length, &privateKeyHeader->cipherName);
553  //Any error to report?
554  if(error)
555  return error;
556 
557  //Point to the next field
558  data += sizeof(uint32_t) + privateKeyHeader->cipherName.length;
559  length -= sizeof(uint32_t) + privateKeyHeader->cipherName.length;
560 
561  //Parse 'kdfname' field
562  error = sshParseString(data, length, &privateKeyHeader->kdfName);
563  //Any error to report?
564  if(error)
565  return error;
566 
567  //Point to the next field
568  data += sizeof(uint32_t) + privateKeyHeader->kdfName.length;
569  length -= sizeof(uint32_t) + privateKeyHeader->kdfName.length;
570 
571  //Parse 'kdfoptions' field
572  error = sshParseBinaryString(data, length, &privateKeyHeader->kdfOptions);
573  //Any error to report?
574  if(error)
575  return error;
576 
577  //Point to the next field
578  data += sizeof(uint32_t) + privateKeyHeader->kdfOptions.length;
579  length -= sizeof(uint32_t) + privateKeyHeader->kdfOptions.length;
580 
581  //Malformed private key?
582  if(length < sizeof(uint32_t))
583  return ERROR_INVALID_SYNTAX;
584 
585  //Parse 'number of keys' field
586  privateKeyHeader->numKeys = LOAD32BE(data);
587 
588  //The implementation supports only one public and private key
589  if(privateKeyHeader->numKeys != 1)
590  return ERROR_INVALID_SYNTAX;
591 
592  //Point to the next field
593  data += sizeof(uint32_t);
594  length -= sizeof(uint32_t);
595 
596  //Parse 'publickey' field
597  error = sshParseBinaryString(data, length, &privateKeyHeader->publicKey);
598  //Any error to report?
599  if(error)
600  return error;
601 
602  //Point to the next field
603  data += sizeof(uint32_t) + privateKeyHeader->publicKey.length;
604  length -= sizeof(uint32_t) + privateKeyHeader->publicKey.length;
605 
606  //Parse 'encrypted' field
607  error = sshParseBinaryString(data, length, &privateKeyHeader->encrypted);
608  //Any error to report?
609  if(error)
610  return error;
611 
612  //Point to the next field
613  data += sizeof(uint32_t) + privateKeyHeader->encrypted.length;
614  length -= sizeof(uint32_t) + privateKeyHeader->encrypted.length;
615 
616  //Malformed private key?
617  if(length != 0)
618  return ERROR_INVALID_SYNTAX;
619 
620  //Successful processing
621  return NO_ERROR;
622 }
623 
624 
625 /**
626  * @brief Parse RSA private key blob (OpenSSH format)
627  * @param[in] data Pointer to the private key blob
628  * @param[in] length Length of the private key blob, in bytes
629  * @param[out] privateKey Information resulting from the parsing process
630  * @return Error code
631  **/
632 
634  SshRsaPrivateKey *privateKey)
635 {
636 #if (SSH_RSA_SIGN_SUPPORT == ENABLED)
637  error_t error;
638 
639  //Malformed private key blob?
640  if(length < sizeof(uint64_t))
641  return ERROR_INVALID_SYNTAX;
642 
643  //Decode 'checkint' fields
644  privateKey->checkInt1 = LOAD32BE(data);
645  privateKey->checkInt2 = LOAD32BE(data + 4);
646 
647  //Before the key is encrypted, a random integer is assigned to both
648  //'checkint' fields so successful decryption can be quickly checked
649  //by verifying that both checkint fields hold the same value
650  if(privateKey->checkInt1 != privateKey->checkInt2)
651  return ERROR_INVALID_SYNTAX;
652 
653  //Point to the next field
654  data += sizeof(uint64_t);
655  length -= sizeof(uint64_t);
656 
657  //Decode key format identifier
658  error = sshParseString(data, length, &privateKey->keyFormatId);
659  //Any error to report?
660  if(error)
661  return error;
662 
663  //Unexpected key format identifier?
664  if(!sshCompareString(&privateKey->keyFormatId, "ssh-rsa"))
665  return ERROR_WRONG_IDENTIFIER;
666 
667  //Point to the next field
668  data += sizeof(uint32_t) + privateKey->keyFormatId.length;
669  length -= sizeof(uint32_t) + privateKey->keyFormatId.length;
670 
671  //Parse RSA modulus
672  error = sshParseBinaryString(data, length, &privateKey->n);
673  //Any error to report?
674  if(error)
675  return error;
676 
677  //Point to the next field
678  data += sizeof(uint32_t) + privateKey->n.length;
679  length -= sizeof(uint32_t) + privateKey->n.length;
680 
681  //Parse RSA public exponent
682  error = sshParseBinaryString(data, length, &privateKey->e);
683  //Any error to report?
684  if(error)
685  return error;
686 
687  //Point to the next field
688  data += sizeof(uint32_t) + privateKey->e.length;
689  length -= sizeof(uint32_t) + privateKey->e.length;
690 
691  //Parse RSA private exponent
692  error = sshParseBinaryString(data, length, &privateKey->d);
693  //Any error to report?
694  if(error)
695  return error;
696 
697  //Point to the next field
698  data += sizeof(uint32_t) + privateKey->d.length;
699  length -= sizeof(uint32_t) + privateKey->d.length;
700 
701  //Parse RSA CRT coefficient
702  error = sshParseBinaryString(data, length, &privateKey->qinv);
703  //Any error to report?
704  if(error)
705  return error;
706 
707  //Point to the next field
708  data += sizeof(uint32_t) + privateKey->qinv.length;
709  length -= sizeof(uint32_t) + privateKey->qinv.length;
710 
711  //Parse RSA first factor
712  error = sshParseBinaryString(data, length, &privateKey->p);
713  //Any error to report?
714  if(error)
715  return error;
716 
717  //Point to the next field
718  data += sizeof(uint32_t) + privateKey->p.length;
719  length -= sizeof(uint32_t) + privateKey->p.length;
720 
721  //Parse RSA second factor
722  error = sshParseBinaryString(data, length, &privateKey->q);
723  //Any error to report?
724  if(error)
725  return error;
726 
727  //Point to the next field
728  data += sizeof(uint32_t) + privateKey->q.length;
729  length -= sizeof(uint32_t) + privateKey->q.length;
730 
731  //The private key is followed by a comment
732  error = sshParseString(data, length, &privateKey->comment);
733  //Any error to report?
734  if(error)
735  return error;
736 
737  //Point to the next field
738  data += sizeof(uint32_t) + privateKey->comment.length;
739  length -= sizeof(uint32_t) + privateKey->comment.length;
740 
741  //The serialized private key is padded to the cipher block size
743 #else
744  //Not implemented
745  return ERROR_NOT_IMPLEMENTED;
746 #endif
747 }
748 
749 
750 /**
751  * @brief Parse DSA private key blob (OpenSSH format)
752  * @param[in] data Pointer to the private key blob
753  * @param[in] length Length of the private key blob, in bytes
754  * @param[out] privateKey Information resulting from the parsing process
755  * @return Error code
756  **/
757 
759  SshDsaPrivateKey *privateKey)
760 {
761 #if (SSH_DSA_SIGN_SUPPORT == ENABLED)
762  error_t error;
763 
764  //Malformed private key blob?
765  if(length < sizeof(uint64_t))
766  return ERROR_INVALID_SYNTAX;
767 
768  //Decode 'checkint' fields
769  privateKey->checkInt1 = LOAD32BE(data);
770  privateKey->checkInt2 = LOAD32BE(data + 4);
771 
772  //Before the key is encrypted, a random integer is assigned to both
773  //'checkint' fields so successful decryption can be quickly checked
774  //by verifying that both checkint fields hold the same value
775  if(privateKey->checkInt1 != privateKey->checkInt2)
776  return ERROR_INVALID_SYNTAX;
777 
778  //Point to the next field
779  data += sizeof(uint64_t);
780  length -= sizeof(uint64_t);
781 
782  //Decode key format identifier
783  error = sshParseString(data, length, &privateKey->keyFormatId);
784  //Any error to report?
785  if(error)
786  return error;
787 
788  //Unexpected key format identifier?
789  if(!sshCompareString(&privateKey->keyFormatId, "ssh-dss"))
790  return ERROR_WRONG_IDENTIFIER;
791 
792  //Point to the next field
793  data += sizeof(uint32_t) + privateKey->keyFormatId.length;
794  length -= sizeof(uint32_t) + privateKey->keyFormatId.length;
795 
796  //Parse DSA prime modulus
797  error = sshParseBinaryString(data, length, &privateKey->p);
798  //Any error to report?
799  if(error)
800  return error;
801 
802  //Point to the next field
803  data += sizeof(uint32_t) + privateKey->p.length;
804  length -= sizeof(uint32_t) + privateKey->p.length;
805 
806  //Parse DSA group order
807  error = sshParseBinaryString(data, length, &privateKey->q);
808  //Any error to report?
809  if(error)
810  return error;
811 
812  //Point to the next field
813  data += sizeof(uint32_t) + privateKey->q.length;
814  length -= sizeof(uint32_t) + privateKey->q.length;
815 
816  //Parse DSA group generator
817  error = sshParseBinaryString(data, length, &privateKey->g);
818  //Any error to report?
819  if(error)
820  return error;
821 
822  //Point to the next field
823  data += sizeof(uint32_t) + privateKey->g.length;
824  length -= sizeof(uint32_t) + privateKey->g.length;
825 
826  //Parse DSA public key value
827  error = sshParseBinaryString(data, length, &privateKey->y);
828  //Any error to report?
829  if(error)
830  return error;
831 
832  //Point to the next field
833  data += sizeof(uint32_t) + privateKey->y.length;
834  length -= sizeof(uint32_t) + privateKey->y.length;
835 
836  //Parse DSA private key value
837  error = sshParseBinaryString(data, length, &privateKey->x);
838  //Any error to report?
839  if(error)
840  return error;
841 
842  //Point to the next field
843  data += sizeof(uint32_t) + privateKey->x.length;
844  length -= sizeof(uint32_t) + privateKey->x.length;
845 
846  //The private key is followed by a comment
847  error = sshParseString(data, length, &privateKey->comment);
848  //Any error to report?
849  if(error)
850  return error;
851 
852  //Point to the next field
853  data += sizeof(uint32_t) + privateKey->comment.length;
854  length -= sizeof(uint32_t) + privateKey->comment.length;
855 
856  //The serialized private key is padded to the cipher block size
858 #else
859  //Not implemented
860  return ERROR_NOT_IMPLEMENTED;
861 #endif
862 }
863 
864 
865 /**
866  * @brief Parse ECDSA private key blob (OpenSSH format)
867  * @param[in] data Pointer to the private key blob
868  * @param[in] length Length of the private key blob, in bytes
869  * @param[out] privateKey Information resulting from the parsing process
870  * @return Error code
871  **/
872 
874  SshEcdsaPrivateKey *privateKey)
875 {
876 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED)
877  error_t error;
878 
879  //Malformed private key blob?
880  if(length < sizeof(uint64_t))
881  return ERROR_INVALID_SYNTAX;
882 
883  //Decode 'checkint' fields
884  privateKey->checkInt1 = LOAD32BE(data);
885  privateKey->checkInt2 = LOAD32BE(data + 4);
886 
887  //Before the key is encrypted, a random integer is assigned to both
888  //'checkint' fields so successful decryption can be quickly checked
889  //by verifying that both checkint fields hold the same value
890  if(privateKey->checkInt1 != privateKey->checkInt2)
891  return ERROR_INVALID_SYNTAX;
892 
893  //Point to the next field
894  data += sizeof(uint64_t);
895  length -= sizeof(uint64_t);
896 
897  //Decode key format identifier
898  error = sshParseString(data, length, &privateKey->keyFormatId);
899  //Any error to report?
900  if(error)
901  return error;
902 
903  //Unexpected key format identifier?
904  if(!sshCompareString(&privateKey->keyFormatId, "ecdsa-sha2-nistp256") &&
905  !sshCompareString(&privateKey->keyFormatId, "ecdsa-sha2-nistp384") &&
906  !sshCompareString(&privateKey->keyFormatId, "ecdsa-sha2-nistp521"))
907  {
908  return ERROR_WRONG_IDENTIFIER;
909  }
910 
911  //Point to the next field
912  data += sizeof(uint32_t) + privateKey->keyFormatId.length;
913  length -= sizeof(uint32_t) + privateKey->keyFormatId.length;
914 
915  //Parse elliptic curve domain parameter identifier
916  error = sshParseString(data, length, &privateKey->curveName);
917  //Any error to report?
918  if(error)
919  return error;
920 
921  //Point to the next field
922  data += sizeof(uint32_t) + privateKey->curveName.length;
923  length -= sizeof(uint32_t) + privateKey->curveName.length;
924 
925  //Parse public key
926  error = sshParseBinaryString(data, length, &privateKey->q);
927  //Any error to report?
928  if(error)
929  return error;
930 
931  //Point to the next field
932  data += sizeof(uint32_t) + privateKey->q.length;
933  length -= sizeof(uint32_t) + privateKey->q.length;
934 
935  //Parse private key
936  error = sshParseBinaryString(data, length, &privateKey->d);
937  //Any error to report?
938  if(error)
939  return error;
940 
941  //Point to the next field
942  data += sizeof(uint32_t) + privateKey->d.length;
943  length -= sizeof(uint32_t) + privateKey->d.length;
944 
945  //The private key is followed by a comment
946  error = sshParseString(data, length, &privateKey->comment);
947  //Any error to report?
948  if(error)
949  return error;
950 
951  //Point to the next field
952  data += sizeof(uint32_t) + privateKey->comment.length;
953  length -= sizeof(uint32_t) + privateKey->comment.length;
954 
955  //The serialized private key is padded to the cipher block size
957 #else
958  //Not implemented
959  return ERROR_NOT_IMPLEMENTED;
960 #endif
961 }
962 
963 
964 /**
965  * @brief Parse Ed25519 private key blob (OpenSSH format)
966  * @param[in] data Pointer to the private key blob
967  * @param[in] length Length of the private key blob, in bytes
968  * @param[out] privateKey Information resulting from the parsing process
969  * @return Error code
970  **/
971 
973  SshEddsaPrivateKey *privateKey)
974 {
975 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
976  error_t error;
977 
978  //Malformed private key blob?
979  if(length < sizeof(uint64_t))
980  return ERROR_INVALID_SYNTAX;
981 
982  //Decode 'checkint' fields
983  privateKey->checkInt1 = LOAD32BE(data);
984  privateKey->checkInt2 = LOAD32BE(data + 4);
985 
986  //Before the key is encrypted, a random integer is assigned to both
987  //'checkint' fields so successful decryption can be quickly checked
988  //by verifying that both checkint fields hold the same value
989  if(privateKey->checkInt1 != privateKey->checkInt2)
990  return ERROR_INVALID_SYNTAX;
991 
992  //Point to the next field
993  data += sizeof(uint64_t);
994  length -= sizeof(uint64_t);
995 
996  //Decode key format identifier
997  error = sshParseString(data, length, &privateKey->keyFormatId);
998  //Any error to report?
999  if(error)
1000  return error;
1001 
1002  //Unexpected key format identifier?
1003  if(!sshCompareString(&privateKey->keyFormatId, "ssh-ed25519"))
1004  return ERROR_WRONG_IDENTIFIER;
1005 
1006  //Point to the next field
1007  data += sizeof(uint32_t) + privateKey->keyFormatId.length;
1008  length -= sizeof(uint32_t) + privateKey->keyFormatId.length;
1009 
1010  //Parse Ed25519 public key
1011  error = sshParseBinaryString(data, length, &privateKey->q);
1012  //Any error to report?
1013  if(error)
1014  return error;
1015 
1016  //The public key shall consist of 32 octets
1017  if(privateKey->q.length != ED25519_PUBLIC_KEY_LEN)
1018  return ERROR_INVALID_SYNTAX;
1019 
1020  //Point to the next field
1021  data += sizeof(uint32_t) + privateKey->q.length;
1022  length -= sizeof(uint32_t) + privateKey->q.length;
1023 
1024  //Parse Ed25519 private key
1025  error = sshParseBinaryString(data, length, &privateKey->d);
1026  //Any error to report?
1027  if(error)
1028  return error;
1029 
1030  //The private key shall consist of 64 octets
1032  return ERROR_INVALID_SYNTAX;
1033 
1034  //Point to the next field
1035  data += sizeof(uint32_t) + privateKey->d.length;
1036  length -= sizeof(uint32_t) + privateKey->d.length;
1037 
1038  //The private key is followed by a comment
1039  error = sshParseString(data, length, &privateKey->comment);
1040  //Any error to report?
1041  if(error)
1042  return error;
1043 
1044  //Point to the next field
1045  data += sizeof(uint32_t) + privateKey->comment.length;
1046  length -= sizeof(uint32_t) + privateKey->comment.length;
1047 
1048  //The serialized private key is padded to the cipher block size
1050 #else
1051  //Not implemented
1052  return ERROR_NOT_IMPLEMENTED;
1053 #endif
1054 }
1055 
1056 
1057 /**
1058  * @brief Parse Ed448 private key blob (OpenSSH format)
1059  * @param[in] data Pointer to the private key blob
1060  * @param[in] length Length of the private key blob, in bytes
1061  * @param[out] privateKey Information resulting from the parsing process
1062  * @return Error code
1063  **/
1064 
1066  SshEddsaPrivateKey *privateKey)
1067 {
1068 #if (SSH_ED448_SIGN_SUPPORT == ENABLED)
1069  error_t error;
1070 
1071  //Malformed private key blob?
1072  if(length < sizeof(uint64_t))
1073  return ERROR_INVALID_SYNTAX;
1074 
1075  //Decode 'checkint' fields
1076  privateKey->checkInt1 = LOAD32BE(data);
1077  privateKey->checkInt2 = LOAD32BE(data + 4);
1078 
1079  //Before the key is encrypted, a random integer is assigned to both
1080  //'checkint' fields so successful decryption can be quickly checked
1081  //by verifying that both checkint fields hold the same value
1082  if(privateKey->checkInt1 != privateKey->checkInt2)
1083  return ERROR_INVALID_SYNTAX;
1084 
1085  //Point to the next field
1086  data += sizeof(uint64_t);
1087  length -= sizeof(uint64_t);
1088 
1089  //Decode key format identifier
1090  error = sshParseString(data, length, &privateKey->keyFormatId);
1091  //Any error to report?
1092  if(error)
1093  return error;
1094 
1095  //Unexpected key format identifier?
1096  if(!sshCompareString(&privateKey->keyFormatId, "ssh-ed448"))
1097  return ERROR_WRONG_IDENTIFIER;
1098 
1099  //Point to the next field
1100  data += sizeof(uint32_t) + privateKey->keyFormatId.length;
1101  length -= sizeof(uint32_t) + privateKey->keyFormatId.length;
1102 
1103  //Parse Ed448 public key
1104  error = sshParseBinaryString(data, length, &privateKey->q);
1105  //Any error to report?
1106  if(error)
1107  return error;
1108 
1109  //The public key shall consist of 57 octets
1110  if(privateKey->q.length != ED448_PUBLIC_KEY_LEN)
1111  return ERROR_INVALID_SYNTAX;
1112 
1113  //Point to the next field
1114  data += sizeof(uint32_t) + privateKey->q.length;
1115  length -= sizeof(uint32_t) + privateKey->q.length;
1116 
1117  //Parse Ed448 private key
1118  error = sshParseBinaryString(data, length, &privateKey->d);
1119  //Any error to report?
1120  if(error)
1121  return error;
1122 
1123  //The private key shall consist of 114 octets
1124  if(privateKey->d.length != (ED448_PRIVATE_KEY_LEN + ED448_PUBLIC_KEY_LEN))
1125  return ERROR_INVALID_SYNTAX;
1126 
1127  //Point to the next field
1128  data += sizeof(uint32_t) + privateKey->d.length;
1129  length -= sizeof(uint32_t) + privateKey->d.length;
1130 
1131  //The private key is followed by a comment
1132  error = sshParseString(data, length, &privateKey->comment);
1133  //Any error to report?
1134  if(error)
1135  return error;
1136 
1137  //Point to the next field
1138  data += sizeof(uint32_t) + privateKey->comment.length;
1139  length -= sizeof(uint32_t) + privateKey->comment.length;
1140 
1141  //The serialized private key is padded to the cipher block size
1143 #else
1144  //Not implemented
1145  return ERROR_NOT_IMPLEMENTED;
1146 #endif
1147 }
1148 
1149 
1150 /**
1151  * @brief Check padding string
1152  * @param[in] pad Pointer to the padding string
1153  * @param[in] length Length of the padding string, in bytes
1154  * @return Error code
1155  **/
1156 
1157 error_t sshCheckPrivateKeyPadding(const uint8_t *pad, size_t length)
1158 {
1159  error_t error;
1160  size_t i;
1161 
1162  //Initialize status code
1163  error = NO_ERROR;
1164 
1165  //The list of private key / comment pairs is padded with the bytes 1, 2,
1166  //3, ... until the total length is a multiple of the cipher block size
1167  for(i = 0; i < length && error == NO_ERROR; i++)
1168  {
1169  //Check the value of the current padding byte
1170  if(pad[i] != ((i + 1) & 0xFF))
1171  {
1172  error = ERROR_INVALID_PADDING;
1173  }
1174  }
1175 
1176  //Return status code
1177  return error;
1178 }
1179 
1180 #endif
error_t sshParseOpenSshDsaPrivateKey(const uint8_t *data, size_t length, SshDsaPrivateKey *privateKey)
Parse DSA private key blob (OpenSSH format)
EdDSA private key (OpenSSH format)
SshBinaryString q
Definition: ssh_key_parse.h:67
error_t sshParseMldsaHostKey(const uint8_t *data, size_t length, SshMldsaHostKey *hostKey)
Parse an ML-DSA host key structure.
SshBinaryString y
Definition: ssh_key_parse.h:69
SshBinaryString q
#define LOAD32BE(p)
Definition: cpu_endian.h:210
error_t sshParseOpenSshPrivateKeyHeader(const uint8_t *data, size_t length, SshPrivateKeyHeader *privateKeyHeader)
Parse private key header (OpenSSH format)
error_t sshParseDsaHostKey(const uint8_t *data, size_t length, SshDsaHostKey *hostKey)
Parse a DSA host key structure.
error_t sshCheckPrivateKeyPadding(const uint8_t *pad, size_t length)
Check padding string.
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
DSA host key.
Definition: ssh_key_parse.h:64
#define ED25519_PUBLIC_KEY_LEN
Definition: ed25519.h:42
SshString keyFormatId
Definition: ssh_key_parse.h:65
SshBinaryString d
ECDSA private key (OpenSSH format)
error_t sshParseEd448HostKey(const uint8_t *data, size_t length, SshEddsaHostKey *hostKey)
Parse an Ed448 host key structure.
#define ED448_PUBLIC_KEY_LEN
Definition: ed448.h:42
uint8_t data[]
Definition: ethernet.h:224
DSA private key (OpenSSH format)
error_t sshParseString(const uint8_t *p, size_t length, SshString *string)
Parse a string.
Definition: ssh_misc.c:1178
SshString keyFormatId
Definition: ssh_key_parse.h:79
#define osMemcmp(p1, p2, length)
Definition: os_port.h:159
SshString keyFormatId
SshBinaryString g
Definition: ssh_key_parse.h:68
SshBinaryString n
Definition: ssh_key_parse.h:55
size_t length
Definition: ssh_types.h:58
SSH key parsing.
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
SshBinaryString key
error_t sshParseRsaHostKey(const uint8_t *data, size_t length, SshRsaHostKey *hostKey)
Parse an RSA host key structure.
SshBinaryString q
SshString keyFormatId
#define ED25519_PRIVATE_KEY_LEN
Definition: ed25519.h:40
Private key header (OpenSSH format)
bool_t sshCompareString(const SshString *string, const char_t *value)
Compare a binary string against the supplied value.
Definition: ssh_misc.c:1691
SshBinaryString encrypted
size_t length
Definition: ssh_types.h:69
SshBinaryString q
Definition: ssh_key_parse.h:81
@ ERROR_INVALID_PADDING
Definition: error.h:112
error_t
Error codes.
Definition: error.h:43
SshBinaryString n
RSA private key (OpenSSH format)
SshString keyFormatId
SshBinaryString e
SshBinaryString kdfOptions
SshString keyFormatId
Definition: ssh_key_parse.h:53
SshBinaryString p
Definition: ssh_key_parse.h:66
EdDSA (Edwards-Curve Digital Signature Algorithm)
SshBinaryString d
SshBinaryString e
Definition: ssh_key_parse.h:54
ML-DSA (Edwards-Curve Digital Signature Algorithm)
error_t sshParseEcdsaHostKey(const uint8_t *data, size_t length, SshEcdsaHostKey *hostKey)
Parse an ECDSA host key structure.
error_t sshParseOpenSshEcdsaPrivateKey(const uint8_t *data, size_t length, SshEcdsaPrivateKey *privateKey)
Parse ECDSA private key blob (OpenSSH format)
error_t sshParseEd25519HostKey(const uint8_t *data, size_t length, SshEddsaHostKey *hostKey)
Parse an Ed25519 host key structure.
#define MLDSA44_PUBLIC_KEY_LEN
Definition: mldsa.h:45
EdDSA host key.
Definition: ssh_key_parse.h:90
uint8_t length
Definition: tcp.h:375
SshBinaryString y
SshBinaryString qinv
String.
Definition: ssh_types.h:56
SshString keyFormatId
Definition: ssh_key_parse.h:91
ML-DSA host key.
SshBinaryString d
#define ED448_PRIVATE_KEY_LEN
Definition: ed448.h:40
uint8_t n
SshBinaryString g
error_t sshParseOpenSshEd448PrivateKey(const uint8_t *data, size_t length, SshEddsaPrivateKey *privateKey)
Parse Ed448 private key blob (OpenSSH format)
ECDSA host key.
Definition: ssh_key_parse.h:78
SshBinaryString q
error_t sshParseOpenSshRsaPrivateKey(const uint8_t *data, size_t length, SshRsaPrivateKey *privateKey)
Parse RSA private key blob (OpenSSH format)
#define MLDSA65_PUBLIC_KEY_LEN
Definition: mldsa.h:56
SSH helper functions.
@ ERROR_WRONG_IDENTIFIER
Definition: error.h:89
RSA host key.
Definition: ssh_key_parse.h:52
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
SshBinaryString p
error_t sshParseBinaryString(const uint8_t *p, size_t length, SshBinaryString *string)
Parse a binary string.
Definition: ssh_misc.c:1215
SshBinaryString x
#define SSH_AUTH_MAGIC_SIZE
Definition: ssh_key_parse.h:38
error_t sshParseHostKey(const uint8_t *data, size_t length, SshString *keyFormatId)
Parse host key structure.
Definition: ssh_key_parse.c:54
SshBinaryString p
SshBinaryString q
Secure Shell (SSH)
error_t sshParseOpenSshEd25519PrivateKey(const uint8_t *data, size_t length, SshEddsaPrivateKey *privateKey)
Parse Ed25519 private key blob (OpenSSH format)
SshString curveName
Definition: ssh_key_parse.h:80
SshBinaryString publicKey
#define MLDSA87_PUBLIC_KEY_LEN
Definition: mldsa.h:67
@ ERROR_INVALID_KEY
Definition: error.h:106
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
SshBinaryString key
Definition: ssh_key_parse.h:92