ssh_cert_import.c
Go to the documentation of this file.
1 /**
2  * @file ssh_cert_import.c
3  * @brief SSH certificate import functions
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_cert_import.h"
37 #include "ssh/ssh_misc.h"
38 #include "encoding/base64.h"
39 #include "debug.h"
40 
41 //Check SSH stack configuration
42 #if (SSH_SUPPORT == ENABLED && SSH_CERT_SUPPORT == ENABLED)
43 
44 
45 /**
46  * @brief List of supported certificate types
47  **/
48 
49 static const char_t *const sshCertTypes[] =
50 {
51 #if (SSH_RSA_SIGN_SUPPORT == ENABLED)
52  "ssh-rsa-cert",
53  "ssh-rsa-cert-v01@openssh.com",
54 #endif
55 #if (SSH_DSA_SIGN_SUPPORT == ENABLED)
56  "ssh-dss-cert",
57  "ssh-dss-cert-v01@openssh.com",
58 #endif
59 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP256_SUPPORT == ENABLED)
60  "ecdsa-sha2-nistp256-cert",
61  "ecdsa-sha2-nistp256-cert-v01@openssh.com",
62 #endif
63 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP384_SUPPORT == ENABLED)
64  "ecdsa-sha2-nistp384-cert",
65  "ecdsa-sha2-nistp384-cert-v01@openssh.com",
66 #endif
67 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP521_SUPPORT == ENABLED)
68  "ecdsa-sha2-nistp521-cert",
69  "ecdsa-sha2-nistp521-cert-v01@openssh.com",
70 #endif
71 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
72  "ssh-ed25519-cert",
73  "ssh-ed25519-cert-v01@openssh.com",
74 #endif
75 #if (SSH_ED448_SIGN_SUPPORT == ENABLED)
76  "ssh-ed448-cert"
77 #endif
78 };
79 
80 
81 /**
82  * @brief Import SSH certificate (OpenSSH format)
83  * @param[in] input SSH certificate file to decode
84  * @param[in] inputLen Length of the SSH certificate file to decode
85  * @param[out] output Pointer to the decoded data (optional parameter)
86  * @param[out] outputLen Length of the decoded data
87  * @return Error code
88  **/
89 
90 error_t sshImportCertificate(const char_t *input, size_t inputLen,
91  uint8_t *output, size_t *outputLen)
92 {
93  error_t error;
94  size_t i;
95  size_t j;
96  const char_t *certType;
97 
98  //Retrieve certificate type
99  certType = sshGetCertType(input, inputLen);
100  //Unrecognized certificate type?
101  if(certType == NULL)
102  return ERROR_INVALID_SYNTAX;
103 
104  //Get the length of the identifier string
105  i = osStrlen(certType);
106 
107  //The identifier must be followed by a whitespace character
108  if(input[i] != ' ' && input[i] != '\t')
109  return ERROR_INVALID_SYNTAX;
110 
111  //Skip whitespace characters
112  while(i < inputLen && (input[i] == ' ' || input[i] == '\t'))
113  {
114  i++;
115  }
116 
117  //Point to the certificate
118  j = i;
119 
120  //The certificate may be followed by a whitespace character and a comment
121  while(j < inputLen && (input[j] != ' ' && input[j] != '\t'))
122  {
123  j++;
124  }
125 
126  //The certificate is Base64-encoded
127  error = base64Decode(input + i, j - i, output, outputLen);
128  //Failed to decode the file?
129  if(error)
130  return error;
131 
132  //Sanity check
133  if(*outputLen == 0)
134  return ERROR_INVALID_SYNTAX;
135 
136  //Successful processing
137  return NO_ERROR;
138 }
139 
140 
141 /**
142  * @brief Import an RSA public key from a certificate
143  * @param[out] publicKey Pointer to the RSA public key
144  * @param[in] cert Pointer to the certificate structure
145  * @return Error code
146  **/
147 
149  const SshCertificate *cert)
150 {
151 #if (SSH_RSA_SIGN_SUPPORT == ENABLED)
152  error_t error;
153  uint_t k;
154 
155  //Unexpected key format identifier?
156  if(!sshCompareString(&cert->keyFormatId, "ssh-rsa-cert") &&
157  !sshCompareString(&cert->keyFormatId, "ssh-rsa-cert-v01@openssh.com"))
158  {
159  return ERROR_WRONG_IDENTIFIER;
160  }
161 
162  //Import RSA public exponent
163  error = mpiImport(&publicKey->e, cert->publicKey.rsaPublicKey.e.value,
165  //Any error to report?
166  if(error)
167  return error;
168 
169  //Import RSA modulus
170  error = mpiImport(&publicKey->n, cert->publicKey.rsaPublicKey.n.value,
172  //Any error to report?
173  if(error)
174  return error;
175 
176  //Get the length of the modulus, in bits
177  k = mpiGetBitLength(&publicKey->n);
178 
179  //Applications should enforce minimum and maximum key sizes
180  if(k < SSH_MIN_RSA_MODULUS_SIZE || k > SSH_MAX_RSA_MODULUS_SIZE)
182 
183  //Successful processing
184  return NO_ERROR;
185 #else
186  //Not implemented
187  return ERROR_NOT_IMPLEMENTED;
188 #endif
189 }
190 
191 
192 /**
193  * @brief Import a DSA public key from a certificate
194  * @param[out] publicKey Pointer to the DSA public key
195  * @param[in] cert Pointer to the certificate structure
196  * @return Error code
197  **/
198 
200  const SshCertificate *cert)
201 {
202 #if (SSH_DSA_SIGN_SUPPORT == ENABLED)
203  error_t error;
204  size_t k;
205 
206  //Unexpected key format identifier?
207  if(!sshCompareString(&cert->keyFormatId, "ssh-dss-cert") &&
208  !sshCompareString(&cert->keyFormatId, "ssh-dss-cert-v01@openssh.com"))
209  {
210  return ERROR_WRONG_IDENTIFIER;
211  }
212 
213  //Import DSA prime modulus
214  error = mpiImport(&publicKey->params.p, cert->publicKey.dsaPublicKey.p.value,
216  //Any error to report?
217  if(error)
218  return error;
219 
220  //Import DSA group order
221  error = mpiImport(&publicKey->params.q, cert->publicKey.dsaPublicKey.q.value,
223  //Any error to report?
224  if(error)
225  return error;
226 
227  //Import DSA group generator
228  error = mpiImport(&publicKey->params.g, cert->publicKey.dsaPublicKey.g.value,
230  //Any error to report?
231  if(error)
232  return error;
233 
234  //Import DSA public key value
235  error = mpiImport(&publicKey->y, cert->publicKey.dsaPublicKey.y.value,
237  //Any error to report?
238  if(error)
239  return error;
240 
241  //Get the length of the modulus, in bits
242  k = mpiGetBitLength(&publicKey->params.p);
243 
244  //Applications should enforce minimum and maximum key sizes
245  if(k < SSH_MIN_DSA_MODULUS_SIZE || k > SSH_MAX_DSA_MODULUS_SIZE)
247 
248  //Successful processing
249  return NO_ERROR;
250 #else
251  //Not implemented
252  return ERROR_NOT_IMPLEMENTED;
253 #endif
254 }
255 
256 
257 /**
258  * @brief Import an ECDSA public key from a certificate
259  * @param[out] publicKey Pointer to the ECDSA public key
260  * @param[in] cert Pointer to the certificate structure
261  * @return Error code
262  **/
263 
265  const SshCertificate *cert)
266 {
267 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED)
268  error_t error;
269  const EcCurve *curve;
270 
271  //Check key format identifier
272  if(sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp256-cert") ||
273  sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp384-cert") ||
274  sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp521-cert") ||
275  sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp256-cert-v01@openssh.com") ||
276  sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp384-cert-v01@openssh.com") ||
277  sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp521-cert-v01@openssh.com"))
278  {
279  //Retrieve the elliptic curve that matches the specified key format
280  //identifier
281  curve = sshGetCurve(&cert->keyFormatId,
283 
284  //Make sure the key format identifier is acceptable
285  if(curve != NULL)
286  {
287  //Import EC public key
288  error = ecImportPublicKey(publicKey, curve,
292  }
293  else
294  {
295  //Report an error
296  error = ERROR_WRONG_IDENTIFIER;
297  }
298  }
299  else
300  {
301  //Unexpected key format identifier
302  error = ERROR_WRONG_IDENTIFIER;
303  }
304 
305  //Return status code
306  return error;
307 #else
308  //Not implemented
309  return ERROR_NOT_IMPLEMENTED;
310 #endif
311 }
312 
313 
314 /**
315  * @brief Import an Ed25519 public key from a certificate
316  * @param[out] publicKey Pointer to the EdDSA public key
317  * @param[in] cert Pointer to the certificate structure
318  * @return Error code
319  **/
320 
322  const SshCertificate *cert)
323 {
324 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
325  error_t error;
326 
327  //Check key format identifier
328  if(sshCompareString(&cert->keyFormatId, "ssh-ed25519-cert") ||
329  sshCompareString(&cert->keyFormatId, "ssh-ed25519-cert-v01@openssh.com"))
330  {
331  //Import Ed25519 public key
332  error = eddsaImportPublicKey(publicKey, ED25519_CURVE,
335  }
336  else
337  {
338  //Unexpected key format identifier
339  error = ERROR_WRONG_IDENTIFIER;
340  }
341 
342  //Return status code
343  return error;
344 #else
345  //Not implemented
346  return ERROR_NOT_IMPLEMENTED;
347 #endif
348 }
349 
350 
351 /**
352  * @brief Import an Ed448 public key from a certificate
353  * @param[out] publicKey Pointer to the EdDSA public key
354  * @param[in] cert Pointer to the certificate structure
355  * @return Error code
356  **/
357 
359  const SshCertificate *cert)
360 {
361 #if (SSH_ED448_SIGN_SUPPORT == ENABLED)
362  error_t error;
363 
364  //Check key format identifier
365  if(sshCompareString(&cert->keyFormatId, "ssh-ed448-cert"))
366  {
367  //Import Ed448 public key
368  error = eddsaImportPublicKey(publicKey, ED448_CURVE,
371  }
372  else
373  {
374  //Unexpected key format identifier
375  error = ERROR_WRONG_IDENTIFIER;
376  }
377 
378  //Return status code
379  return error;
380 #else
381  //Not implemented
382  return ERROR_NOT_IMPLEMENTED;
383 #endif
384 }
385 
386 
387 /**
388  * @brief Get SSH certificate type
389  * @param[in] input SSH certificate file
390  * @param[in] length Length of the SSH certificate file
391  * @return SSH certificate type
392  **/
393 
394 const char_t *sshGetCertType(const char_t *input, size_t length)
395 {
396  uint_t i;
397  size_t n;
398  const char_t *certType;
399 
400  //Initialize certificate type
401  certType = NULL;
402 
403  //Loop through the list of identifiers
404  for(i = 0; i < arraysize(sshCertTypes); i++)
405  {
406  //Get the length of the identifier
407  n = osStrlen(sshCertTypes[i]);
408 
409  //Matching identifier?
410  if(length > n && osMemcmp(input, sshCertTypes[i], n) == 0)
411  {
412  //The identifier must be followed by a whitespace character
413  if(input[n] == ' ' || input[n] == '\t')
414  {
415  certType = sshCertTypes[i];
416  break;
417  }
418  }
419  }
420 
421  //Return certificate type
422  return certType;
423 }
424 
425 #endif
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
Mpi q
Group order.
Definition: dsa.h:51
error_t ecImportPublicKey(EcPublicKey *key, const EcCurve *curve, const uint8_t *input, size_t length, EcPublicKeyFormat format)
Import an EC public key.
Definition: ec.c:263
SshRsaCertPublicKey rsaPublicKey
#define ED25519_CURVE
Definition: ec_curves.h:72
SshBinaryString g
#define osMemcmp(p1, p2, length)
Definition: os_port.h:159
#define ED448_CURVE
Definition: ec_curves.h:73
SshBinaryString q
SshBinaryString n
@ EC_PUBLIC_KEY_FORMAT_X963
Definition: ec.h:386
Mpi e
Public exponent.
Definition: rsa.h:59
Mpi p
Prime modulus.
Definition: dsa.h:50
error_t sshImportEcdsaCertPublicKey(EcPublicKey *publicKey, const SshCertificate *cert)
Import an ECDSA public key from a certificate.
#define osStrlen(s)
Definition: os_port.h:171
bool_t sshCompareString(const SshString *string, const char_t *value)
Compare a binary string against the supplied value.
Definition: ssh_misc.c:1691
Mpi n
Modulus.
Definition: rsa.h:58
SshBinaryString p
size_t length
Definition: ssh_types.h:69
error_t sshImportEd25519CertPublicKey(EddsaPublicKey *publicKey, const SshCertificate *cert)
Import an Ed25519 public key from a certificate.
error_t base64Decode(const char_t *input, size_t inputLen, void *output, size_t *outputLen)
Base64 decoding algorithm.
Definition: base64.c:258
DSA public key.
Definition: dsa.h:61
error_t
Error codes.
Definition: error.h:43
Mpi g
Group generator.
Definition: dsa.h:52
EdDSA public key.
Definition: eddsa.h:64
#define SSH_MAX_DSA_MODULUS_SIZE
Definition: ssh.h:745
const EcCurve * sshGetCurve(const SshString *keyFormatId, const SshString *curveName)
Get the elliptic curve that matches the specified key format identifier.
Definition: ssh_misc.c:1096
RSA public key.
Definition: rsa.h:57
error_t mpiImport(Mpi *r, const uint8_t *input, size_t length, MpiFormat format)
Octet string to integer conversion.
Definition: mpi.c:714
SshDsaCertPublicKey dsaPublicKey
@ ERROR_INVALID_KEY_LENGTH
Definition: error.h:107
Base64 encoding scheme.
DsaDomainParameters params
DSA domain parameters.
Definition: dsa.h:62
SshEcdsaCertPublicKey ecdsaPublicKey
uint8_t length
Definition: tcp.h:375
SshBinaryString y
error_t sshImportEd448CertPublicKey(EddsaPublicKey *publicKey, const SshCertificate *cert)
Import an Ed448 public key from a certificate.
error_t sshImportCertificate(const char_t *input, size_t inputLen, uint8_t *output, size_t *outputLen)
Import SSH certificate (OpenSSH format)
#define SSH_MAX_RSA_MODULUS_SIZE
Definition: ssh.h:731
SshBinaryString q
uint_t mpiGetBitLength(const Mpi *a)
Get the actual length in bits.
Definition: mpi.c:255
const uint8_t * value
Definition: ssh_types.h:68
SshBinaryString e
EC public key.
Definition: ec.h:421
char char_t
Definition: compiler_port.h:55
SshString keyFormatId
SshCertPublicKey publicKey
uint8_t n
SSH certificate import functions.
SSH helper functions.
@ MPI_FORMAT_BIG_ENDIAN
Definition: mpi.h:93
Mpi y
Public key value.
Definition: dsa.h:63
@ ERROR_WRONG_IDENTIFIER
Definition: error.h:89
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
error_t eddsaImportPublicKey(EddsaPublicKey *key, const EcCurve *curve, const uint8_t *input, size_t length)
Import an EdDSA public key.
Definition: eddsa.c:275
error_t sshImportDsaCertPublicKey(DsaPublicKey *publicKey, const SshCertificate *cert)
Import a DSA public key from a certificate.
#define EcCurve
Definition: ec.h:346
unsigned int uint_t
Definition: compiler_port.h:57
Secure Shell (SSH)
SshEddsaCertPublicKey eddsaPublicKey
SSH certificate (OpenSSH format)
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
const char_t * sshGetCertType(const char_t *input, size_t length)
Get SSH certificate type.
SshBinaryString q
#define arraysize(a)
Definition: os_port.h:71
error_t sshImportRsaCertPublicKey(RsaPublicKey *publicKey, const SshCertificate *cert)
Import an RSA public key from a certificate.