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