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) 2024-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSCEP 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.4
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  //Since SCEP messages are already cryptographically secured, it does
335  //not require TLS (refer to RFC 8894, section 7.10)
336  if(context->tlsInitCallback != NULL)
337  {
338  //Register TLS initialization callback
339  error = httpClientRegisterTlsInitCallback(&context->httpClientContext,
340  scepClientInitTlsContext, context);
341  }
342 #endif
343  //Check status code
344  if(!error)
345  {
346  //Select HTTP protocol version
347  error = httpClientSetVersion(&context->httpClientContext,
349  }
350 
351  //Check status code
352  if(!error)
353  {
354  //Set timeout value for blocking operations
355  error = httpClientSetTimeout(&context->httpClientContext,
356  context->timeout);
357  }
358 
359  //Check status code
360  if(!error)
361  {
362  //Bind the HTTP client to the relevant network interface
363  error = httpClientBindToInterface(&context->httpClientContext,
364  context->interface);
365  }
366 
367  //Check status code
368  if(!error)
369  {
370  //Establish HTTP connection
371  context->state = SCEP_CLIENT_STATE_CONNECTING;
372  }
373  }
374  else if(context->state == SCEP_CLIENT_STATE_CONNECTING)
375  {
376  //Establish HTTP connection
377  error = httpClientConnect(&context->httpClientContext, serverIpAddr,
378  serverPort);
379 
380  //Check status code
381  if(error == NO_ERROR)
382  {
383  //The HTTP connection is established
384  context->state = SCEP_CLIENT_STATE_CONNECTED;
385  }
386  }
387  else if(context->state == SCEP_CLIENT_STATE_CONNECTED)
388  {
389  //The client is connected to the SCEP server
390  break;
391  }
392  else
393  {
394  //Invalid state
395  error = ERROR_WRONG_STATE;
396  }
397  }
398 
399  //Failed to establish connection with the SCEP server?
400  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
401  {
402  //Clean up side effects
403  httpClientClose(&context->httpClientContext);
404  //Update SCEP client state
405  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
406  }
407 
408  //Return status code
409  return error;
410 }
411 
412 
413 /**
414  * @brief Load public/private key pair
415  * @param[in] context Pointer to the SCEP client context
416  * @param[in] publicKey Public key (PEM format)
417  * @param[in] publicKeyLen Length of the public key
418  * @param[in] privateKey Private key (PEM format)
419  * @param[in] privateKeyLen Length of the private key
420  * @param[in] password NULL-terminated string containing the password. This
421  * parameter is required if the private key is encrypted
422  * @return Error code
423  **/
424 
426  const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey,
427  size_t privateKeyLen, const char_t *password)
428 {
429  error_t error;
431 
432  //Check parameters
433  if(context == NULL || publicKey == NULL || publicKeyLen == 0 ||
434  privateKey == NULL || privateKeyLen == 0)
435  {
437  }
438 
439  //Release the current key pair, if any
440  scepClientUnloadKeyPair(context);
441 
442  //Extract the type of the public key
443  type = pemGetPublicKeyType(publicKey, publicKeyLen);
444 
445 #if (SCEP_CLIENT_RSA_SUPPORT == ENABLED)
446  //RSA public key?
447  if(type == X509_KEY_TYPE_RSA)
448  {
449  //Save public key type
450  context->keyType = X509_KEY_TYPE_RSA;
451 
452  //Initialize RSA public and private keys
453  rsaInitPublicKey(&context->rsaPublicKey);
454  rsaInitPrivateKey(&context->rsaPrivateKey);
455 
456  //Decode the PEM file that contains the RSA public key
457  error = pemImportRsaPublicKey(&context->rsaPublicKey, publicKey,
458  publicKeyLen);
459 
460  //Check status code
461  if(!error)
462  {
463  //Decode the PEM file that contains the RSA private key
464  error = pemImportRsaPrivateKey(&context->rsaPrivateKey, privateKey,
465  privateKeyLen, password);
466  }
467  }
468  else
469 #endif
470  //Invalid public key?
471  {
472  //The supplied public key is not valid
473  error = ERROR_INVALID_KEY;
474  }
475 
476  //Any error to report?
477  if(error)
478  {
479  //Clean up side effects
480  scepClientUnloadKeyPair(context);
481  }
482 
483  //Return status code
484  return error;
485 }
486 
487 
488 /**
489  * @brief Unload public/private key pair
490  * @param[in] context Pointer to the SCEP client context
491  **/
492 
494 {
495  //Make sure the SCEP client context is valid
496  if(context != NULL)
497  {
498 #if (SCEP_CLIENT_RSA_SUPPORT == ENABLED)
499  //RSA key pair?
500  if(context->keyType == X509_KEY_TYPE_RSA)
501  {
502  //Release RSA public and private keys
503  rsaFreePublicKey(&context->rsaPublicKey);
504  rsaFreePrivateKey(&context->rsaPrivateKey);
505  }
506  else
507 #endif
508  //Invalid key pair?
509  {
510  //Just for sanity
511  }
512  }
513 }
514 
515 
516 /**
517  * @brief Load client's certificate
518  * @param[in] context Pointer to the SCEP client context
519  * @param[out] input Pointer to the PEM-encoded certificate
520  * @param[out] length Length of the PEM-encoded certificate
521  * @return Error code
522  **/
523 
525  const char_t *input, size_t length)
526 {
527  error_t error;
528  size_t n;
529 
530  //Initialize status code
531  error = NO_ERROR;
532 
533  //Make sure the SCEP client context is valid
534  if(context != NULL)
535  {
536  //Valid certificate?
537  if(input != NULL && length > 0)
538  {
539  //The first pass calculates the length of the DER-encoded certificate
540  error = pemImportCertificate(input, length, NULL, &n, NULL);
541 
542  //Check status code
543  if(!error)
544  {
545  //Check the length of the certificate
547  {
548  //The second pass decodes the PEM certificate
549  error = pemImportCertificate(input, length, context->cert,
550  &context->certLen, NULL);
551  }
552  else
553  {
554  //Report an error
555  error = ERROR_INVALID_LENGTH;
556  }
557  }
558  }
559  else
560  {
561  //Clear certificate
562  context->certLen = 0;
563  }
564  }
565  else
566  {
567  //Report an error
568  error = ERROR_INVALID_PARAMETER;
569  }
570 
571  //Return status code
572  return error;
573 }
574 
575 
576 /**
577  * @brief Store client's certificate
578  * @param[in] context Pointer to the SCEP client context
579  * @param[out] output Pointer to the buffer where to store the PEM-encoded
580  * certificate (optional parameter)
581  * @param[out] written Length of the resulting PEM string
582  * @return Error code
583  **/
584 
586  char_t *output, size_t *written)
587 {
588  error_t error;
589 
590  //Check parameters
591  if(context == NULL || written == NULL)
593 
594  //Valid certificate?
595  if(context->certLen > 0)
596  {
597  //Export the certificate to PEM format
598  error = pemExportCertificate(context->cert, context->certLen, output,
599  written);
600  }
601  else
602  {
603  //Report an error
604  error = ERROR_NO_CERTIFICATE;
605  }
606 
607  //Return status code
608  return error;
609 }
610 
611 
612 /**
613  * @brief Load out of band CA certificate
614  * @param[in] context Pointer to the SCEP client context
615  * @param[out] input Pointer to the PEM-encoded CA certificate
616  * @param[out] length Length of the PEM-encoded CA certificate
617  * @return Error code
618  **/
619 
621  const char_t *input, size_t length)
622 {
623  error_t error;
624  size_t n;
625 
626  //Initialize status code
627  error = NO_ERROR;
628 
629  //Make sure the SCEP client context is valid
630  if(context != NULL)
631  {
632  //Valid certificate?
633  if(input != NULL && length > 0)
634  {
635  //The first pass calculates the length of the DER-encoded certificate
636  error = pemImportCertificate(input, length, NULL, &n, NULL);
637 
638  //Check status code
639  if(!error)
640  {
641  //Check the length of the CA certificate
643  {
644  //The second pass decodes the PEM certificate
645  error = pemImportCertificate(input, length, context->caCert,
646  &context->caCertLen, NULL);
647  }
648  else
649  {
650  //Report an error
651  error = ERROR_INVALID_LENGTH;
652  }
653  }
654  }
655  else
656  {
657  //Clear CA certificate
658  context->caCertLen = 0;
659  }
660  }
661  else
662  {
663  //Report an error
664  error = ERROR_INVALID_PARAMETER;
665  }
666 
667  //Return status code
668  return error;
669 }
670 
671 
672 /**
673  * @brief Store CA certificate
674  * @param[in] context Pointer to the SCEP client context
675  * @param[out] output Pointer to the buffer where to store the PEM-encoded
676  * CA certificate (optional parameter)
677  * @param[out] written Length of the resulting PEM string
678  * @return Error code
679  **/
680 
682  char_t *output, size_t *written)
683 {
684  error_t error;
685  size_t n;
686  size_t length;
687  const uint8_t *data;
688  Asn1Tag tag;
689 
690  //Check parameters
691  if(context == NULL || written == NULL)
693 
694  //Initialize status code
695  error = NO_ERROR;
696 
697  //Total number of bytes that have been written
698  *written = 0;
699 
700  //Valid CA certificate chain?
701  if(context->caCertLen > 0)
702  {
703  //Point to the first certificate of the chain
704  data = context->caCert;
705  length = context->caCertLen;
706 
707  //The intermediate CA certificate is the leaf certificate
708  while(length > 0 && !error)
709  {
710  //Parse certificate
711  error = asn1ReadSequence(data, length, &tag);
712 
713  //Check status code
714  if(!error)
715  {
716  //Export the certificate to PEM format
717  error = pemExportCertificate(data, tag.totalLength, output, &n);
718  }
719 
720  //Check status code
721  if(!error)
722  {
723  //Next DER certificate
724  data += tag.totalLength;
725  length -= tag.totalLength;
726 
727  //Next PEM certificate
728  ASN1_INC_POINTER(output, n);
729  *written += n;
730  }
731  }
732  }
733  else
734  {
735  //Report an error
736  error = ERROR_NO_CERTIFICATE;
737  }
738 
739  //Return status code
740  return error;
741 }
742 
743 
744 /**
745  * @brief Request capabilities from a CA
746  * @param[in] context Pointer to the SCEP client context
747  * @param[out] caCaps List of CA capabilities
748  * @return Error code
749  **/
750 
752 {
753  error_t error;
754 
755  //Check parameters
756  if(context == NULL || caCaps == NULL)
758 
759  //Initialize variables
760  error = NO_ERROR;
761 
762  //Execute the sequence of HTTP requests
763  while(!error)
764  {
765  //Check SCEP client state
766  if(context->state == SCEP_CLIENT_STATE_CONNECTED ||
767  context->state == SCEP_CLIENT_STATE_CERT_POLL)
768  {
769  //Update SCEP client state
770  context->state = SCEP_CLIENT_STATE_GET_CA_CAPS;
771  }
772  else if(context->state == SCEP_CLIENT_STATE_GET_CA_CAPS)
773  {
774  //Perform GetCACaps operation
775  error = scepClientSendGetCaCaps(context);
776 
777  //Check status code
778  if(!error)
779  {
780  //Return CA capabilities
781  *caCaps = context->caCaps;
782 
783  //Update SCEP client state
784  context->state = SCEP_CLIENT_STATE_CONNECTED;
785  break;
786  }
787  }
788  else
789  {
790  //Invalid state
791  error = ERROR_WRONG_STATE;
792  }
793  }
794 
795  //Check status code
796  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
797  error == ERROR_RESPONSE_TOO_LARGE)
798  {
799  //Revert to default state
800  context->state = SCEP_CLIENT_STATE_CONNECTED;
801  }
802 
803  //Return status code
804  return error;
805 }
806 
807 
808 /**
809  * @brief Get CA certificate
810  * @param[in] context Pointer to the SCEP client context
811  * @return Error code
812  **/
813 
815 {
816  error_t error;
817 
818  //Make sure the SCEP client context is valid
819  if(context == NULL)
821 
822  //Initialize variables
823  error = NO_ERROR;
824 
825  //Execute the sequence of HTTP requests
826  while(!error)
827  {
828  //Check SCEP client state
829  if(context->state == SCEP_CLIENT_STATE_CONNECTED ||
830  context->state == SCEP_CLIENT_STATE_CERT_POLL)
831  {
832  //Update SCEP client state
833  context->state = SCEP_CLIENT_STATE_GET_CA;
834  }
835  else if(context->state == SCEP_CLIENT_STATE_GET_CA)
836  {
837  //Perform GetCACert operation
838  error = scepClientSendGetCaCert(context);
839 
840  //Check status code
841  if(!error)
842  {
843  //Update SCEP client state
844  context->state = SCEP_CLIENT_STATE_CONNECTED;
845  break;
846  }
847  }
848  else
849  {
850  //Invalid state
851  error = ERROR_WRONG_STATE;
852  }
853  }
854 
855  //Check status code
856  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
857  error == ERROR_RESPONSE_TOO_LARGE)
858  {
859  //Revert to default state
860  context->state = SCEP_CLIENT_STATE_CONNECTED;
861  }
862 
863  //Return status code
864  return error;
865 }
866 
867 
868 /**
869  * @brief Certificate enrollment
870  * @param[in] context Pointer to the SCEP client context
871  * @return Error code
872  **/
873 
875 {
876  error_t error;
877 
878  //Make sure the SCEP client context is valid
879  if(context == NULL)
881 
882  //Initialize variables
883  error = NO_ERROR;
884 
885  //Execute the sequence of HTTP requests
886  while(!error)
887  {
888  //Check SCEP client state
889  if(context->state == SCEP_CLIENT_STATE_CONNECTED ||
890  context->state == SCEP_CLIENT_STATE_CERT_POLL)
891  {
892  //Update SCEP client state
893  context->state = SCEP_CLIENT_STATE_GET_CA;
894  }
895  else if(context->state == SCEP_CLIENT_STATE_GET_CA)
896  {
897  //If the CA certificate(s) have not previously been acquired by the
898  //client through some other means, the client must retrieve them before
899  //any PKI operation can be started (refer to RFC 8894, section 2.2)
900  if(context->caCertLen == 0)
901  {
902  //Perform GetCACert operation
903  error = scepClientSendGetCaCert(context);
904  }
905 
906  //Check status code
907  if(!error)
908  {
909  //Update SCEP client state
910  context->state = SCEP_CLIENT_STATE_GET_CA_CAPS;
911  }
912  }
913  else if(context->state == SCEP_CLIENT_STATE_GET_CA_CAPS)
914  {
915  //In order to provide support for future enhancements to the protocol,
916  //CAs must implement the GetCACaps message to allow clients to query
917  //which functionality is available from the CA (refer to RFC 8894,
918  //section 3.5)
919  if(context->caCaps == 0)
920  {
921  //Perform GetCACaps operation
922  error = scepClientSendGetCaCaps(context);
923  }
924 
925  //Check status code
926  if(!error)
927  {
928  //Update SCEP client state
929  context->state = SCEP_CLIENT_STATE_CSR_GEN;
930  }
931  }
932  else if(context->state == SCEP_CLIENT_STATE_CSR_GEN)
933  {
934  //Generate PKCS #10 certificate request
935  error = scepClientGenerateCsr(context);
936 
937  //Check status code
938  if(!error)
939  {
940  //Update SCEP client state
942  }
943  }
944  else if(context->state == SCEP_CLIENT_STATE_SELF_SIGNED_CERT_GEN)
945  {
946  //If the client does not have an appropriate existing certificate, then
947  //a locally generated self-signed certificate must be used (refer to
948  //RFC 8894, section 2.3)
949  error = scepClientGenerateSelfSignedCert(context);
950 
951  //Check status code
952  if(!error)
953  {
954  //Update SCEP client state
955  context->state = SCEP_CLIENT_STATE_TRANSACTION_ID_GEN;
956  }
957  }
958  else if(context->state == SCEP_CLIENT_STATE_TRANSACTION_ID_GEN)
959  {
960  //The client must use a unique string as the transaction identifier,
961  //encoded as a PrintableString, which must be used for all PKI messages
962  //exchanged for a given operation, such as a certificate issue (refer
963  //to RFC 8894, section 3.2.1.1)
964  error = scepClientGenerateTransactionId(context);
965 
966  //Check status code
967  if(!error)
968  {
969  //Update SCEP client state
970  context->state = SCEP_CLIENT_STATE_PKCS_REQ;
971  }
972  }
973  else if(context->state == SCEP_CLIENT_STATE_PKCS_REQ)
974  {
975  //Perform PKCSReq operation
976  error = scepClientSendPkcsReq(context);
977 
978  //Check status code
979  if(!error)
980  {
981  //Update SCEP client state
982  context->state = SCEP_CLIENT_STATE_CONNECTED;
983  break;
984  }
985  }
986  else
987  {
988  //Invalid state
989  error = ERROR_WRONG_STATE;
990  }
991  }
992 
993  //Check status code
994  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
996  {
997  //Revert to default state
998  context->state = SCEP_CLIENT_STATE_CONNECTED;
999  }
1000  else if(error == ERROR_REQUEST_PENDING)
1001  {
1002  //If the CA is configured to manually authenticate the client, a CertRep
1003  //PENDING message may be returned
1004  context->state = SCEP_CLIENT_STATE_CERT_POLL;
1005  }
1006  else
1007  {
1008  //Just for sanity
1009  }
1010 
1011  //Return status code
1012  return error;
1013 }
1014 
1015 
1016 /**
1017  * @brief Certificate renewal
1018  * @param[in] context Pointer to the SCEP client context
1019  * @return Error code
1020  **/
1021 
1023 {
1024  error_t error;
1025 
1026  //Make sure the SCEP client context is valid
1027  if(context == NULL)
1028  return ERROR_INVALID_PARAMETER;
1029 
1030  //Check whether the previously issued certificate is valid
1031  if(context->certLen == 0)
1032  return ERROR_BAD_CERTIFICATE;
1033 
1034  //Initialize variables
1035  error = NO_ERROR;
1036 
1037  //Execute the sequence of HTTP requests
1038  while(!error)
1039  {
1040  //Check SCEP client state
1041  if(context->state == SCEP_CLIENT_STATE_CONNECTED ||
1042  context->state == SCEP_CLIENT_STATE_CERT_POLL)
1043  {
1044  //Update SCEP client state
1045  context->state = SCEP_CLIENT_STATE_GET_CA;
1046  }
1047  else if(context->state == SCEP_CLIENT_STATE_GET_CA)
1048  {
1049  //If the CA certificate(s) have not previously been acquired by the
1050  //client through some other means, the client must retrieve them before
1051  //any PKI operation can be started (refer to RFC 8894, section 2.2)
1052  if(context->caCertLen == 0)
1053  {
1054  //Perform GetCACert operation
1055  error = scepClientSendGetCaCert(context);
1056  }
1057 
1058  //Check status code
1059  if(!error)
1060  {
1061  //Update SCEP client state
1062  context->state = SCEP_CLIENT_STATE_GET_CA_CAPS;
1063  }
1064  }
1065  else if(context->state == SCEP_CLIENT_STATE_GET_CA_CAPS)
1066  {
1067  //In order to provide support for future enhancements to the protocol,
1068  //CAs must implement the GetCACaps message to allow clients to query
1069  //which functionality is available from the CA (refer to RFC 8894,
1070  //section 3.5)
1071  if(context->caCaps == 0)
1072  {
1073  //Perform GetCACaps operation
1074  error = scepClientSendGetCaCaps(context);
1075  }
1076 
1077  //Check status code
1078  if(!error)
1079  {
1080  //Update SCEP client state
1081  context->state = SCEP_CLIENT_STATE_CSR_GEN;
1082  }
1083  }
1084  else if(context->state == SCEP_CLIENT_STATE_CSR_GEN)
1085  {
1086  //Generate PKCS #10 certificate request
1087  error = scepClientGenerateCsr(context);
1088 
1089  //Check status code
1090  if(!error)
1091  {
1092  //Update SCEP client state
1093  context->state = SCEP_CLIENT_STATE_TRANSACTION_ID_GEN;
1094  }
1095  }
1096  else if(context->state == SCEP_CLIENT_STATE_TRANSACTION_ID_GEN)
1097  {
1098  //The client must use a unique string as the transaction identifier,
1099  //encoded as a PrintableString, which must be used for all PKI messages
1100  //exchanged for a given operation, such as a certificate issue (refer
1101  //to RFC 8894, section 3.2.1.1)
1102  error = scepClientGenerateTransactionId(context);
1103 
1104  //Check status code
1105  if(!error)
1106  {
1107  //Update SCEP client state
1108  context->state = SCEP_CLIENT_STATE_RENEWAL_REQ;
1109  }
1110  }
1111  else if(context->state == SCEP_CLIENT_STATE_RENEWAL_REQ)
1112  {
1113  //Perform RenewalReq operation
1114  error = scepClientSendRenewalReq(context);
1115 
1116  //Check status code
1117  if(!error)
1118  {
1119  //Update SCEP client state
1120  context->state = SCEP_CLIENT_STATE_CONNECTED;
1121  break;
1122  }
1123  }
1124  else
1125  {
1126  //Invalid state
1127  error = ERROR_WRONG_STATE;
1128  }
1129  }
1130 
1131  //Check status code
1132  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1133  error == ERROR_RESPONSE_TOO_LARGE || error == ERROR_REQUEST_REJECTED)
1134  {
1135  //Revert to default state
1136  context->state = SCEP_CLIENT_STATE_CONNECTED;
1137  }
1138  else if(error == ERROR_REQUEST_PENDING)
1139  {
1140  //If the CA is configured to manually authenticate the client, a CertRep
1141  //PENDING message may be returned
1142  context->state = SCEP_CLIENT_STATE_CERT_POLL;
1143  }
1144  else
1145  {
1146  //Just for sanity
1147  }
1148 
1149  //Return status code
1150  return error;
1151 }
1152 
1153 
1154 /**
1155  * @brief Certificate polling
1156  * @param[in] context Pointer to the SCEP client context
1157  * @return Error code
1158  **/
1159 
1161 {
1162  error_t error;
1163 
1164  //Make sure the SCEP client context is valid
1165  if(context == NULL)
1166  return ERROR_INVALID_PARAMETER;
1167 
1168  //Initialize variables
1169  error = NO_ERROR;
1170 
1171  //Execute the sequence of HTTP requests
1172  while(!error)
1173  {
1174  //Check SCEP client state
1175  if(context->state == SCEP_CLIENT_STATE_CERT_POLL)
1176  {
1177  //Perform CertPoll operation
1178  error = scepClientSendCertPoll(context);
1179 
1180  //Check status code
1181  if(!error)
1182  {
1183  //Update SCEP client state
1184  context->state = SCEP_CLIENT_STATE_CONNECTED;
1185  break;
1186  }
1187  }
1188  else
1189  {
1190  //Invalid state
1191  error = ERROR_WRONG_STATE;
1192  }
1193  }
1194 
1195  //Check status code
1196  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1197  error == ERROR_RESPONSE_TOO_LARGE || error == ERROR_REQUEST_REJECTED)
1198  {
1199  //Revert to default state
1200  context->state = SCEP_CLIENT_STATE_CONNECTED;
1201  }
1202  else if(error == ERROR_REQUEST_PENDING)
1203  {
1204  //If the CA is configured to manually authenticate the client, a CertRep
1205  //PENDING message may be returned
1206  context->state = SCEP_CLIENT_STATE_CERT_POLL;
1207  }
1208  else
1209  {
1210  //Just for sanity
1211  }
1212 
1213  //Return status code
1214  return error;
1215 }
1216 
1217 
1218 /**
1219  * @brief Get failure reason
1220  * @param[in] context Pointer to the SCEP client context
1221  * @return Failure reason
1222  **/
1223 
1225 {
1226  ScepFailInfo failInfo;
1227 
1228  //Make sure the SCEP client context is valid
1229  if(context != NULL)
1230  {
1231  failInfo = (ScepFailInfo) context->failInfo;
1232  }
1233  else
1234  {
1235  failInfo = (ScepFailInfo) 0;
1236  }
1237 
1238  //Return the value of the failInfo attribute
1239  return failInfo;
1240 }
1241 
1242 
1243 /**
1244  * @brief Gracefully disconnect from the SCEP server
1245  * @param[in] context Pointer to the SCEP client context
1246  * @return Error code
1247  **/
1248 
1250 {
1251  error_t error;
1252 
1253  //Make sure the SCEP client context is valid
1254  if(context == NULL)
1255  return ERROR_INVALID_PARAMETER;
1256 
1257  //Initialize status code
1258  error = NO_ERROR;
1259 
1260  //Gracefully disconnect from the SCEP server
1261  while(!error)
1262  {
1263  //Check SCEP client state
1264  if(context->state == SCEP_CLIENT_STATE_CONNECTED)
1265  {
1266  //Gracefully shutdown HTTP connection
1267  context->state = SCEP_CLIENT_STATE_DISCONNECTING;
1268  }
1269  else if(context->state == SCEP_CLIENT_STATE_DISCONNECTING)
1270  {
1271  //Gracefully shutdown HTTP connection
1272  error = httpClientDisconnect(&context->httpClientContext);
1273 
1274  //Check status code
1275  if(error == NO_ERROR)
1276  {
1277  //Close HTTP connection
1278  httpClientClose(&context->httpClientContext);
1279  //Update SCEP client state
1280  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
1281  }
1282  }
1283  else if(context->state == SCEP_CLIENT_STATE_DISCONNECTED)
1284  {
1285  //The client is disconnected from the SCEP server
1286  break;
1287  }
1288  else
1289  {
1290  //Invalid state
1291  error = ERROR_WRONG_STATE;
1292  }
1293  }
1294 
1295  //Failed to gracefully disconnect from the SCEP server?
1296  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
1297  {
1298  //Close HTTP connection
1299  httpClientClose(&context->httpClientContext);
1300  //Update SCEP client state
1301  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
1302  }
1303 
1304  //Return status code
1305  return error;
1306 }
1307 
1308 
1309 /**
1310  * @brief Close the connection with the SCEP server
1311  * @param[in] context Pointer to the SCEP client context
1312  * @return Error code
1313  **/
1314 
1316 {
1317  //Make sure the SCEP client context is valid
1318  if(context == NULL)
1319  return ERROR_INVALID_PARAMETER;
1320 
1321  //Close HTTP connection
1322  httpClientClose(&context->httpClientContext);
1323  //Update SCEP client state
1324  context->state = SCEP_CLIENT_STATE_DISCONNECTED;
1325 
1326  //Successful processing
1327  return NO_ERROR;
1328 }
1329 
1330 
1331 /**
1332  * @brief Release SCEP client context
1333  * @param[in] context Pointer to the SCEP client context
1334  **/
1335 
1337 {
1338  //Make sure the SCEP client context is valid
1339  if(context != NULL)
1340  {
1341  //Release HTTP client context
1342  httpClientDeinit(&context->httpClientContext);
1343 
1344  //Release public/private key pair
1345  scepClientUnloadKeyPair(context);
1346 
1347  //Clear SCEP client context
1348  osMemset(context, 0, sizeof(ScepClientContext));
1349  }
1350 }
1351 
1352 #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:637
error_t scepClientSendCertPoll(ScepClientContext *context)
Perform CertPoll operation.
error_t httpClientDisconnect(HttpClientContext *context)
Gracefully disconnect from the HTTP server.
Definition: http_client.c:2202
error_t scepClientGetCaCert(ScepClientContext *context)
Get CA certificate.
Definition: scep_client.c:814
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:297
@ 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:1008
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:425
#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:585
error_t httpClientSetVersion(HttpClientContext *context, HttpVersion version)
Set the HTTP protocol version to be used.
Definition: http_client.c:171
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:2298
@ 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:1315
size_t totalLength
Definition: asn1.h:111
error_t scepClientGetCaCaps(ScepClientContext *context, uint_t *caCaps)
Request capabilities from a CA.
Definition: scep_client.c:751
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:493
error_t
Error codes.
Definition: error.h:43
error_t(* ScepClientTlsInitCallback)(ScepClientContext *context, TlsContext *tlsContext)
TLS initialization callback function.
Definition: scep_client.h:220
void rsaFreePrivateKey(RsaPrivateKey *key)
Release an RSA private key.
Definition: rsa.c:148
error_t httpClientRegisterTlsInitCallback(HttpClientContext *context, HttpClientTlsInitCallback callback, void *param)
Register TLS initialization callback function.
Definition: http_client.c:118
@ 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:1336
#define ASN1_INC_POINTER(p, n)
Definition: asn1.h:58
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:196
error_t httpClientClose(HttpClientContext *context)
Close the connection with the HTTP server.
Definition: http_client.c:2277
@ 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:117
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 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:681
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:1160
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:320
@ 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:1022
error_t scepClientLoadCert(ScepClientContext *context, const char_t *input, size_t length)
Load client's certificate.
Definition: scep_client.c:524
#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
@ 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:1224
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:874
error_t scepClientSendGetCaCert(ScepClientContext *context)
Perform GetCACert operation.
@ ERROR_REQUEST_REJECTED
Definition: error.h:273
error_t scepClientLoadCaCert(ScepClientContext *context, const char_t *input, size_t length)
Load out of band CA certificate.
Definition: scep_client.c:620
@ 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:635
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 scepClientInitTlsContext(HttpClientContext *httpClientContext, TlsContext *tlsContext, void *param)
TLS initialization.
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:1249
@ 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