scep_client.c
Go to the documentation of this file.
1 /**
2  * @file scep_client.c
3  * @brief 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  * @section Description
28  *
29  * SCEP is a protocol used to determine the current status of a digital
30  * certificate without requiring CRLs. Refer to the following RFCs for
31  * complete details:
32  * - RFC 6960: X.509 Internet Public Key Infrastructure SCEP
33  * - RFC 8954: Online Certificate Status Protocol (SCEP) Nonce Extension
34  *
35  * @author Oryx Embedded SARL (www.oryx-embedded.com)
36  * @version 2.5.2
37  **/
38 
39 //Switch to the appropriate trace level
40 #define TRACE_LEVEL SCEP_TRACE_LEVEL
41 
42 //Dependencies
43 #include "scep/scep_client.h"
45 #include "scep/scep_client_misc.h"
46 #include "pkix/pem_import.h"
47 #include "pkix/pem_key_import.h"
48 #include "pkix/pem_export.h"
49 #include "encoding/asn1.h"
50 #include "debug.h"
51 
52 //Check crypto library configuration
53 #if (SCEP_CLIENT_SUPPORT == ENABLED)
54 
55 
56 /**
57  * @brief SCEP client initialization
58  * @param[in] context Pointer to the SCEP client context
59  * @return Error code
60  **/
61 
63 {
64  error_t error;
65 
66  //Make sure the SCEP client context is valid
67  if(context == NULL)
69 
70  //Debug message
71  TRACE_INFO("Initializing SCEP client...\r\n");
72 
73  //Initialize context
74  osMemset(context, 0, sizeof(ScepClientContext));
75 
76  //Initialize HTTP client context
77  error = httpClientInit(&context->httpClientContext);
78  //Any error to report?
79  if(error)
80  return error;
81 
82  //Initialize SCEP client state
83  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
84 
85  //Default timeout
86  context->timeout = SCEP_CLIENT_DEFAULT_TIMEOUT;
87  //Default URI
88  osStrcpy(context->uri, "/");
89 
90  //Successful initialization
91  return NO_ERROR;
92 }
93 
94 
95 #if (SCEP_CLIENT_TLS_SUPPORT == ENABLED)
96 
97 /**
98  * @brief Register TLS initialization callback function
99  * @param[in] context Pointer to the SCEP client context
100  * @param[in] callback TLS initialization callback function
101  * @return Error code
102  **/
103 
105  ScepClientTlsInitCallback callback)
106 {
107  //Make sure the SCEP client context is valid
108  if(context == NULL)
110 
111  //Save callback function
112  context->tlsInitCallback = callback;
113 
114  //Successful processing
115  return NO_ERROR;
116 }
117 
118 #endif
119 
120 
121 /**
122  * @brief Register CA certificate verification callback function
123  * @param[in] context Pointer to the SCEP client context
124  * @param[in] callback CA certificate verification callback function
125  * @return Error code
126  **/
127 
130 {
131  //Make sure the SCEP client context is valid
132  if(context == NULL)
134 
135  //Save callback function
136  context->caCertVerifyCallback = callback;
137 
138  //Successful processing
139  return NO_ERROR;
140 }
141 
142 
143 /**
144  * @brief Register CSR generation callback function
145  * @param[in] context Pointer to the SCEP client context
146  * @param[in] callback CSR generation callback function
147  * @return Error code
148  **/
149 
151  ScepClientCsrGenCallback callback)
152 {
153  //Make sure the SCEP client context is valid
154  if(context == NULL)
156 
157  //Save callback function
158  context->csrGenCallback = callback;
159 
160  //Successful processing
161  return NO_ERROR;
162 }
163 
164 
165 /**
166  * @brief Register self-signed certificate generation callback function
167  * @param[in] context Pointer to the SCEP client context
168  * @param[in] callback Self-signed certificate generation callback function
169  * @return Error code
170  **/
171 
174 {
175  //Make sure the SCEP client context is valid
176  if(context == NULL)
178 
179  //Save callback function
180  context->selfSignedCertGenCallback = callback;
181 
182  //Successful processing
183  return NO_ERROR;
184 }
185 
186 
187 /**
188  * @brief Set the pseudo-random number generator to be used
189  * @param[in] context Pointer to the SCEP client context
190  * @param[in] prngAlgo PRNG algorithm
191  * @param[in] prngContext Pointer to the PRNG context
192  * @return Error code
193  **/
194 
196  void *prngContext)
197 {
198  //Check parameters
199  if(context == NULL || prngAlgo == NULL || prngContext == NULL)
201 
202  //PRNG algorithm that will be used to generate nonces
203  context->prngAlgo = prngAlgo;
204  //PRNG context
205  context->prngContext = prngContext;
206 
207  //Successful processing
208  return NO_ERROR;
209 }
210 
211 
212 /**
213  * @brief Set communication timeout
214  * @param[in] context Pointer to the SCEP client context
215  * @param[in] timeout Timeout value, in milliseconds
216  * @return Error code
217  **/
218 
220 {
221  //Make sure the SCEP client context is valid
222  if(context == NULL)
224 
225  //Save timeout value
226  context->timeout = timeout;
227 
228  //Successful processing
229  return NO_ERROR;
230 }
231 
232 
233 /**
234  * @brief Set the domain name of the SCEP server
235  * @param[in] context Pointer to the SCEP client context
236  * @param[in] host NULL-terminated string containing the host name
237  * @return Error code
238  **/
239 
241 {
242  //Check parameters
243  if(context == NULL || host == NULL)
245 
246  //Make sure the length of the host name is acceptable
248  return ERROR_INVALID_LENGTH;
249 
250  //Save host name
251  osStrcpy(context->serverName, host);
252 
253  //Successful processing
254  return NO_ERROR;
255 }
256 
257 
258 /**
259  * @brief Set request URI
260  * @param[in] context Pointer to the SCEP client context
261  * @param[in] uri NULL-terminated string that contains the resource name
262  * @return Error code
263  **/
264 
266 {
267  //Check parameters
268  if(context == NULL || uri == NULL)
270 
271  //Make sure the length of the URI is acceptable
273  return ERROR_INVALID_LENGTH;
274 
275  //Save the URI of the directory object
276  osStrcpy(context->uri, uri);
277 
278  //Successful processing
279  return NO_ERROR;
280 }
281 
282 /**
283  * @brief Bind the SCEP client to a particular network interface
284  * @param[in] context Pointer to the SCEP client context
285  * @param[in] interface Network interface to be used
286  * @return Error code
287  **/
288 
290  NetInterface *interface)
291 {
292  //Make sure the SCEP client context is valid
293  if(context == NULL)
295 
296  //Explicitly associate the SCEP client with the specified interface
297  context->interface = interface;
298 
299  //Successful processing
300  return NO_ERROR;
301 }
302 
303 
304 /**
305  * @brief Specify the address of the SCEP server
306  * @param[in] context Pointer to the SCEP client context
307  * @param[in] serverIpAddr IP address of the SCEP server to connect to
308  * @param[in] serverPort UDP port number
309  * @return Error code
310  **/
311 
313  const IpAddr *serverIpAddr, uint16_t serverPort)
314 {
315  error_t error;
316 
317  //Make sure the SCEP client context is valid
318  if(context == NULL)
320 
321  //Initialize status code
322  error = NO_ERROR;
323 
324  //Establish connection with the HTTP server
325  while(!error)
326  {
327  //Check SCEP client state
328  if(context->state == SCEP_CLIENT_STATE_DISCONNECTED)
329  {
330  //Save the TCP port number to be used
331  context->serverPort = serverPort;
332 
333 #if (SCEP_CLIENT_TLS_SUPPORT == ENABLED)
334  //Register TLS initialization callback
335  error = httpClientRegisterTlsInitCallback(&context->httpClientContext,
336  context->tlsInitCallback);
337 #endif
338  //Check status code
339  if(!error)
340  {
341  //Select HTTP protocol version
342  error = httpClientSetVersion(&context->httpClientContext,
344  }
345 
346  //Check status code
347  if(!error)
348  {
349  //Set timeout value for blocking operations
350  error = httpClientSetTimeout(&context->httpClientContext,
351  context->timeout);
352  }
353 
354  //Check status code
355  if(!error)
356  {
357  //Bind the HTTP client to the relevant network interface
358  error = httpClientBindToInterface(&context->httpClientContext,
359  context->interface);
360  }
361 
362  //Check status code
363  if(!error)
364  {
365  //Establish HTTP connection
366  context->state = SCEP_CLIENT_STATE_CONNECTING;
367  }
368  }
369  else if(context->state == SCEP_CLIENT_STATE_CONNECTING)
370  {
371  //Establish HTTP connection
372  error = httpClientConnect(&context->httpClientContext, serverIpAddr,
373  serverPort);
374 
375  //Check status code
376  if(error == NO_ERROR)
377  {
378  //The HTTP connection is established
379  context->state = SCEP_CLIENT_STATE_CONNECTED;
380  }
381  }
382  else if(context->state == SCEP_CLIENT_STATE_CONNECTED)
383  {
384  //The client is connected to the SCEP server
385  break;
386  }
387  else
388  {
389  //Invalid state
390  error = ERROR_WRONG_STATE;
391  }
392  }
393 
394  //Failed to establish connection with the SCEP server?
395  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
396  {
397  //Clean up side effects
398  httpClientClose(&context->httpClientContext);
399  //Update SCEP client state
400  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
401  }
402 
403  //Return status code
404  return error;
405 }
406 
407 
408 /**
409  * @brief Load public/private key pair
410  * @param[in] context Pointer to the SCEP client context
411  * @param[in] publicKey Public key (PEM format)
412  * @param[in] publicKeyLen Length of the public key
413  * @param[in] privateKey Private key (PEM format)
414  * @param[in] privateKeyLen Length of the private key
415  * @param[in] password NULL-terminated string containing the password. This
416  * parameter is required if the private key is encrypted
417  * @return Error code
418  **/
419 
421  const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey,
422  size_t privateKeyLen, const char_t *password)
423 {
424  error_t error;
426 
427  //Check parameters
428  if(context == NULL || publicKey == NULL || publicKeyLen == 0 ||
429  privateKey == NULL || privateKeyLen == 0)
430  {
432  }
433 
434  //Release the current key pair, if any
435  scepClientUnloadKeyPair(context);
436 
437  //Extract the type of the public key
438  type = pemGetPublicKeyType(publicKey, publicKeyLen);
439 
440 #if (SCEP_CLIENT_RSA_SUPPORT == ENABLED)
441  //RSA public key?
442  if(type == X509_KEY_TYPE_RSA)
443  {
444  //Save public key type
445  context->keyType = X509_KEY_TYPE_RSA;
446 
447  //Initialize RSA public and private keys
448  rsaInitPublicKey(&context->rsaPublicKey);
449  rsaInitPrivateKey(&context->rsaPrivateKey);
450 
451  //Decode the PEM file that contains the RSA public key
452  error = pemImportRsaPublicKey(&context->rsaPublicKey, publicKey,
453  publicKeyLen);
454 
455  //Check status code
456  if(!error)
457  {
458  //Decode the PEM file that contains the RSA private key
459  error = pemImportRsaPrivateKey(&context->rsaPrivateKey, privateKey,
460  privateKeyLen, password);
461  }
462  }
463  else
464 #endif
465  //Invalid public key?
466  {
467  //The supplied public key is not valid
468  error = ERROR_INVALID_KEY;
469  }
470 
471  //Any error to report?
472  if(error)
473  {
474  //Clean up side effects
475  scepClientUnloadKeyPair(context);
476  }
477 
478  //Return status code
479  return error;
480 }
481 
482 
483 /**
484  * @brief Unload public/private key pair
485  * @param[in] context Pointer to the SCEP client context
486  **/
487 
489 {
490  //Make sure the SCEP client context is valid
491  if(context != NULL)
492  {
493 #if (SCEP_CLIENT_RSA_SUPPORT == ENABLED)
494  //RSA key pair?
495  if(context->keyType == X509_KEY_TYPE_RSA)
496  {
497  //Release RSA public and private keys
498  rsaFreePublicKey(&context->rsaPublicKey);
499  rsaFreePrivateKey(&context->rsaPrivateKey);
500  }
501  else
502 #endif
503  //Invalid key pair?
504  {
505  //Just for sanity
506  }
507  }
508 }
509 
510 
511 /**
512  * @brief Load client's certificate
513  * @param[in] context Pointer to the SCEP client context
514  * @param[out] input Pointer to the PEM-encoded certificate
515  * @param[out] length Length of the PEM-encoded certificate
516  * @return Error code
517  **/
518 
520  char_t *input, size_t length)
521 {
522  error_t error;
523  size_t n;
524 
525  //Initialize status code
526  error = NO_ERROR;
527 
528  //Make sure the SCEP client context is valid
529  if(context != NULL)
530  {
531  //Valid certificate?
532  if(input != NULL && length > 0)
533  {
534  //The first pass calculates the length of the DER-encoded certificate
535  error = pemImportCertificate(input, length, NULL, &n, NULL);
536 
537  //Check status code
538  if(!error)
539  {
540  //Check the length of the certificate
542  {
543  //The second pass decodes the PEM certificate
544  error = pemImportCertificate(input, length, context->cert,
545  &context->certLen, NULL);
546  }
547  else
548  {
549  //Report an error
550  error = ERROR_INVALID_LENGTH;
551  }
552  }
553  }
554  else
555  {
556  //Clear certificate
557  context->certLen = 0;
558  }
559  }
560  else
561  {
562  //Report an error
563  error = ERROR_INVALID_PARAMETER;
564  }
565 
566  //Return status code
567  return error;
568 }
569 
570 
571 /**
572  * @brief Store client's certificate
573  * @param[in] context Pointer to the SCEP client context
574  * @param[out] output Pointer to the buffer where to store the PEM-encoded
575  * certificate (optional parameter)
576  * @param[out] written Actual length of the certificate, in bytes
577  * @return Error code
578  **/
579 
581  char_t *output, size_t *written)
582 {
583  error_t error;
584 
585  //Check parameters
586  if(context == NULL || written == NULL)
588 
589  //Valid certificate?
590  if(context->certLen > 0)
591  {
592  //Export the certificate to PEM format
593  error = pemExportCertificate(context->cert, context->certLen, output,
594  written);
595  }
596  else
597  {
598  //Report an error
599  error = ERROR_NO_CERTIFICATE;
600  }
601 
602  //Return status code
603  return error;
604 }
605 
606 
607 /**
608  * @brief Load out of band CA certificate
609  * @param[in] context Pointer to the SCEP client context
610  * @param[out] input Pointer to the PEM-encoded CA certificate
611  * @param[out] length Length of the PEM-encoded CA certificate
612  * @return Error code
613  **/
614 
616  char_t *input, size_t length)
617 {
618  error_t error;
619  size_t n;
620 
621  //Initialize status code
622  error = NO_ERROR;
623 
624  //Make sure the SCEP client context is valid
625  if(context != NULL)
626  {
627  //Valid certificate?
628  if(input != NULL && length > 0)
629  {
630  //The first pass calculates the length of the DER-encoded certificate
631  error = pemImportCertificate(input, length, NULL, &n, NULL);
632 
633  //Check status code
634  if(!error)
635  {
636  //Check the length of the CA certificate
638  {
639  //The second pass decodes the PEM certificate
640  error = pemImportCertificate(input, length, context->caCert,
641  &context->caCertLen, NULL);
642  }
643  else
644  {
645  //Report an error
646  error = ERROR_INVALID_LENGTH;
647  }
648  }
649  }
650  else
651  {
652  //Clear CA certificate
653  context->caCertLen = 0;
654  }
655  }
656  else
657  {
658  //Report an error
659  error = ERROR_INVALID_PARAMETER;
660  }
661 
662  //Return status code
663  return error;
664 }
665 
666 
667 /**
668  * @brief Store CA certificate
669  * @param[in] context Pointer to the SCEP client context
670  * @param[out] output Pointer to the buffer where to store the PEM-encoded
671  * CA certificate (optional parameter)
672  * @param[out] written Actual length of the CA certificate, in bytes
673  * @return Error code
674  **/
675 
677  char_t *output, size_t *written)
678 {
679  error_t error;
680  size_t n;
681  size_t length;
682  const uint8_t *data;
683  Asn1Tag tag;
684 
685  //Check parameters
686  if(context == NULL || written == NULL)
688 
689  //Initialize status code
690  error = NO_ERROR;
691 
692  //Total number of bytes that have been written
693  *written = 0;
694 
695  //Valid CA certificate chain?
696  if(context->caCertLen > 0)
697  {
698  //Point to the first certificate of the chain
699  data = context->caCert;
700  length = context->caCertLen;
701 
702  //The intermediate CA certificate is the leaf certificate
703  while(length > 0 && !error)
704  {
705  //Parse certificate
706  error = asn1ReadSequence(data, length, &tag);
707 
708  //Check status code
709  if(!error)
710  {
711  //Export the certificate to PEM format
712  error = pemExportCertificate(data, tag.totalLength, output, &n);
713  }
714 
715  //Check status code
716  if(!error)
717  {
718  //Next DER certificate
719  data += tag.totalLength;
720  length -= tag.totalLength;
721 
722  //Next PEM certificate
723  ASN1_INC_POINTER(output, n);
724  *written += n;
725  }
726  }
727  }
728  else
729  {
730  //Report an error
731  error = ERROR_NO_CERTIFICATE;
732  }
733 
734  //Return status code
735  return error;
736 }
737 
738 
739 /**
740  * @brief Request capabilities from a CA
741  * @param[in] context Pointer to the SCEP client context
742  * @param[out] caCaps List of CA capabilities
743  * @return Error code
744  **/
745 
747 {
748  error_t error;
749 
750  //Check parameters
751  if(context == NULL || caCaps == NULL)
753 
754  //Initialize variables
755  error = NO_ERROR;
756 
757  //Execute the sequence of HTTP requests
758  while(!error)
759  {
760  //Check SCEP client state
761  if(context->state == SCEP_CLIENT_STATE_CONNECTED ||
762  context->state == SCEP_CLIENT_STATE_CERT_POLL)
763  {
764  //Update SCEP client state
765  context->state = SCEP_CLIENT_STATE_GET_CA_CAPS;
766  }
767  else if(context->state == SCEP_CLIENT_STATE_GET_CA_CAPS)
768  {
769  //Perform GetCACaps operation
770  error = scepClientSendGetCaCaps(context);
771 
772  //Check status code
773  if(!error)
774  {
775  //Return CA capabilities
776  *caCaps = context->caCaps;
777 
778  //Update SCEP client state
779  context->state = SCEP_CLIENT_STATE_CONNECTED;
780  break;
781  }
782  }
783  else
784  {
785  //Invalid state
786  error = ERROR_WRONG_STATE;
787  }
788  }
789 
790  //Check status code
791  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
792  error == ERROR_RESPONSE_TOO_LARGE)
793  {
794  //Revert to default state
795  context->state = SCEP_CLIENT_STATE_CONNECTED;
796  }
797 
798  //Return status code
799  return error;
800 }
801 
802 
803 /**
804  * @brief Get CA certificate
805  * @param[in] context Pointer to the SCEP client context
806  * @return Error code
807  **/
808 
810 {
811  error_t error;
812 
813  //Make sure the SCEP client context is valid
814  if(context == NULL)
816 
817  //Initialize variables
818  error = NO_ERROR;
819 
820  //Execute the sequence of HTTP requests
821  while(!error)
822  {
823  //Check SCEP client state
824  if(context->state == SCEP_CLIENT_STATE_CONNECTED ||
825  context->state == SCEP_CLIENT_STATE_CERT_POLL)
826  {
827  //Update SCEP client state
828  context->state = SCEP_CLIENT_STATE_GET_CA;
829  }
830  else if(context->state == SCEP_CLIENT_STATE_GET_CA)
831  {
832  //Perform GetCACert operation
833  error = scepClientSendGetCaCert(context);
834 
835  //Check status code
836  if(!error)
837  {
838  //Update SCEP client state
839  context->state = SCEP_CLIENT_STATE_CONNECTED;
840  break;
841  }
842  }
843  else
844  {
845  //Invalid state
846  error = ERROR_WRONG_STATE;
847  }
848  }
849 
850  //Check status code
851  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
852  error == ERROR_RESPONSE_TOO_LARGE)
853  {
854  //Revert to default state
855  context->state = SCEP_CLIENT_STATE_CONNECTED;
856  }
857 
858  //Return status code
859  return error;
860 }
861 
862 
863 /**
864  * @brief Certificate enrollment
865  * @param[in] context Pointer to the SCEP client context
866  * @return Error code
867  **/
868 
870 {
871  error_t error;
872 
873  //Make sure the SCEP client context is valid
874  if(context == NULL)
876 
877  //Initialize variables
878  error = NO_ERROR;
879 
880  //Execute the sequence of HTTP requests
881  while(!error)
882  {
883  //Check SCEP client state
884  if(context->state == SCEP_CLIENT_STATE_CONNECTED ||
885  context->state == SCEP_CLIENT_STATE_CERT_POLL)
886  {
887  //Update SCEP client state
888  context->state = SCEP_CLIENT_STATE_GET_CA;
889  }
890  else if(context->state == SCEP_CLIENT_STATE_GET_CA)
891  {
892  //If the CA certificate(s) have not previously been acquired by the
893  //client through some other means, the client must retrieve them before
894  //any PKI operation can be started (refer to RFC 8894, section 2.2)
895  if(context->caCertLen == 0)
896  {
897  //Perform GetCACert operation
898  error = scepClientSendGetCaCert(context);
899  }
900 
901  //Check status code
902  if(!error)
903  {
904  //Update SCEP client state
905  context->state = SCEP_CLIENT_STATE_GET_CA_CAPS;
906  }
907  }
908  else if(context->state == SCEP_CLIENT_STATE_GET_CA_CAPS)
909  {
910  //In order to provide support for future enhancements to the protocol,
911  //CAs must implement the GetCACaps message to allow clients to query
912  //which functionality is available from the CA (refer to RFC 8894,
913  //section 3.5)
914  if(context->caCaps == 0)
915  {
916  //Perform GetCACaps operation
917  error = scepClientSendGetCaCaps(context);
918  }
919 
920  //Check status code
921  if(!error)
922  {
923  //Update SCEP client state
924  context->state = SCEP_CLIENT_STATE_CSR_GEN;
925  }
926  }
927  else if(context->state == SCEP_CLIENT_STATE_CSR_GEN)
928  {
929  //Generate PKCS #10 certificate request
930  error = scepClientGenerateCsr(context);
931 
932  //Check status code
933  if(!error)
934  {
935  //Update SCEP client state
937  }
938  }
939  else if(context->state == SCEP_CLIENT_STATE_SELF_SIGNED_CERT_GEN)
940  {
941  //If the client does not have an appropriate existing certificate, then
942  //a locally generated self-signed certificate must be used (refer to
943  //RFC 8894, section 2.3)
944  error = scepClientGenerateSelfSignedCert(context);
945 
946  //Check status code
947  if(!error)
948  {
949  //Update SCEP client state
950  context->state = SCEP_CLIENT_STATE_TRANSACTION_ID_GEN;
951  }
952  }
953  else if(context->state == SCEP_CLIENT_STATE_TRANSACTION_ID_GEN)
954  {
955  //The client must use a unique string as the transaction identifier,
956  //encoded as a PrintableString, which must be used for all PKI messages
957  //exchanged for a given operation, such as a certificate issue (refer
958  //to RFC 8894, section 3.2.1.1)
959  error = scepClientGenerateTransactionId(context);
960 
961  //Check status code
962  if(!error)
963  {
964  //Update SCEP client state
965  context->state = SCEP_CLIENT_STATE_PKCS_REQ;
966  }
967  }
968  else if(context->state == SCEP_CLIENT_STATE_PKCS_REQ)
969  {
970  //Perform PKCSReq operation
971  error = scepClientSendPkcsReq(context);
972 
973  //Check status code
974  if(!error)
975  {
976  //Update SCEP client state
977  context->state = SCEP_CLIENT_STATE_CONNECTED;
978  break;
979  }
980  }
981  else
982  {
983  //Invalid state
984  error = ERROR_WRONG_STATE;
985  }
986  }
987 
988  //Check status code
989  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
991  {
992  //Revert to default state
993  context->state = SCEP_CLIENT_STATE_CONNECTED;
994  }
995  else if(error == ERROR_REQUEST_PENDING)
996  {
997  //If the CA is configured to manually authenticate the client, a CertRep
998  //PENDING message may be returned
999  context->state = SCEP_CLIENT_STATE_CERT_POLL;
1000  }
1001  else
1002  {
1003  //Just for sanity
1004  }
1005 
1006  //Return status code
1007  return error;
1008 }
1009 
1010 
1011 /**
1012  * @brief Certificate renewal
1013  * @param[in] context Pointer to the SCEP client context
1014  * @return Error code
1015  **/
1016 
1018 {
1019  error_t error;
1020 
1021  //Make sure the SCEP client context is valid
1022  if(context == NULL)
1023  return ERROR_INVALID_PARAMETER;
1024 
1025  //Check whether the previously issued certificate is valid
1026  if(context->certLen == 0)
1027  return ERROR_BAD_CERTIFICATE;
1028 
1029  //Initialize variables
1030  error = NO_ERROR;
1031 
1032  //Execute the sequence of HTTP requests
1033  while(!error)
1034  {
1035  //Check SCEP client state
1036  if(context->state == SCEP_CLIENT_STATE_CONNECTED ||
1037  context->state == SCEP_CLIENT_STATE_CERT_POLL)
1038  {
1039  //Update SCEP client state
1040  context->state = SCEP_CLIENT_STATE_GET_CA;
1041  }
1042  else if(context->state == SCEP_CLIENT_STATE_GET_CA)
1043  {
1044  //If the CA certificate(s) have not previously been acquired by the
1045  //client through some other means, the client must retrieve them before
1046  //any PKI operation can be started (refer to RFC 8894, section 2.2)
1047  if(context->caCertLen == 0)
1048  {
1049  //Perform GetCACert operation
1050  error = scepClientSendGetCaCert(context);
1051  }
1052 
1053  //Check status code
1054  if(!error)
1055  {
1056  //Update SCEP client state
1057  context->state = SCEP_CLIENT_STATE_GET_CA_CAPS;
1058  }
1059  }
1060  else if(context->state == SCEP_CLIENT_STATE_GET_CA_CAPS)
1061  {
1062  //In order to provide support for future enhancements to the protocol,
1063  //CAs must implement the GetCACaps message to allow clients to query
1064  //which functionality is available from the CA (refer to RFC 8894,
1065  //section 3.5)
1066  if(context->caCaps == 0)
1067  {
1068  //Perform GetCACaps operation
1069  error = scepClientSendGetCaCaps(context);
1070  }
1071 
1072  //Check status code
1073  if(!error)
1074  {
1075  //Update SCEP client state
1076  context->state = SCEP_CLIENT_STATE_CSR_GEN;
1077  }
1078  }
1079  else if(context->state == SCEP_CLIENT_STATE_CSR_GEN)
1080  {
1081  //Generate PKCS #10 certificate request
1082  error = scepClientGenerateCsr(context);
1083 
1084  //Check status code
1085  if(!error)
1086  {
1087  //Update SCEP client state
1088  context->state = SCEP_CLIENT_STATE_TRANSACTION_ID_GEN;
1089  }
1090  }
1091  else if(context->state == SCEP_CLIENT_STATE_TRANSACTION_ID_GEN)
1092  {
1093  //The client must use a unique string as the transaction identifier,
1094  //encoded as a PrintableString, which must be used for all PKI messages
1095  //exchanged for a given operation, such as a certificate issue (refer
1096  //to RFC 8894, section 3.2.1.1)
1097  error = scepClientGenerateTransactionId(context);
1098 
1099  //Check status code
1100  if(!error)
1101  {
1102  //Update SCEP client state
1103  context->state = SCEP_CLIENT_STATE_RENEWAL_REQ;
1104  }
1105  }
1106  else if(context->state == SCEP_CLIENT_STATE_RENEWAL_REQ)
1107  {
1108  //Perform RenewalReq operation
1109  error = scepClientSendRenewalReq(context);
1110 
1111  //Check status code
1112  if(!error)
1113  {
1114  //Update SCEP client state
1115  context->state = SCEP_CLIENT_STATE_CONNECTED;
1116  break;
1117  }
1118  }
1119  else
1120  {
1121  //Invalid state
1122  error = ERROR_WRONG_STATE;
1123  }
1124  }
1125 
1126  //Check status code
1127  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1128  error == ERROR_RESPONSE_TOO_LARGE || error == ERROR_REQUEST_REJECTED)
1129  {
1130  //Revert to default state
1131  context->state = SCEP_CLIENT_STATE_CONNECTED;
1132  }
1133  else if(error == ERROR_REQUEST_PENDING)
1134  {
1135  //If the CA is configured to manually authenticate the client, a CertRep
1136  //PENDING message may be returned
1137  context->state = SCEP_CLIENT_STATE_CERT_POLL;
1138  }
1139  else
1140  {
1141  //Just for sanity
1142  }
1143 
1144  //Return status code
1145  return error;
1146 }
1147 
1148 
1149 /**
1150  * @brief Certificate polling
1151  * @param[in] context Pointer to the SCEP client context
1152  * @return Error code
1153  **/
1154 
1156 {
1157  error_t error;
1158 
1159  //Make sure the SCEP client context is valid
1160  if(context == NULL)
1161  return ERROR_INVALID_PARAMETER;
1162 
1163  //Initialize variables
1164  error = NO_ERROR;
1165 
1166  //Execute the sequence of HTTP requests
1167  while(!error)
1168  {
1169  //Check SCEP client state
1170  if(context->state == SCEP_CLIENT_STATE_CERT_POLL)
1171  {
1172  //Perform CertPoll operation
1173  error = scepClientSendCertPoll(context);
1174 
1175  //Check status code
1176  if(!error)
1177  {
1178  //Update SCEP client state
1179  context->state = SCEP_CLIENT_STATE_CONNECTED;
1180  break;
1181  }
1182  }
1183  else
1184  {
1185  //Invalid state
1186  error = ERROR_WRONG_STATE;
1187  }
1188  }
1189 
1190  //Check status code
1191  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1192  error == ERROR_RESPONSE_TOO_LARGE || error == ERROR_REQUEST_REJECTED)
1193  {
1194  //Revert to default state
1195  context->state = SCEP_CLIENT_STATE_CONNECTED;
1196  }
1197  else if(error == ERROR_REQUEST_PENDING)
1198  {
1199  //If the CA is configured to manually authenticate the client, a CertRep
1200  //PENDING message may be returned
1201  context->state = SCEP_CLIENT_STATE_CERT_POLL;
1202  }
1203  else
1204  {
1205  //Just for sanity
1206  }
1207 
1208  //Return status code
1209  return error;
1210 }
1211 
1212 
1213 /**
1214  * @brief Get failure reason
1215  * @param[in] context Pointer to the SCEP client context
1216  * @return Failure reason
1217  **/
1218 
1220 {
1221  ScepFailInfo failInfo;
1222 
1223  //Make sure the SCEP client context is valid
1224  if(context != NULL)
1225  {
1226  failInfo = (ScepFailInfo) context->failInfo;
1227  }
1228  else
1229  {
1230  failInfo = (ScepFailInfo) 0;
1231  }
1232 
1233  //Return the value of the failInfo attribute
1234  return failInfo;
1235 }
1236 
1237 
1238 /**
1239  * @brief Gracefully disconnect from the SCEP server
1240  * @param[in] context Pointer to the SCEP client context
1241  * @return Error code
1242  **/
1243 
1245 {
1246  error_t error;
1247 
1248  //Make sure the SCEP client context is valid
1249  if(context == NULL)
1250  return ERROR_INVALID_PARAMETER;
1251 
1252  //Initialize status code
1253  error = NO_ERROR;
1254 
1255  //Gracefully disconnect from the SCEP server
1256  while(!error)
1257  {
1258  //Check SCEP client state
1259  if(context->state == SCEP_CLIENT_STATE_CONNECTED)
1260  {
1261  //Gracefully shutdown HTTP connection
1262  context->state = SCEP_CLIENT_STATE_DISCONNECTING;
1263  }
1264  else if(context->state == SCEP_CLIENT_STATE_DISCONNECTING)
1265  {
1266  //Gracefully shutdown HTTP connection
1267  error = httpClientDisconnect(&context->httpClientContext);
1268 
1269  //Check status code
1270  if(error == NO_ERROR)
1271  {
1272  //Close HTTP connection
1273  httpClientClose(&context->httpClientContext);
1274  //Update SCEP client state
1275  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
1276  }
1277  }
1278  else if(context->state == SCEP_CLIENT_STATE_DISCONNECTED)
1279  {
1280  //The client is disconnected from the SCEP server
1281  break;
1282  }
1283  else
1284  {
1285  //Invalid state
1286  error = ERROR_WRONG_STATE;
1287  }
1288  }
1289 
1290  //Failed to gracefully disconnect from the SCEP server?
1291  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
1292  {
1293  //Close HTTP connection
1294  httpClientClose(&context->httpClientContext);
1295  //Update SCEP client state
1296  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
1297  }
1298 
1299  //Return status code
1300  return error;
1301 }
1302 
1303 
1304 /**
1305  * @brief Close the connection with the SCEP server
1306  * @param[in] context Pointer to the SCEP client context
1307  * @return Error code
1308  **/
1309 
1311 {
1312  //Make sure the SCEP client context is valid
1313  if(context == NULL)
1314  return ERROR_INVALID_PARAMETER;
1315 
1316  //Close HTTP connection
1317  httpClientClose(&context->httpClientContext);
1318  //Update SCEP client state
1319  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
1320 
1321  //Successful processing
1322  return NO_ERROR;
1323 }
1324 
1325 
1326 /**
1327  * @brief Release SCEP client context
1328  * @param[in] context Pointer to the SCEP client context
1329  **/
1330 
1332 {
1333  //Make sure the SCEP client context is valid
1334  if(context != NULL)
1335  {
1336  //Release HTTP client context
1337  httpClientDeinit(&context->httpClientContext);
1338 
1339  //Release public/private key pair
1340  scepClientUnloadKeyPair(context);
1341 
1342  //Clear SCEP client context
1343  osMemset(context, 0, sizeof(ScepClientContext));
1344  }
1345 }
1346 
1347 #endif
error_t scepClientRegisterTlsInitCallback(ScepClientContext *context, ScepClientTlsInitCallback callback)
Register TLS initialization callback function.
Definition: scep_client.c:104
@ X509_KEY_TYPE_RSA
Definition: x509_common.h:635
error_t scepClientSendCertPoll(ScepClientContext *context)
Perform CertPoll operation.
error_t httpClientDisconnect(HttpClientContext *context)
Gracefully disconnect from the HTTP server.
Definition: http_client.c:2151
error_t scepClientGetCaCert(ScepClientContext *context)
Get CA certificate.
Definition: scep_client.c:809
void rsaFreePublicKey(RsaPublicKey *key)
Release an RSA public key.
Definition: rsa.c:113
error_t httpClientBindToInterface(HttpClientContext *context, NetInterface *interface)
Bind the HTTP client to a particular network interface.
Definition: http_client.c:246
@ ERROR_WOULD_BLOCK
Definition: error.h:96
#define ScepClientContext
Definition: scep_client.h:165
IP network address.
Definition: ip.h:90
#define PrngAlgo
Definition: crypto.h:980
error_t scepClientGenerateTransactionId(ScepClientContext *context)
Transaction identifier generation.
#define SCEP_CLIENT_MAX_HOST_LEN
Definition: scep_client.h:111
error_t scepClientConnect(ScepClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Specify the address of the SCEP server.
Definition: scep_client.c:312
uint8_t data[]
Definition: ethernet.h:224
@ SCEP_CLIENT_STATE_CSR_GEN
Definition: scep_client.h:184
uint8_t type
Definition: coap_common.h:176
#define SCEP_CLIENT_DEFAULT_TIMEOUT
Definition: scep_client.h:97
@ SCEP_CLIENT_STATE_PKCS_REQ
Definition: scep_client.h:187
error_t scepClientInit(ScepClientContext *context)
SCEP client initialization.
Definition: scep_client.c:62
PEM key file import functions.
error_t scepClientLoadKeyPair(ScepClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen, const char_t *password)
Load public/private key pair.
Definition: scep_client.c:420
#define osStrlen(s)
Definition: os_port.h:168
error_t scepClientSendRenewalReq(ScepClientContext *context)
Perform RenewalReq operation.
void rsaInitPrivateKey(RsaPrivateKey *key)
Initialize an RSA private key.
Definition: rsa.c:126
error_t scepClientStoreCert(ScepClientContext *context, char_t *output, size_t *written)
Store client's certificate.
Definition: scep_client.c:580
error_t httpClientSetVersion(HttpClientContext *context, HttpVersion version)
Set the HTTP protocol version to be used.
Definition: http_client.c:162
error_t scepClientSetHost(ScepClientContext *context, const char_t *host)
Set the domain name of the SCEP server.
Definition: scep_client.c:240
@ SCEP_CLIENT_STATE_TRANSACTION_ID_GEN
Definition: scep_client.h:186
@ ERROR_WRONG_STATE
Definition: error.h:210
error_t scepClientRegisterCaCertVerifyCallback(ScepClientContext *context, ScepClientCaCertVerifyCallback callback)
Register CA certificate verification callback function.
Definition: scep_client.c:128
void httpClientDeinit(HttpClientContext *context)
Release HTTP client context.
Definition: http_client.c:2247
@ SCEP_CLIENT_STATE_GET_CA_CAPS
Definition: scep_client.h:182
error_t scepClientClose(ScepClientContext *context)
Close the connection with the SCEP server.
Definition: scep_client.c:1310
size_t totalLength
Definition: asn1.h:111
error_t scepClientGetCaCaps(ScepClientContext *context, uint_t *caCaps)
Request capabilities from a CA.
Definition: scep_client.c:746
error_t scepClientSetUri(ScepClientContext *context, const char_t *uri)
Set request URI.
Definition: scep_client.c:265
error_t pemImportCertificate(const char_t *input, size_t inputLen, uint8_t *output, size_t *outputLen, size_t *consumed)
Decode a PEM file containing a certificate.
Definition: pem_import.c:55
error_t pemImportRsaPrivateKey(RsaPrivateKey *privateKey, const char_t *input, size_t length, const char_t *password)
Decode a PEM file containing an RSA private key.
PEM file import functions.
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
@ ERROR_UNEXPECTED_STATUS
Definition: error.h:284
void scepClientUnloadKeyPair(ScepClientContext *context)
Unload public/private key pair.
Definition: scep_client.c:488
error_t
Error codes.
Definition: error.h:43
void rsaFreePrivateKey(RsaPrivateKey *key)
Release an RSA private key.
Definition: rsa.c:148
@ ERROR_RESPONSE_TOO_LARGE
Definition: error.h:285
ASN.1 tag.
Definition: asn1.h:105
void scepClientDeinit(ScepClientContext *context)
Release SCEP client context.
Definition: scep_client.c:1331
#define ASN1_INC_POINTER(p, n)
Definition: asn1.h:58
error_t httpClientRegisterTlsInitCallback(HttpClientContext *context, HttpClientTlsInitCallback callback)
Register TLS initialization callback function.
Definition: http_client.c:111
error_t pemImportRsaPublicKey(RsaPublicKey *publicKey, const char_t *input, size_t length)
Decode a PEM file containing an RSA public key.
#define NetInterface
Definition: net.h:36
error_t httpClientSetTimeout(HttpClientContext *context, systime_t timeout)
Set communication timeout.
Definition: http_client.c:187
error_t httpClientClose(HttpClientContext *context)
Close the connection with the HTTP server.
Definition: http_client.c:2226
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ ERROR_BAD_CERTIFICATE
Definition: error.h:236
error_t(* ScepClientCsrGenCallback)(ScepClientContext *context, uint8_t *buffer, size_t size, size_t *length)
CSR generation callback function.
Definition: scep_client.h:238
@ ERROR_NO_CERTIFICATE
Definition: error.h:235
ScepFailInfo
Fail info.
Definition: scep_common.h:80
error_t scepClientSetTimeout(ScepClientContext *context, systime_t timeout)
Set communication timeout.
Definition: scep_client.c:219
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
error_t httpClientInit(HttpClientContext *context)
Initialize HTTP client context.
Definition: http_client.c:66
error_t scepClientLoadCaCert(ScepClientContext *context, char_t *input, size_t length)
Load out of band CA certificate.
Definition: scep_client.c:615
error_t scepClientRegisterCsrGenCallback(ScepClientContext *context, ScepClientCsrGenCallback callback)
Register CSR generation callback function.
Definition: scep_client.c:150
PEM file export functions.
#define SCEP_CLIENT_MAX_CA_CERT_LEN
Definition: scep_client.h:146
error_t scepClientStoreCaCert(ScepClientContext *context, char_t *output, size_t *written)
Store CA certificate.
Definition: scep_client.c:676
uint32_t systime_t
System time.
error_t scepClientRegisterSelfSignedCertGenCallback(ScepClientContext *context, ScepClientSelfSignedCertGenCallback callback)
Register self-signed certificate generation callback function.
Definition: scep_client.c:172
SCEP client.
error_t scepClientPoll(ScepClientContext *context)
Certificate polling.
Definition: scep_client.c:1155
char char_t
Definition: compiler_port.h:55
@ SCEP_CLIENT_STATE_GET_CA
Definition: scep_client.h:183
error_t httpClientConnect(HttpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish a connection with the specified HTTP server.
Definition: http_client.c:269
@ SCEP_CLIENT_STATE_CERT_POLL
Definition: scep_client.h:189
#define SCEP_CLIENT_MAX_URI_LEN
Definition: scep_client.h:118
uint8_t n
@ HTTP_VERSION_1_1
Definition: http_common.h:63
error_t scepClientRenew(ScepClientContext *context)
Certificate renewal.
Definition: scep_client.c:1017
#define SCEP_CLIENT_MAX_CERT_LEN
Definition: scep_client.h:139
error_t pemExportCertificate(const uint8_t *cert, size_t certLen, char_t *output, size_t *written)
Export an X.509 certificate to PEM format.
Definition: pem_export.c:53
error_t(* ScepClientSelfSignedCertGenCallback)(ScepClientContext *context, uint8_t *buffer, size_t size, size_t *length)
Self-signed certificate generation callback function.
Definition: scep_client.h:246
error_t(* ScepClientTlsInitCallback)(HttpClientContext *context, TlsContext *tlsContext)
TLS initialization callback function.
Definition: scep_client.h:220
@ SCEP_CLIENT_STATE_DISCONNECTED
Definition: scep_client.h:179
error_t scepClientGenerateSelfSignedCert(ScepClientContext *context)
Generate self-signed certificate.
error_t scepClientSendGetCaCaps(ScepClientContext *context)
Perform GetCACaps operation.
ScepFailInfo scepClientGetFailInfo(ScepClientContext *context)
Get failure reason.
Definition: scep_client.c:1219
error_t scepClientSendPkcsReq(ScepClientContext *context)
Perform PKCSReq operation.
error_t(* ScepClientCaCertVerifyCallback)(ScepClientContext *context, const X509CertInfo *certInfo)
CA certificate verification callback function.
Definition: scep_client.h:230
error_t scepClientEnroll(ScepClientContext *context)
Certificate enrollment.
Definition: scep_client.c:869
error_t scepClientSendGetCaCert(ScepClientContext *context)
Perform GetCACert operation.
@ ERROR_REQUEST_REJECTED
Definition: error.h:273
@ SCEP_CLIENT_STATE_CONNECTED
Definition: scep_client.h:181
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:138
@ SCEP_CLIENT_STATE_RENEWAL_REQ
Definition: scep_client.h:188
X509KeyType
Public Key types.
Definition: x509_common.h:633
error_t asn1ReadSequence(const uint8_t *data, size_t length, Asn1Tag *tag)
Read an ASN.1 sequence from the input stream.
Definition: asn1.c:163
SCEP operations.
error_t scepClientGenerateCsr(ScepClientContext *context)
Generate PKCS #10 certificate request.
@ ERROR_REQUEST_PENDING
Definition: error.h:310
Helper functions for SCEP client.
@ SCEP_CLIENT_STATE_SELF_SIGNED_CERT_GEN
Definition: scep_client.h:185
#define osStrcpy(s1, s2)
Definition: os_port.h:210
error_t scepClientSetPrng(ScepClientContext *context, const PrngAlgo *prngAlgo, void *prngContext)
Set the pseudo-random number generator to be used.
Definition: scep_client.c:195
@ ERROR_INVALID_RESPONSE
Definition: error.h:71
error_t scepClientLoadCert(ScepClientContext *context, char_t *input, size_t length)
Load client's certificate.
Definition: scep_client.c:519
error_t scepClientBindToInterface(ScepClientContext *context, NetInterface *interface)
Bind the SCEP client to a particular network interface.
Definition: scep_client.c:289
error_t scepClientDisconnect(ScepClientContext *context)
Gracefully disconnect from the SCEP server.
Definition: scep_client.c:1244
@ ERROR_INVALID_KEY
Definition: error.h:106
@ NO_ERROR
Success.
Definition: error.h:44
@ SCEP_CLIENT_STATE_CONNECTING
Definition: scep_client.h:180
X509KeyType pemGetPublicKeyType(const char_t *input, size_t length)
Extract the public key type from a PEM file.
Debugging facilities.
void rsaInitPublicKey(RsaPublicKey *key)
Initialize an RSA public key.
Definition: rsa.c:100
ASN.1 (Abstract Syntax Notation One)
@ SCEP_CLIENT_STATE_DISCONNECTING
Definition: scep_client.h:190