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