scep_client_misc.c
Go to the documentation of this file.
1 /**
2  * @file scep_client_misc.c
3  * @brief Helper functions for SCEP client
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneCRYPTO 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 SCEP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "scep/scep_client.h"
36 #include "scep/scep_client_misc.h"
37 #include "pkix/x509_cert_parse.h"
38 #include "pkix/x509_cert_create.h"
39 #include "pkix/x509_csr_parse.h"
41 #include "debug.h"
42 
43 //Check crypto library configuration
44 #if (SCEP_CLIENT_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief Content encryption algorithm selection
49  * @param[in] context Pointer to the SCEP client context
50  * @param[out] contentEncrAlgo Content encryption algorithm
51  * @return Error code
52  **/
53 
55  Pkcs7ContentEncrAlgo *contentEncrAlgo)
56 {
57  error_t error;
58 
59  //Initialize status code
60  error = NO_ERROR;
61 
62  //Clear contentEncryptionAlgorithm structure
63  osMemset(contentEncrAlgo, 0, sizeof(Pkcs7ContentEncrAlgo));
64 
65 #if (SCEP_CLIENT_AES_SUPPORT == ENABLED)
66  //AES128-CBC encryption algorithm?
67  if((context->caCaps & SCEP_CA_CAPS_AES) != 0)
68  {
69  //The client should use AES128-CBC in preference to triple DES-CBC if
70  //it is supported by the CA (refer to RFC 8894, section 3.5.2)
71  contentEncrAlgo->oid.value = AES128_CBC_OID;
72  contentEncrAlgo->oid.length = sizeof(AES128_CBC_OID);
73  }
74  else
75 #endif
76 #if (SCEP_CLIENT_3DES_SUPPORT == ENABLED)
77  //Triple DES-CBC encryption algorithm?
78  if((context->caCaps & SCEP_CA_CAPS_DES3) != 0)
79  {
80  //CA supports the triple DES-CBC encryption algorithm
81  contentEncrAlgo->oid.value = DES_EDE3_CBC_OID;
82  contentEncrAlgo->oid.length = sizeof(DES_EDE3_CBC_OID);
83  }
84  else
85 #endif
86  {
87  //Report an error
88  error = ERROR_UNSUPPORTED_ALGO;
89  }
90 
91  //Return status code
92  return error;
93 }
94 
95 
96 /**
97  * @brief Signature algorithm selection
98  * @param[in] context Pointer to the SCEP client context
99  * @param[out] signatureAlgo Signature algorithm
100  **/
101 
103  X509SignAlgoId *signatureAlgo)
104 {
105  error_t error;
106 
107  //Initialize status code
108  error = NO_ERROR;
109 
110  //Clear signatureValue structure
111  osMemset(signatureAlgo, 0, sizeof(X509SignAlgoId));
112 
113 #if (SCEP_CLIENT_RSA_SUPPORT == ENABLED)
114  //RSA private key?
115  if(context->keyType == X509_KEY_TYPE_RSA)
116  {
117 #if (SCEP_CLIENT_SHA512_SUPPORT == ENABLED)
118  //SHA-512 hashing algorithm?
119  if((context->caCaps & SCEP_CA_CAPS_SHA512) != 0)
120  {
121  //CA supports the SHA-512 hashing algorithm
122  signatureAlgo->oid.value = SHA512_WITH_RSA_ENCRYPTION_OID;
123  signatureAlgo->oid.length = sizeof(SHA512_WITH_RSA_ENCRYPTION_OID);
124  }
125  else
126 #endif
127 #if (SCEP_CLIENT_SHA256_SUPPORT == ENABLED)
128  //SHA-256 hashing algorithm?
129  if((context->caCaps & SCEP_CA_CAPS_SHA256) != 0)
130  {
131  //The client should use SHA-256 in preference to SHA-1 hashing if they
132  //are supported by the CA (refer to RFC 8894, section 3.5.2)
133  signatureAlgo->oid.value = SHA256_WITH_RSA_ENCRYPTION_OID;
134  signatureAlgo->oid.length = sizeof(SHA256_WITH_RSA_ENCRYPTION_OID);
135  }
136  else
137 #endif
138 #if (SCEP_CLIENT_SHA1_SUPPORT == ENABLED)
139  //SHA-1 hashing algorithm?
140  if((context->caCaps & SCEP_CA_CAPS_SHA1) != 0)
141  {
142  //CA supports the SHA-1 hashing algorithm
143  signatureAlgo->oid.value = SHA1_WITH_RSA_ENCRYPTION_OID;
144  signatureAlgo->oid.length = sizeof(SHA1_WITH_RSA_ENCRYPTION_OID);
145  }
146  else
147 #endif
148  {
149  //Report an error
150  error = ERROR_UNSUPPORTED_ALGO;
151  }
152  }
153  else
154 #endif
155  //Invalid private key?
156  {
157  //Report an error
158  error = ERROR_INVALID_KEY;
159  }
160 
161  //Return status code
162  return error;
163 }
164 
165 
166 /**
167  * @brief Parse CA certificate
168  * @param[in] context Pointer to the SCEP client context
169  * @param[out] certInfo Information resulting from the parsing process
170  * @return Error code
171  **/
172 
174  X509CertInfo *certInfo)
175 {
176  error_t error;
177  size_t length;
178  const uint8_t *data;
179 
180  //Initialize status code
181  error = NO_ERROR;
182 
183  //Valid CA certificate chain?
184  if(context->caCertLen > 0)
185  {
186  //Point to the first certificate of the chain
187  data = context->caCert;
188  length = context->caCertLen;
189 
190  //The intermediate CA certificate is the leaf certificate
191  while(length > 0 && !error)
192  {
193  //Parse certificate
194  error = x509ParseCertificate(data, length, certInfo);
195 
196  //Check status code
197  if(!error)
198  {
199  //Next certificate
200  data += certInfo->raw.length;
201  length -= certInfo->raw.length;
202  }
203  }
204  }
205  else
206  {
207  //Report an error
208  error = ERROR_NO_CERTIFICATE;
209  }
210 
211  //Return status code
212  return error;
213 }
214 
215 
216 /**
217  * @brief Verify CA certificate
218  * @param[in] context Pointer to the SCEP client context
219  * @return Error code
220  **/
221 
223 {
224  error_t error;
225  time_t currentTime;
226  X509CertInfo *certInfo;
227 
228  //Any registered callback?
229  if(context->caCertVerifyCallback != NULL)
230  {
231  //Allocate a memory buffer to store X.509 certificate info
232  certInfo = cryptoAllocMem(sizeof(X509CertInfo));
233 
234  //Successful memory allocation?
235  if(certInfo != NULL)
236  {
237  //Parse CA certificate
238  error = scepClientParseCaCert(context, certInfo);
239 
240  //Check status code
241  if(!error)
242  {
243  //Retrieve current time
244  currentTime = getCurrentUnixTime();
245 
246  //Any real-time clock implemented?
247  if(currentTime != 0)
248  {
249  DateTime currentDate;
250  const X509Validity *validity;
251 
252  //Convert Unix timestamp to date
253  convertUnixTimeToDate(currentTime, &currentDate);
254 
255  //The certificate validity period is the time interval during which
256  //the CA warrants that it will maintain information about the status
257  //of the certificate
258  validity = &certInfo->tbsCert.validity;
259 
260  //Check the validity period
261  if(compareDateTime(&currentDate, &validity->notBefore) < 0 ||
262  compareDateTime(&currentDate, &validity->notAfter) > 0)
263  {
264  //The certificate has expired or is not yet valid
266  }
267  }
268  }
269 
270  //Check status code
271  if(!error)
272  {
273  //After the client gets the CA certificate, it should authenticate it.
274  //For example, the client could compare the certificate's fingerprint
275  //with locally configured, out-of-band distributed, identifying
276  //information, or by some equivalent means such as a direct comparison
277  //with a locally stored copy of the certificate
278  error = context->caCertVerifyCallback(context, certInfo);
279  }
280 
281  //Release previously allocated memory
282  cryptoFreeMem(certInfo);
283  }
284  else
285  {
286  //Failed to allocate memory
287  error = ERROR_OUT_OF_MEMORY;
288  }
289  }
290  else
291  {
292  //Report an error
293  error = ERROR_BAD_CERTIFICATE;
294  }
295 
296  //Invalid CA certificate?
297  if(error)
298  {
299  //Clear CA certificate
300  context->caCertLen = 0;
301  }
302 
303  //Return status code
304  return error;
305 }
306 
307 
308 /**
309  * @brief Transaction identifier generation
310  * @param[in] context Pointer to the SCEP client context
311  * @return Error code
312  **/
313 
315 {
316  error_t error;
317  size_t i;
318  uint8_t buffer[SCEP_CLIENT_TRANSACTION_ID_SIZE];
319 
320  //Hex conversion table
321  static const char_t hexDigit[16] =
322  {
323  '0', '1', '2', '3', '4', '5', '6', '7',
324  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
325  };
326 
327  //The transactionID must be unique, but not necessarily randomly generated
328  //(refer to RFC 8894, section 3.2.1.1)
329  error = context->prngAlgo->read(context->prngContext, buffer,
331 
332  //Check status code
333  if(!error)
334  {
335  //The transaction identifier must be encoded as a PrintableString
336  for(i = 0; i < SCEP_CLIENT_TRANSACTION_ID_SIZE; i++)
337  {
338  //Convert upper nibble
339  context->transactionId[i * 2] = hexDigit[(buffer[i] >> 4) & 0x0F];
340  //Then convert lower nibble
341  context->transactionId[i * 2 + 1] = hexDigit[buffer[i] & 0x0F];
342  }
343 
344  //Properly terminate the string with a NULL character
345  context->transactionId[i * 2] = '\0';
346  }
347 
348  //Return status code
349  return error;
350 }
351 
352 
353 /**
354  * @brief Generate PKCS #10 certificate request
355  * @param[in] context Pointer to the SCEP client context
356  * @return Error code
357  **/
358 
360 {
361  error_t error;
362 
363  //Any registered callback?
364  if(context->csrGenCallback != NULL)
365  {
366  //Generate PKCS #10 certificate request
367  error = context->csrGenCallback(context, context->csr,
368  SCEP_CLIENT_MAX_CSR_LEN, &context->csrLen);
369  }
370  else
371  {
372  //Report an error
373  error = ERROR_INVALID_CSR;
374  }
375 
376  //Return status code
377  return error;
378 }
379 
380 
381 /**
382  * @brief Generate self-signed certificate
383  * @param[in] context Pointer to the SCEP client context
384  * @return Error code
385  **/
386 
388 {
389  error_t error;
390 
391  //Any registered callback?
392  if(context->selfSignedCertGenCallback != NULL)
393  {
394  //Generate self-signed X.509 certificate
395  error = context->selfSignedCertGenCallback(context, context->cert,
396  SCEP_CLIENT_MAX_CERT_LEN, &context->certLen);
397  }
398  else
399  {
400  time_t currentTime;
401  X509CsrInfo *csrInfo;
403  X509Validity validity;
404 
405  //Debug message
406  TRACE_INFO("Generating self-signed certificate...\r\n");
407 
408  //Allocate a memory buffer to hold CSR info
409  csrInfo = cryptoAllocMem(sizeof(X509CsrInfo));
410 
411  //Successful memory allocation?
412  if(csrInfo != NULL)
413  {
414  //The self-signed certificate should use the same subject name and key as
415  //in the PKCS #10 request (refer to RFC 8894, section 2.3)
416  error = x509ParseCsr(context->csr, context->csrLen, csrInfo);
417 
418  //Check status code
419  if(!error)
420  {
421  //Clear CSR attributes
422  osMemset(&csrInfo->certReqInfo.attributes, 0, sizeof(X509Attributes));
423 
424  //Point to the certificate extensions
426 
427  //The cA boolean indicates whether the certified public key may be
428  //used to verify certificate signatures
429  extensions->basicConstraints.critical = TRUE;
430  extensions->basicConstraints.cA = FALSE;
431  extensions->basicConstraints.pathLenConstraint = -1;
432 
433  //The keyUsage extension in the certificate must indicate that it is
434  //valid for digitalSignature and keyEncipherment (refer to RFC 8894,
435  //section 2.3)
436  extensions->keyUsage.critical = TRUE;
437  extensions->keyUsage.bitmap = X509_KEY_USAGE_DIGITAL_SIGNATURE |
439 
440  //Retrieve current time
441  currentTime = getCurrentUnixTime();
442 
443  //Any real-time clock implemented?
444  if(currentTime != 0)
445  {
446  //Validity period
447  convertUnixTimeToDate(currentTime, &validity.notBefore);
448  convertUnixTimeToDate(currentTime + 86400, &validity.notAfter);
449  }
450  else
451  {
452  //Validity period
453  validity.notBefore.year = 2020;
454  validity.notBefore.month = 1;
455  validity.notBefore.day = 1;
456  validity.notBefore.hours = 12;
457  validity.notBefore.minutes = 0;
458  validity.notBefore.seconds = 0;
459  validity.notAfter.year = 2021;
460  validity.notAfter.month = 1;
461  validity.notAfter.day = 1;
462  validity.notAfter.hours = 12;
463  validity.notAfter.minutes = 0;
464  validity.notAfter.seconds = 0;
465  }
466 
467  //Create a self-signed X.509 certificate
468  error = x509CreateCertificate(context->prngAlgo, context->prngContext,
469  &csrInfo->certReqInfo, &context->rsaPublicKey, NULL, NULL, &validity,
470  &csrInfo->signatureAlgo, &context->rsaPrivateKey, context->cert,
471  &context->certLen);
472  }
473 
474  //Release previously allocated memory
475  cryptoFreeMem(csrInfo);
476  }
477  else
478  {
479  //Failed to allocate memory
480  error = ERROR_OUT_OF_MEMORY;
481  }
482  }
483 
484  //Return status code
485  return error;
486 }
487 
488 #endif
error_t scepClientVerifyCaCert(ScepClientContext *context)
Verify CA certificate.
X509Validity validity
Definition: x509_common.h:1107
@ X509_KEY_TYPE_RSA
Definition: x509_common.h:635
X.509 certificate parsing.
uint8_t extensions[]
Definition: ntp_common.h:207
error_t scepClientSelectContentEncrAlgo(ScepClientContext *context, Pkcs7ContentEncrAlgo *contentEncrAlgo)
Content encryption algorithm selection.
X509TbsCertificate tbsCert
Definition: x509_common.h:1121
const uint8_t SHA512_WITH_RSA_ENCRYPTION_OID[9]
Definition: rsa.c:69
#define ScepClientContext
Definition: scep_client.h:165
error_t scepClientGenerateTransactionId(ScepClientContext *context)
Transaction identifier generation.
Validity.
Definition: x509_common.h:763
uint16_t year
Definition: date_time.h:48
#define TRUE
Definition: os_port.h:50
error_t x509ParseCertificate(const uint8_t *data, size_t length, X509CertInfo *certInfo)
Parse a X.509 certificate.
Collection of AEAD algorithms.
uint8_t data[]
Definition: ethernet.h:224
X509SignAlgoId signatureAlgo
Definition: x509_common.h:1324
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
X509Extensions extensionReq
Definition: x509_common.h:1299
void convertUnixTimeToDate(time_t t, DateTime *date)
Convert Unix timestamp to date.
Definition: date_time.c:204
error_t x509ParseCsr(const uint8_t *data, size_t length, X509CsrInfo *csrInfo)
Parse a CSR (Certificate Signing Request)
Content encryption algorithm.
Definition: pkcs7_common.h:328
uint8_t day
Definition: date_time.h:50
DateTime notAfter
Definition: x509_common.h:765
#define FALSE
Definition: os_port.h:46
@ SCEP_CA_CAPS_AES
AES.
Definition: scep_common.h:96
X509OctetString raw
Definition: x509_common.h:1120
X.509 certificate generation.
X.509 certificate.
Definition: x509_common.h:1119
uint8_t minutes
Definition: date_time.h:53
error_t
Error codes.
Definition: error.h:43
@ ERROR_UNSUPPORTED_ALGO
Definition: error.h:126
error_t x509CreateCertificate(const PrngAlgo *prngAlgo, void *prngContext, const X509CertRequestInfo *certReqInfo, const void *subjectPublicKey, const X509CertInfo *issuerCertInfo, const X509SerialNumber *serialNumber, const X509Validity *validity, const X509SignAlgoId *signatureAlgo, const void *signerPrivateKey, uint8_t *output, size_t *written)
Generate a X.509 certificate.
error_t scepClientParseCaCert(ScepClientContext *context, X509CertInfo *certInfo)
Parse CA certificate.
const uint8_t DES_EDE3_CBC_OID[8]
Definition: des3.c:48
X509CertRequestInfo certReqInfo
Definition: x509_common.h:1323
@ SCEP_CA_CAPS_SHA1
SHA-1.
Definition: scep_common.h:101
@ ERROR_BAD_CERTIFICATE
Definition: error.h:236
@ ERROR_NO_CERTIFICATE
Definition: error.h:235
uint8_t hours
Definition: date_time.h:52
Date and time representation.
Definition: date_time.h:47
@ X509_KEY_USAGE_DIGITAL_SIGNATURE
Definition: x509_common.h:526
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
@ ERROR_INVALID_CSR
Definition: error.h:309
X509OctetString oid
Definition: x509_common.h:1089
uint8_t seconds
Definition: date_time.h:54
const uint8_t SHA256_WITH_RSA_ENCRYPTION_OID[9]
Definition: rsa.c:65
#define SCEP_CLIENT_TRANSACTION_ID_SIZE
Definition: scep_client.h:153
SCEP client.
const uint8_t AES128_CBC_OID[9]
Definition: aes.c:182
#define SCEP_CLIENT_MAX_CSR_LEN
Definition: scep_client.h:132
uint8_t month
Definition: date_time.h:49
char char_t
Definition: compiler_port.h:55
@ SCEP_CA_CAPS_DES3
DES3.
Definition: scep_common.h:97
X509OctetString oid
Definition: pkcs7_common.h:329
DateTime notBefore
Definition: x509_common.h:764
@ SCEP_CA_CAPS_SHA256
SHA-256.
Definition: scep_common.h:102
#define SCEP_CLIENT_MAX_CERT_LEN
Definition: scep_client.h:139
@ ERROR_CERTIFICATE_EXPIRED
Definition: error.h:239
#define cryptoFreeMem(p)
Definition: crypto.h:833
error_t scepClientGenerateSelfSignedCert(ScepClientContext *context)
Generate self-signed certificate.
error_t scepClientSelectSignatureAlgo(ScepClientContext *context, X509SignAlgoId *signatureAlgo)
Signature algorithm selection.
CSR attributes.
Definition: x509_common.h:1296
#define cryptoAllocMem(size)
Definition: crypto.h:828
X.509 certificate extensions.
Definition: x509_common.h:1051
const uint8_t * value
Definition: x509_common.h:702
#define osMemset(p, value, length)
Definition: os_port.h:138
@ X509_KEY_USAGE_KEY_ENCIPHERMENT
Definition: x509_common.h:528
CSR (Certificate Signing Request)
Definition: x509_common.h:1322
error_t scepClientGenerateCsr(ScepClientContext *context)
Generate PKCS #10 certificate request.
Helper functions for SCEP client.
X509Attributes attributes
Definition: x509_common.h:1313
CSR (Certificate Signing Request) parsing.
__weak_func time_t getCurrentUnixTime(void)
Get current time.
Definition: date_time.c:186
const uint8_t SHA1_WITH_RSA_ENCRYPTION_OID[9]
Definition: rsa.c:61
@ SCEP_CA_CAPS_SHA512
SHA-512.
Definition: scep_common.h:103
Signature algorithm identifier.
Definition: x509_common.h:1088
@ ERROR_INVALID_KEY
Definition: error.h:106
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
int_t compareDateTime(const DateTime *date1, const DateTime *date2)
Compare dates.
Definition: date_time.c:310