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.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_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-v01@openssh.com",
57 #endif
58 #if (SSH_DSA_SIGN_SUPPORT == ENABLED)
59  "ssh-dss-cert-v01@openssh.com",
60 #endif
61 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP256_SUPPORT == ENABLED)
62  "ecdsa-sha2-nistp256-cert-v01@openssh.com",
63 #endif
64 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED && SSH_NISTP384_SUPPORT == ENABLED)
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-v01@openssh.com",
69 #endif
70 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
71  "ssh-ed25519-cert-v01@openssh.com",
72 #endif
73 };
74 
75 
76 /**
77  * @brief Import SSH certificate (OpenSSH format)
78  * @param[in] input SSH certificate file to decode
79  * @param[in] inputLen Length of the SSH certificate file to decode
80  * @param[out] output Pointer to the decoded data (optional parameter)
81  * @param[out] outputLen Length of the decoded data
82  * @return Error code
83  **/
84 
85 error_t sshImportCertificate(const char_t *input, size_t inputLen,
86  uint8_t *output, size_t *outputLen)
87 {
88  error_t error;
89  size_t i;
90  size_t j;
91  const char_t *certType;
92 
93  //Retrieve certificate type
94  certType = sshGetCertType(input, inputLen);
95  //Unrecognized certificate type?
96  if(certType == NULL)
97  return ERROR_INVALID_SYNTAX;
98 
99  //Get the length of the identifier string
100  i = osStrlen(certType);
101 
102  //The identifier must be followed by a whitespace character
103  if(input[i] != ' ' && input[i] != '\t')
104  return ERROR_INVALID_SYNTAX;
105 
106  //Skip whitespace characters
107  while(i < inputLen && (input[i] == ' ' || input[i] == '\t'))
108  {
109  i++;
110  }
111 
112  //Point to the certificate
113  j = i;
114 
115  //The certificate may be followed by a whitespace character and a comment
116  while(j < inputLen && (input[j] != ' ' && input[j] != '\t'))
117  {
118  j++;
119  }
120 
121  //The certificate is Base64-encoded
122  error = base64Decode(input + i, j - i, output, outputLen);
123  //Failed to decode the file?
124  if(error)
125  return error;
126 
127  //Sanity check
128  if(*outputLen == 0)
129  return ERROR_INVALID_SYNTAX;
130 
131  //Successful processing
132  return NO_ERROR;
133 }
134 
135 
136 /**
137  * @brief Import an RSA public key from a certificate
138  * @param[out] publicKey Pointer to the RSA public key
139  * @param[in] cert Pointer to the certificate structure
140  * @return Error code
141  **/
142 
144  const SshCertificate *cert)
145 {
146 #if (SSH_RSA_SIGN_SUPPORT == ENABLED)
147  error_t error;
148  uint_t k;
149 
150  //Unexpected key format identifier?
151  if(!sshCompareString(&cert->keyFormatId, "ssh-rsa-cert-v01@openssh.com"))
152  return ERROR_WRONG_IDENTIFIER;
153 
154  //Import RSA public exponent
155  error = mpiImport(&publicKey->e, cert->publicKey.rsaPublicKey.e.value,
157  //Any error to report?
158  if(error)
159  return error;
160 
161  //Import RSA modulus
162  error = mpiImport(&publicKey->n, cert->publicKey.rsaPublicKey.n.value,
164  //Any error to report?
165  if(error)
166  return error;
167 
168  //Get the length of the modulus, in bits
169  k = mpiGetBitLength(&publicKey->n);
170 
171  //Applications should enforce minimum and maximum key sizes
172  if(k < SSH_MIN_RSA_MODULUS_SIZE || k > SSH_MAX_RSA_MODULUS_SIZE)
174 
175  //Successful processing
176  return NO_ERROR;
177 #else
178  //Not implemented
179  return ERROR_NOT_IMPLEMENTED;
180 #endif
181 }
182 
183 
184 /**
185  * @brief Import a DSA public key from a certificate
186  * @param[out] publicKey Pointer to the DSA public key
187  * @param[in] cert Pointer to the certificate structure
188  * @return Error code
189  **/
190 
192  const SshCertificate *cert)
193 {
194 #if (SSH_DSA_SIGN_SUPPORT == ENABLED)
195  error_t error;
196  size_t k;
197 
198  //Unexpected key format identifier?
199  if(!sshCompareString(&cert->keyFormatId, "ssh-dss-cert-v01@openssh.com"))
200  return ERROR_WRONG_IDENTIFIER;
201 
202  //Import DSA prime modulus
203  error = mpiImport(&publicKey->params.p, cert->publicKey.dsaPublicKey.p.value,
205  //Any error to report?
206  if(error)
207  return error;
208 
209  //Import DSA group order
210  error = mpiImport(&publicKey->params.q, cert->publicKey.dsaPublicKey.q.value,
212  //Any error to report?
213  if(error)
214  return error;
215 
216  //Import DSA group generator
217  error = mpiImport(&publicKey->params.g, cert->publicKey.dsaPublicKey.g.value,
219  //Any error to report?
220  if(error)
221  return error;
222 
223  //Import DSA public key value
224  error = mpiImport(&publicKey->y, cert->publicKey.dsaPublicKey.y.value,
226  //Any error to report?
227  if(error)
228  return error;
229 
230  //Get the length of the modulus, in bits
231  k = mpiGetBitLength(&publicKey->params.p);
232 
233  //Applications should enforce minimum and maximum key sizes
234  if(k < SSH_MIN_DSA_MODULUS_SIZE || k > SSH_MAX_DSA_MODULUS_SIZE)
236 
237  //Successful processing
238  return NO_ERROR;
239 #else
240  //Not implemented
241  return ERROR_NOT_IMPLEMENTED;
242 #endif
243 }
244 
245 
246 /**
247  * @brief Import an ECDSA public key from a certificate
248  * @param[out] publicKey Pointer to the ECDSA public key
249  * @param[in] cert Pointer to the certificate structure
250  * @return Error code
251  **/
252 
254  const SshCertificate *cert)
255 {
256 #if (SSH_ECDSA_SIGN_SUPPORT == ENABLED)
257  error_t error;
258  const EcCurve *curve;
259 
260  //Check key format identifier
261  if(sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp256-cert-v01@openssh.com") ||
262  sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp384-cert-v01@openssh.com") ||
263  sshCompareString(&cert->keyFormatId, "ecdsa-sha2-nistp521-cert-v01@openssh.com"))
264  {
265  //Retrieve the elliptic curve that matches the specified key format
266  //identifier
267  curve = sshGetCurve(&cert->keyFormatId,
269 
270  //Make sure the key format identifier is acceptable
271  if(curve != NULL)
272  {
273  //Import EC public key
274  error = ecImportPublicKey(publicKey, curve,
278  }
279  else
280  {
281  //Report an error
282  error = ERROR_WRONG_IDENTIFIER;
283  }
284  }
285  else
286  {
287  //Unexpected key format identifier
288  error = ERROR_WRONG_IDENTIFIER;
289  }
290 
291  //Return status code
292  return error;
293 #else
294  //Not implemented
295  return ERROR_NOT_IMPLEMENTED;
296 #endif
297 }
298 
299 
300 /**
301  * @brief Import an Ed25519 public key from a certificate
302  * @param[out] publicKey Pointer to the EdDSA public key
303  * @param[in] cert Pointer to the certificate structure
304  * @return Error code
305  **/
306 
308  const SshCertificate *cert)
309 {
310 #if (SSH_ED25519_SIGN_SUPPORT == ENABLED)
311  error_t error;
312 
313  //Check key format identifier
314  if(sshCompareString(&cert->keyFormatId, "ssh-ed25519-cert-v01@openssh.com"))
315  {
316  //Import Ed25519 public key
317  error = eddsaImportPublicKey(publicKey, ED25519_CURVE,
320  }
321  else
322  {
323  //Unexpected key format identifier
324  error = ERROR_WRONG_IDENTIFIER;
325  }
326 
327  //Return status code
328  return error;
329 #else
330  //Not implemented
331  return ERROR_NOT_IMPLEMENTED;
332 #endif
333 }
334 
335 
336 /**
337  * @brief Get SSH certificate type
338  * @param[in] input SSH certificate file
339  * @param[in] length Length of the SSH certificate file
340  * @return SSH certificate type
341  **/
342 
343 const char_t *sshGetCertType(const char_t *input, size_t length)
344 {
345  uint_t i;
346  size_t n;
347  const char_t *certType;
348 
349  //Initialize certificate type
350  certType = NULL;
351 
352  //Loop through the list of identifiers
353  for(i = 0; i < arraysize(sshCertTypes); i++)
354  {
355  //Get the length of the identifier
356  n = osStrlen(sshCertTypes[i]);
357 
358  //Matching identifier?
359  if(length > n && osMemcmp(input, sshCertTypes[i], n) == 0)
360  {
361  //The identifier must be followed by a whitespace character
362  if(input[n] == ' ' || input[n] == '\t')
363  {
364  certType = sshCertTypes[i];
365  break;
366  }
367  }
368  }
369 
370  //Return certificate type
371  return certType;
372 }
373 
374 #endif
error_t eddsaImportPublicKey(EddsaPublicKey *key, const EcCurve *curve, const uint8_t *data, size_t length)
Import an EdDSA public key.
Definition: eddsa.c:274
error_t ecImportPublicKey(EcPublicKey *key, const EcCurve *curve, const uint8_t *data, size_t length, EcPublicKeyFormat format)
Import an EC public key.
Definition: ec.c:263
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
Mpi q
Group order.
Definition: dsa.h:51
SshRsaCertPublicKey rsaPublicKey
#define ED25519_CURVE
Definition: ec_curves.h:72
SshBinaryString g
#define osMemcmp(p1, p2, length)
Definition: os_port.h:156
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:1626
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:1069
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 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 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)
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.
SshEd25519CertPublicKey ed25519PublicKey