acme_client.c
Go to the documentation of this file.
1 /**
2  * @file acme_client.c
3  * @brief ACME client (Automatic Certificate Management Environment)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneACME 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  * ACME is a protocol that a CA and an applicant can use to automate the
30  * process of verification and certificate issuance. The protocol also
31  * provides facilities for other certificate management functions, such as
32  * certificate revocation. Refer to the following RFCs for complete details:
33  * - RFC 8555: Automatic Certificate Management Environment (ACME)
34  * - RFC 8737: ACME TLS Application-Layer Protocol Negotiation (ALPN) Challenge Extension
35  * - RFC 7515: JSON Web Signature (JWS)
36  * - RFC 7517: JSON Web Key (JWK)
37  * - RFC 7518: JSON Web Algorithms (JWA)
38  * - RFC 7638: JSON Web Key (JWK) Thumbprint
39  *
40  * @author Oryx Embedded SARL (www.oryx-embedded.com)
41  * @version 2.4.4
42  **/
43 
44 //Switch to the appropriate trace level
45 #define TRACE_LEVEL ACME_TRACE_LEVEL
46 
47 //Dependencies
48 #include "acme/acme_client.h"
50 #include "acme/acme_client_nonce.h"
52 #include "acme/acme_client_order.h"
53 #include "acme/acme_client_auth.h"
56 #include "acme/acme_client_misc.h"
57 #include "debug.h"
58 
59 //Check TCP/IP stack configuration
60 #if (ACME_CLIENT_SUPPORT == ENABLED)
61 
62 
63 /**
64  * @brief Initialize ACME client context
65  * @param[in] context Pointer to the ACME client context
66  * @return Error code
67  **/
68 
70 {
71  error_t error;
72 
73  //Make sure the ACME client context is valid
74  if(context == NULL)
76 
77  //Clear ACME client context
78  osMemset(context, 0, sizeof(AcmeClientContext));
79 
80  //Initialize HTTP client context
81  error = httpClientInit(&context->httpClientContext);
82  //Any error to report?
83  if(error)
84  return error;
85 
86  //Initialize ACME client state
87  context->state = ACME_CLIENT_STATE_DISCONNECTED;
88  //Initialize HTTP request state
89  context->requestState = ACME_REQ_STATE_INIT;
90  //Default timeout
91  context->timeout = ACME_CLIENT_DEFAULT_TIMEOUT;
92 
93  //Default directory URI
94  osStrcpy(context->directoryUri, "/directory");
95 
96  //Successful initialization
97  return NO_ERROR;
98 }
99 
100 
101 /**
102  * @brief Register TLS initialization callback function
103  * @param[in] context Pointer to the ACME client context
104  * @param[in] callback TLS initialization callback function
105  * @return Error code
106  **/
107 
109  AcmeClientTlsInitCallback callback)
110 {
111  //Make sure the ACME client context is valid
112  if(context == NULL)
114 
115  //Save callback function
116  context->tlsInitCallback = callback;
117 
118  //Successful processing
119  return NO_ERROR;
120 }
121 
122 
123 /**
124  * @brief Register CSR generation callback function
125  * @param[in] context Pointer to the ACME client context
126  * @param[in] callback TLS initialization callback function
127  * @return Error code
128  **/
129 
131  AcmeClientCsrCallback callback)
132 {
133  //Make sure the ACME client context is valid
134  if(context == NULL)
136 
137  //Save callback function
138  context->csrCallback = callback;
139 
140  //Successful processing
141  return NO_ERROR;
142 }
143 
144 
145 /**
146  * @brief Set the pseudo-random number generator to be used
147  * @param[in] context Pointer to the ACME client context
148  * @param[in] prngAlgo PRNG algorithm
149  * @param[in] prngContext Pointer to the PRNG context
150  * @return Error code
151  **/
152 
154  void *prngContext)
155 {
156  //Check parameters
157  if(context == NULL || prngAlgo == NULL || prngContext == NULL)
159 
160  //PRNG algorithm that will be used to generate random numbers
161  context->prngAlgo = prngAlgo;
162  //PRNG context
163  context->prngContext = prngContext;
164 
165  //Successful processing
166  return NO_ERROR;
167 }
168 
169 
170 /**
171  * @brief Set communication timeout
172  * @param[in] context Pointer to the ACME client context
173  * @param[in] timeout Timeout value, in milliseconds
174  * @return Error code
175  **/
176 
178 {
179  //Make sure the ACME client context is valid
180  if(context == NULL)
182 
183  //Save timeout value
184  context->timeout = timeout;
185 
186  //Successful processing
187  return NO_ERROR;
188 }
189 
190 
191 /**
192  * @brief Set the domain name of the ACME server
193  * @param[in] context Pointer to the ACME client context
194  * @param[in] host NULL-terminated string containing the host name
195  * @return Error code
196  **/
197 
199 {
200  //Check parameters
201  if(context == NULL || host == NULL)
203 
204  //Make sure the length of the host name is acceptable
206  return ERROR_INVALID_LENGTH;
207 
208  //Save host name
209  osStrcpy(context->serverName, host);
210 
211  //Successful processing
212  return NO_ERROR;
213 }
214 
215 
216 /**
217  * @brief Set the URI of the directory object
218  * @param[in] context Pointer to the ACME client context
219  * @param[in] directoryUri NULL-terminated string containing the directory URI
220  * @return Error code
221  **/
222 
224  const char_t *directoryUri)
225 {
226  //Check parameters
227  if(context == NULL || directoryUri == NULL)
229 
230  //Make sure the length of the URI is acceptable
231  if(osStrlen(directoryUri) > ACME_CLIENT_MAX_URI_LEN)
232  return ERROR_INVALID_LENGTH;
233 
234  //Save the URI of the directory object
235  osStrcpy(context->directoryUri, directoryUri);
236 
237  //Successful processing
238  return NO_ERROR;
239 }
240 
241 
242 /**
243  * @brief Bind the ACME client to a particular network interface
244  * @param[in] context Pointer to the ACME client context
245  * @param[in] interface Network interface to be used
246  * @return Error code
247  **/
248 
250  NetInterface *interface)
251 {
252  //Make sure the ACME client context is valid
253  if(context == NULL)
255 
256  //Explicitly associate the ACME client with the specified interface
257  context->interface = interface;
258 
259  //Successful processing
260  return NO_ERROR;
261 }
262 
263 
264 /**
265  * @brief Establish a connection with the specified ACME server
266  * @param[in] context Pointer to the ACME client context
267  * @param[in] serverIpAddr IP address of the ACME server to connect to
268  * @param[in] serverPort Port number
269  * @return Error code
270  **/
271 
273  const IpAddr *serverIpAddr, uint16_t serverPort)
274 {
275  error_t error;
276 
277  //Initialize status code
278  error = NO_ERROR;
279 
280  //Make sure the ACME client context is valid
281  if(context == NULL)
283 
284  //Establish connection with the HTTP server
285  while(!error)
286  {
287  //Check ACME client state
288  if(context->state == ACME_CLIENT_STATE_DISCONNECTED)
289  {
290  //Save the TCP port number to be used
291  context->serverPort = serverPort;
292 
293  //Use of HTTPS is required (refer to RFC 8555, section 6.1)
294  if(context->tlsInitCallback != NULL)
295  {
296  //Register TLS initialization callback
297  error = httpClientRegisterTlsInitCallback(&context->httpClientContext,
298  context->tlsInitCallback);
299  }
300  else
301  {
302  //Report an error
303  error = ERROR_INVALID_PARAMETER;
304  }
305 
306  //Check status code
307  if(!error)
308  {
309  //Select HTTP protocol version
310  error = httpClientSetVersion(&context->httpClientContext,
312  }
313 
314  //Check status code
315  if(!error)
316  {
317  //Set timeout value for blocking operations
318  error = httpClientSetTimeout(&context->httpClientContext,
319  context->timeout);
320  }
321 
322  //Check status code
323  if(!error)
324  {
325  //Bind the HTTP client to the relevant network interface
326  error = httpClientBindToInterface(&context->httpClientContext,
327  context->interface);
328  }
329 
330  //Check status code
331  if(!error)
332  {
333  //Establish HTTPS connection
334  context->state = ACME_CLIENT_STATE_CONNECTING;
335  }
336  }
337  else if(context->state == ACME_CLIENT_STATE_CONNECTING)
338  {
339  //Establish HTTPS connection
340  error = httpClientConnect(&context->httpClientContext, serverIpAddr,
341  serverPort);
342 
343  //Check status code
344  if(error == NO_ERROR)
345  {
346  //The HTTPS connection is established
347  context->state = ACME_CLIENT_STATE_CONNECTED;
348  }
349  }
350  else if(context->state == ACME_CLIENT_STATE_CONNECTED)
351  {
352  //Initialize HTTP request state
353  context->requestState = ACME_REQ_STATE_INIT;
354 
355  //Initialize the directory object
356  osMemset(&context->directory, 0, sizeof(AcmeDirectory));
357  //Invalidate the nonce
358  osMemset(context->nonce, 0, ACME_CLIENT_MAX_NONCE_LEN);
359 
360  //Reset error counter
361  context->badNonceErrors = 0;
362 
363  //The client is connected to the ACME server
364  break;
365  }
366  else
367  {
368  //Invalid state
369  error = ERROR_WRONG_STATE;
370  }
371  }
372 
373  //Failed to establish connection with the ACME server?
374  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
375  {
376  //Clean up side effects
377  httpClientClose(&context->httpClientContext);
378  //Update ACME client state
379  context->state = ACME_CLIENT_STATE_DISCONNECTED;
380  }
381 
382  //Return status code
383  return error;
384 }
385 
386 
387 /**
388  * @brief Load account key pair
389  * @param[in] context Pointer to the ACME client context
390  * @param[in] publicKey Public key (PEM format)
391  * @param[in] publicKeyLen Length of the public key
392  * @param[in] privateKey Private key (PEM format)
393  * @param[in] privateKeyLen Length of the private key
394  * @return Error code
395  **/
396 
398  const char_t *publicKey, size_t publicKeyLen,
399  const char_t *privateKey, size_t privateKeyLen)
400 {
401  //Check parameters
402  if(context == NULL || publicKey == NULL || publicKeyLen == 0 ||
403  privateKey == NULL || privateKeyLen == 0)
404  {
406  }
407 
408  //Release the current key pair, if any
409  acmeClientUnloadKeyPair(&context->accountKey);
410 
411  //The public and private keys are encoded in PEM format
412  return acmeClientLoadKeyPair(&context->accountKey, publicKey, publicKeyLen,
413  privateKey, privateKeyLen);
414 }
415 
416 
417 /**
418  * @brief Account creation
419  * @param[in] context Pointer to the ACME client context
420  * @param[in] params Account information
421  * @return Error code
422  **/
423 
425  const AcmeAccountParams *params)
426 {
427  error_t error;
428 
429  //Check parameters
430  if(context == NULL || params == NULL)
432 
433  //Initialize variables
434  error = NO_ERROR;
435 
436  //Execute the sequence of HTTP requests
437  while(!error)
438  {
439  //Check ACME client state
440  if(context->state == ACME_CLIENT_STATE_CONNECTED)
441  {
442  //Check account information
443  error = acmeClientCheckAccountParams(params);
444 
445  //Check status code
446  if(!error)
447  {
448  //Initialize account object
449  osMemset(&context->account, 0, sizeof(AcmeAccount));
450 
451  //Release the current key pair, if any
452  acmeClientUnloadKeyPair(&context->accountKey);
453 
454  //The public and private keys are encoded in PEM format
455  error = acmeClientLoadKeyPair(&context->accountKey,
456  params->publicKey, params->publicKeyLen,
457  params->privateKey, params->privateKeyLen);
458  }
459 
460  //Check status code
461  if(!error)
462  {
463  //In order to help clients configure themselves with the right URLs
464  //for each ACME operation, ACME servers provide a directory object
465  //(refer to RFC 8555, section 7.1.1)
466  context->state = ACME_CLIENT_STATE_DIRECTORY;
467  }
468  }
469  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
470  {
471  //If the directory object is no longer valid, the client must access
472  //the directory again by sending a GET request to the directory URL
473  error = acmeClientSendDirectoryRequest(context);
474 
475  //Check status code
476  if(!error)
477  {
478  //Update ACME client state
479  context->state = ACME_CLIENT_STATE_NEW_NONCE;
480  }
481  }
482  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
483  {
484  //Before sending a POST request to the server, an ACME client needs to
485  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
486  error = acmeClientSendNewNonceRequest(context);
487 
488  //Check status code
489  if(!error)
490  {
491  //Update ACME client state
492  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
493  }
494  }
495  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
496  {
497  //A client creates a new account by sending a POST request to the
498  //server's newAccount URL (refer to RFC 8555, section 7.3)
499  error = acmeClientSendNewAccountRequest(context, params, FALSE);
500 
501  //Check status code
502  if(!error)
503  {
504  //Update ACME client state
505  context->state = ACME_CLIENT_STATE_CONNECTED;
506  break;
507  }
508  }
509  else
510  {
511  //Invalid state
512  error = ERROR_WRONG_STATE;
513  }
514  }
515 
516  //Unexpected HTTP response?
517  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
518  error == ERROR_RESPONSE_TOO_LARGE)
519  {
520  //Revert to default state
521  context->state = ACME_CLIENT_STATE_CONNECTED;
522  }
523 
524  //Return status code
525  return error;
526 }
527 
528 
529 /**
530  * @brief Account information update
531  * @param[in] context Pointer to the ACME client context
532  * @param[in] params Updated account information
533  * @return Error code
534  **/
535 
537  const AcmeAccountParams *params)
538 {
539  error_t error;
540 
541  //Check parameters
542  if(context == NULL || params == NULL)
544 
545  //Initialize variables
546  error = NO_ERROR;
547 
548  //Execute the sequence of HTTP requests
549  while(!error)
550  {
551  //Check ACME client state
552  if(context->state == ACME_CLIENT_STATE_CONNECTED)
553  {
554  //Check account information
555  error = acmeClientCheckAccountParams(params);
556 
557  //Check status code
558  if(!error)
559  {
560  //In order to help clients configure themselves with the right URLs
561  //for each ACME operation, ACME servers provide a directory object
562  //(refer to RFC 8555, section 7.1.1)
563  context->state = ACME_CLIENT_STATE_DIRECTORY;
564  }
565  }
566  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
567  {
568  //If the directory object is no longer valid, the client must access
569  //the directory again by sending a GET request to the directory URL
570  error = acmeClientSendDirectoryRequest(context);
571 
572  //Check status code
573  if(!error)
574  {
575  //Update ACME client state
576  context->state = ACME_CLIENT_STATE_NEW_NONCE;
577  }
578  }
579  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
580  {
581  //Before sending a POST request to the server, an ACME client needs to
582  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
583  error = acmeClientSendNewNonceRequest(context);
584 
585  //Check status code
586  if(!error)
587  {
588  //Update ACME client state
589  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
590  }
591  }
592  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
593  {
594  //If a client wishes to find the URL for an existing account, then
595  //it should do so by sending a POST request to the newAccount URL
596  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
597  //section 7.3.1)
598  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
599 
600  //Check status code
601  if(!error)
602  {
603  //Update ACME client state
604  context->state = ACME_CLIENT_STATE_UPDATE_ACCOUNT;
605  }
606  }
607  else if(context->state == ACME_CLIENT_STATE_UPDATE_ACCOUNT)
608  {
609  //If the client wishes to update the account information, it sends a
610  //POST request with updated information to the account URL (refer to
611  //RFC 8555, section 7.3.1)
612  error = acmeClientSendUpdateAccountRequest(context, params);
613 
614  //Check status code
615  if(!error)
616  {
617  //Update ACME client state
618  context->state = ACME_CLIENT_STATE_CONNECTED;
619  break;
620  }
621  }
622  else
623  {
624  //Invalid state
625  error = ERROR_WRONG_STATE;
626  }
627  }
628 
629  //Unexpected HTTP response?
630  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
631  error == ERROR_RESPONSE_TOO_LARGE)
632  {
633  //Revert to default state
634  context->state = ACME_CLIENT_STATE_CONNECTED;
635  }
636 
637  //Return status code
638  return error;
639 }
640 
641 
642 /**
643  * @brief Account key rollover
644  * @param[in] context Pointer to the ACME client context
645  * @param[in] publicKey New public key (PEM format)
646  * @param[in] publicKeyLen Length of the new public key
647  * @param[in] privateKey New private key (PEM format)
648  * @param[in] privateKeyLen Length of the new private key
649  * @return Error code
650  **/
651 
653  const char_t *publicKey, size_t publicKeyLen,
654  const char_t *privateKey, size_t privateKeyLen)
655 {
656  error_t error;
657 
658  //Check parameters
659  if(context == NULL || publicKey == NULL || publicKeyLen == 0 ||
660  privateKey == NULL || privateKeyLen == 0)
661  {
663  }
664 
665  //Initialize variables
666  error = NO_ERROR;
667 
668  //Execute the sequence of HTTP requests
669  while(!error)
670  {
671  //Check ACME client state
672  if(context->state == ACME_CLIENT_STATE_CONNECTED)
673  {
674  //In order to help clients configure themselves with the right URLs for
675  //each ACME operation, ACME servers provide a directory object (refer
676  //to RFC 8555, section 7.1.1)
677  context->state = ACME_CLIENT_STATE_DIRECTORY;
678  }
679  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
680  {
681  //If the directory object is no longer valid, the client must access
682  //the directory again by sending a GET request to the directory URL
683  error = acmeClientSendDirectoryRequest(context);
684 
685  //Check status code
686  if(!error)
687  {
688  //Update ACME client state
689  context->state = ACME_CLIENT_STATE_NEW_NONCE;
690  }
691  }
692  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
693  {
694  //Before sending a POST request to the server, an ACME client needs to
695  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
696  error = acmeClientSendNewNonceRequest(context);
697 
698  //Check status code
699  if(!error)
700  {
701  //Update ACME client state
702  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
703  }
704  }
705  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
706  {
707  //If a client wishes to find the URL for an existing account, then
708  //it should do so by sending a POST request to the newAccount URL
709  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
710  //section 7.3.1)
711  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
712 
713  //Check status code
714  if(!error)
715  {
716  //Update ACME client state
717  context->state = ACME_CLIENT_STATE_CHANGE_KEY;
718  }
719  }
720  else if(context->state == ACME_CLIENT_STATE_CHANGE_KEY)
721  {
722  //A client can change the public key that is associated with an
723  //account, by sending a POST request to the server's keyChange
724  //URL (refer to RFC 8555, section 7.3.5)
725  error = acmeClientSendKeyChangeRequest(context, publicKey,
726  publicKeyLen, privateKey, privateKeyLen);
727 
728  //Check status code
729  if(!error)
730  {
731  //Unload the old account key
732  acmeClientUnloadKeyPair(&context->accountKey);
733 
734  //Load the new account key
735  error = acmeClientLoadKeyPair(&context->accountKey, publicKey,
736  publicKeyLen, privateKey, privateKeyLen);
737 
738  //Update ACME client state
739  context->state = ACME_CLIENT_STATE_CONNECTED;
740  break;
741  }
742  }
743  else
744  {
745  //Invalid state
746  error = ERROR_WRONG_STATE;
747  }
748  }
749 
750  //Unexpected HTTP response?
751  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
752  error == ERROR_RESPONSE_TOO_LARGE)
753  {
754  //Revert to default state
755  context->state = ACME_CLIENT_STATE_CONNECTED;
756  }
757 
758  //Return status code
759  return error;
760 }
761 
762 
763 /**
764  * @brief ACME account deactivation
765  * @param[in] context Pointer to the ACME client context
766  * @return Error code
767  **/
768 
770 {
771  error_t error;
772  AcmeAccountParams params;
773 
774  //Make sure the ACME client context is valid
775  if(context == NULL)
777 
778  //Initialize variables
779  error = NO_ERROR;
780 
781  //Execute the sequence of HTTP requests
782  while(!error)
783  {
784  //Check ACME client state
785  if(context->state == ACME_CLIENT_STATE_CONNECTED)
786  {
787  //In order to help clients configure themselves with the right URLs for
788  //each ACME operation, ACME servers provide a directory object (refer
789  //to RFC 8555, section 7.1.1)
790  context->state = ACME_CLIENT_STATE_DIRECTORY;
791  }
792  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
793  {
794  //If the directory object is no longer valid, the client must access
795  //the directory again by sending a GET request to the directory URL
796  error = acmeClientSendDirectoryRequest(context);
797 
798  //Check status code
799  if(!error)
800  {
801  //Update ACME client state
802  context->state = ACME_CLIENT_STATE_NEW_NONCE;
803  }
804  }
805  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
806  {
807  //Before sending a POST request to the server, an ACME client needs to
808  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
809  error = acmeClientSendNewNonceRequest(context);
810 
811  //Check status code
812  if(!error)
813  {
814  //Update ACME client state
815  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
816  }
817  }
818  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
819  {
820  //If a client wishes to find the URL for an existing account, then
821  //it should do so by sending a POST request to the newAccount URL
822  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
823  //section 7.3.1)
824  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
825 
826  //Check status code
827  if(!error)
828  {
829  //Update ACME client state
830  context->state = ACME_CLIENT_STATE_DEACTIVATE_ACCOUNT;
831  }
832  }
833  else if(context->state == ACME_CLIENT_STATE_DEACTIVATE_ACCOUNT)
834  {
835  //Initialize account parameters
836  osMemset(&params, 0, sizeof(AcmeAccountParams));
837 
838  //A client can deactivate an account by posting a signed update to the
839  //account URL with a status field of "deactivated" (refer to RFC 8555,
840  //section 7.3.1)
841  params.status = "deactivated";
842 
843  //Send the POST request to the account URL
844  error = acmeClientSendUpdateAccountRequest(context, &params);
845 
846  //Check status code
847  if(!error)
848  {
849  //Update ACME client state
850  context->state = ACME_CLIENT_STATE_CONNECTED;
851  break;
852  }
853  }
854  else
855  {
856  //Invalid state
857  error = ERROR_WRONG_STATE;
858  }
859  }
860 
861  //Unexpected HTTP response?
862  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
863  error == ERROR_RESPONSE_TOO_LARGE)
864  {
865  //Revert to default state
866  context->state = ACME_CLIENT_STATE_CONNECTED;
867  }
868 
869  //Return status code
870  return error;
871 }
872 
873 
874 /**
875  * @brief Begin the certificate issuance process
876  * @param[in] context Pointer to the ACME client context
877  * @param[in] params Certificate order information
878  * @return Error code
879  **/
880 
882  const AcmeOrderParams *params)
883 {
884  error_t error;
885 
886  //Check parameters
887  if(context == NULL || params == NULL)
889 
890  //Initialize variables
891  error = NO_ERROR;
892 
893  //Execute the sequence of HTTP requests
894  while(!error)
895  {
896  //Check ACME client state
897  if(context->state == ACME_CLIENT_STATE_CONNECTED)
898  {
899  //Check certificate order information
900  error = acmeClientCheckOrderParams(params);
901 
902  //Check status code
903  if(!error)
904  {
905  //Initialize order object
906  error = acmeClientInitOrder(context, params);
907  }
908 
909  //Check status code
910  if(!error)
911  {
912  //In order to help clients configure themselves with the right URLs
913  //for each ACME operation, ACME servers provide a directory object
914  //(refer to RFC 8555, section 7.1.1)
915  context->state = ACME_CLIENT_STATE_DIRECTORY;
916  }
917  }
918  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
919  {
920  //If the directory object is no longer valid, the client must access
921  //the directory again by sending a GET request to the directory URL
922  error = acmeClientSendDirectoryRequest(context);
923 
924  //Check status code
925  if(!error)
926  {
927  //Update ACME client state
928  context->state = ACME_CLIENT_STATE_NEW_NONCE;
929  }
930  }
931  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
932  {
933  //Before sending a POST request to the server, an ACME client needs to
934  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
935  error = acmeClientSendNewNonceRequest(context);
936 
937  //Check status code
938  if(!error)
939  {
940  //Update ACME client state
941  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
942  }
943  }
944  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
945  {
946  //If a client wishes to find the URL for an existing account, then
947  //it should do so by sending a POST request to the newAccount URL
948  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
949  //section 7.3.1)
950  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
951 
952  //Check status code
953  if(!error)
954  {
955  //Update ACME client state
956  context->state = ACME_CLIENT_STATE_NEW_ORDER;
957  }
958  }
959  else if(context->state == ACME_CLIENT_STATE_NEW_ORDER)
960  {
961  //The client begins the certificate issuance process by sending a
962  //POST request to the server's newOrder resource (refer to RFC 8555,
963  //section 7.4)
964  error = acmeClientSendNewOrderRequest(context, params);
965 
966  //Check status code
967  if(!error)
968  {
969  //Point to the first authorization
970  context->index = 0;
971  //Update ACME client state
972  context->state = ACME_CLIENT_STATE_AUTHORIZATION;
973  }
974  }
975  else if(context->state == ACME_CLIENT_STATE_AUTHORIZATION)
976  {
977  //Loop through the authorizations
978  if(context->index < context->numAuthorizations)
979  {
980  AcmeAuthorization *authorization;
981 
982  //Point to the current authorization
983  authorization = &context->authorizations[context->index];
984 
985  //When a client receives an order from the server in reply to a
986  //newOrder request, it downloads the authorization resources by
987  //sending POST-as-GET requests to the indicated URLs (refer to
988  //RFC 8555, section 7.5)
989  error = acmeClientSendAuthorizationRequest(context, authorization);
990 
991  //Check status code
992  if(!error)
993  {
994  //Point the next authorization
995  context->index++;
996  }
997  }
998  else
999  {
1000  //All the authorizations have been downloaded
1001  context->state = ACME_CLIENT_STATE_CONNECTED;
1002  break;
1003  }
1004  }
1005  else
1006  {
1007  //Invalid state
1008  error = ERROR_WRONG_STATE;
1009  }
1010  }
1011 
1012  //Unexpected HTTP response?
1013  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1014  error == ERROR_RESPONSE_TOO_LARGE)
1015  {
1016  //Revert to default state
1017  context->state = ACME_CLIENT_STATE_CONNECTED;
1018  }
1019 
1020  //Return status code
1021  return error;
1022 }
1023 
1024 
1025 /**
1026  * @brief Get the key authorization that matches a given token (HTTP challenge)
1027  * @param[in] context Pointer to the ACME client context
1028  * @param[in] token NULL-terminated string that contains the token
1029  * @return The function returns a NULL-terminated string that contains the key
1030  * authorization if the token is valid. Else, the NULL pointer is returned
1031  **/
1032 
1034  const char_t *token)
1035 {
1036  const char_t *keyAuth;
1037 
1038  //Default value
1039  keyAuth = NULL;
1040 
1041 #if (ACME_CLIENT_HTTP_CHALLENGE_SUPPORT == ENABLED)
1042  //Check parameters
1043  if(context != NULL && token != NULL)
1044  {
1045  uint_t i;
1046 
1047  //Loop through the challenges
1048  for(i = 0; i < context->numChallenges; i++)
1049  {
1050  //Check the status of the challenge
1051  if(context->challenges[i].status == ACME_CHALLENGE_STATUS_PENDING ||
1052  context->challenges[i].status == ACME_CHALLENGE_STATUS_PROCESSING)
1053  {
1054  //HTTP validation method?
1055  if(context->challenges[i].type == ACME_CHALLENGE_TYPE_HTTP_01)
1056  {
1057  //Compare token values
1058  if(osStrcmp(context->challenges[i].token, token) == 0)
1059  {
1060  //Point to the key authorization
1061  keyAuth = context->challenges[i].keyAuth;
1062  break;
1063  }
1064  }
1065  }
1066  }
1067  }
1068 #endif
1069 
1070  //Return the ASCII representation of the key authorization
1071  return keyAuth;
1072 }
1073 
1074 
1075 /**
1076  * @brief Get the key authorization digest that matches a given identifier (DNS challenge)
1077  * @param[in] context Pointer to the ACME client context
1078  * @param[in] identifier NULL-terminated string that contains the domain name
1079  * @return The function returns a NULL-terminated string that contains the
1080  * Base64url-encoded digest of the key authorization if the identifier is
1081  * valid. Else, the NULL pointer is returned
1082  **/
1083 
1085  const char_t *identifier)
1086 {
1087  const char_t *keyAuth;
1088 
1089  //Default value
1090  keyAuth = NULL;
1091 
1092 #if (ACME_CLIENT_DNS_CHALLENGE_SUPPORT == ENABLED)
1093  //Check parameters
1094  if(context != NULL && identifier != NULL)
1095  {
1096  uint_t i;
1097 
1098  //Loop through the challenges
1099  for(i = 0; i < context->numChallenges; i++)
1100  {
1101  //Check the status of the challenge
1102  if(context->challenges[i].status == ACME_CHALLENGE_STATUS_PENDING ||
1103  context->challenges[i].status == ACME_CHALLENGE_STATUS_PROCESSING)
1104  {
1105  //DNS validation method?
1106  if(context->challenges[i].type == ACME_CHALLENGE_TYPE_DNS_01)
1107  {
1108  //Any identifier of type "dns" may have a wildcard domain name as
1109  //its value
1110  if(context->challenges[i].wildcard)
1111  {
1112  //A wildcard domain name consists of a single asterisk character
1113  //followed by a single full stop character ("*.") followed by a
1114  //domain name
1115  if(osStrncmp(identifier, "*.", 2) == 0 &&
1116  osStrcmp(context->challenges[i].identifier, identifier + 2) == 0)
1117  {
1118  //Point to the key authorization digest
1119  keyAuth = context->challenges[i].keyAuth;
1120  break;
1121  }
1122  }
1123  else
1124  {
1125  //Compare identifier values
1126  if(osStrcmp(context->challenges[i].identifier, identifier) == 0)
1127  {
1128  //Point to the key authorization digest
1129  keyAuth = context->challenges[i].keyAuth;
1130  break;
1131  }
1132  }
1133  }
1134  }
1135  }
1136  }
1137 #endif
1138 
1139  //Return the Base64url representation of the key authorization digest
1140  return keyAuth;
1141 }
1142 
1143 
1144 /**
1145  * @brief Get the self-certificate that matches a given identifier (TLS-ALPN challenge)
1146  * @param[in] context Pointer to the ACME client context
1147  * @param[in] identifier NULL-terminated string that contains the domain name
1148  * @return The function returns a NULL-terminated string that contains the
1149  * TLS-ALPN certificate if the identifier is valid. Else, the NULL pointer
1150  * is returned
1151  **/
1152 
1154  const char_t *identifier)
1155 {
1156  const char_t *cert;
1157 
1158  //Default value
1159  cert = NULL;
1160 
1161 #if (ACME_CLIENT_TLS_ALPN_CHALLENGE_SUPPORT == ENABLED)
1162  //Check parameters
1163  if(context != NULL && identifier != NULL)
1164  {
1165  uint_t i;
1166 
1167  //Loop through the challenges
1168  for(i = 0; i < context->numChallenges; i++)
1169  {
1170  //Check the status of the challenge
1171  if(context->challenges[i].status == ACME_CHALLENGE_STATUS_PENDING ||
1172  context->challenges[i].status == ACME_CHALLENGE_STATUS_PROCESSING)
1173  {
1174  //TLS with ALPN validation method?
1175  if(context->challenges[i].type == ACME_CHALLENGE_TYPE_TLS_ALPN_01)
1176  {
1177  //Compare identifier values
1178  if(osStrcmp(context->challenges[i].identifier, identifier) == 0)
1179  {
1180  //Point to the self-signed certificate
1181  cert = context->challenges[i].cert;
1182  break;
1183  }
1184  }
1185  }
1186  }
1187  }
1188 #endif
1189 
1190  //Return the TLS-ALPN certificate
1191  return cert;
1192 }
1193 
1194 
1195 /**
1196  * @brief Poll for order status
1197  * @param[in] context Pointer to the ACME client context
1198  * @param[out] orderStatus Order status
1199  * @return Error code
1200  **/
1201 
1203  AcmeOrderStatus *orderStatus)
1204 {
1205  error_t error;
1206 
1207  //Check parameters
1208  if(context == NULL || orderStatus == NULL)
1209  return ERROR_INVALID_PARAMETER;
1210 
1211  //Initialize variables
1212  error = NO_ERROR;
1213 
1214  //Execute the sequence of HTTP requests
1215  while(!error)
1216  {
1217  //Check ACME client state
1218  if(context->state == ACME_CLIENT_STATE_CONNECTED)
1219  {
1220  //Check the order of the order
1221  if(context->order.status == ACME_ORDER_STATUS_PENDING ||
1222  context->order.status == ACME_ORDER_STATUS_READY ||
1223  context->order.status == ACME_ORDER_STATUS_PROCESSING)
1224  {
1225  //In order to help clients configure themselves with the right URLs
1226  //for each ACME operation, ACME servers provide a directory object
1227  //(refer to RFC 8555, section 7.1.1)
1228  context->state = ACME_CLIENT_STATE_DIRECTORY;
1229  }
1230  else if(context->order.status == ACME_ORDER_STATUS_VALID ||
1231  context->order.status == ACME_ORDER_STATUS_INVALID)
1232  {
1233  //Exit immediately
1234  break;
1235  }
1236  else
1237  {
1238  //Report an error
1239  error = ERROR_WRONG_STATE;
1240  }
1241  }
1242  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
1243  {
1244  //If the directory object is no longer valid, the client must access
1245  //the directory again by sending a GET request to the directory URL
1246  error = acmeClientSendDirectoryRequest(context);
1247 
1248  //Check status code
1249  if(!error)
1250  {
1251  //Update ACME client state
1252  context->state = ACME_CLIENT_STATE_NEW_NONCE;
1253  }
1254  }
1255  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
1256  {
1257  //Before sending a POST request to the server, an ACME client needs to
1258  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
1259  error = acmeClientSendNewNonceRequest(context);
1260 
1261  //Check status code
1262  if(!error)
1263  {
1264  //Update ACME client state
1265  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
1266  }
1267  }
1268  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
1269  {
1270  //If a client wishes to find the URL for an existing account, then
1271  //it should do so by sending a POST request to the newAccount URL
1272  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
1273  //section 7.3.1)
1274  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
1275 
1276  //Check status code
1277  if(!error)
1278  {
1279  //Clients should check the status of the order to determine whether
1280  //they need to take any action (refer to RFC 8555, section 7.1.3)
1281  if(context->order.status == ACME_ORDER_STATUS_PENDING)
1282  {
1283  //Point to the first challenge
1284  context->index = 0;
1285 
1286  //The client indicate to the server that it is ready for the
1287  //challenge validation
1288  context->state = ACME_CLIENT_STATE_CHALLENGE_READY;
1289  }
1290  else if(context->order.status == ACME_ORDER_STATUS_READY)
1291  {
1292  //All the authorizations listed in the order object are in the
1293  //"valid" state
1294  context->state = ACME_CLIENT_STATE_FINALIZE;
1295  }
1296  else if(context->order.status == ACME_ORDER_STATUS_PROCESSING)
1297  {
1298  //The client has already submitted a request to the order's
1299  //"finalize" URL
1300  context->state = ACME_CLIENT_STATE_POLL_STATUS_2;
1301  }
1302  else
1303  {
1304  //Report an error
1305  error = ERROR_WRONG_STATE;
1306  }
1307  }
1308  }
1309  else if(context->state == ACME_CLIENT_STATE_CHALLENGE_READY)
1310  {
1311  //Loop through the authorizations
1312  if(context->index < context->numChallenges)
1313  {
1314  AcmeChallenge *challenge;
1315 
1316  //Point to the current challenge
1317  challenge = &context->challenges[context->index];
1318 
1319  //Check the status of the challenge
1320  if(challenge->status == ACME_CHALLENGE_STATUS_PENDING)
1321  {
1322  //The client indicates to the server that it is ready for the
1323  //challenge validation by sending an empty JSON body carried in
1324  //a POST request to the challenge URL (refer to RFC 8555,
1325  //section 7.5.1)
1326  error = acmeClientSendChallengeReadyRequest(context, challenge);
1327 
1328  //Check status code
1329  if(!error)
1330  {
1331  //The challenge transitions to the "processing" state when
1332  //the client responds to the challenge
1334  }
1335  }
1336 
1337  //Check status code
1338  if(!error)
1339  {
1340  //Point the next challenge
1341  context->index++;
1342  }
1343  }
1344  else
1345  {
1346  //Update ACME client state
1347  context->state = ACME_CLIENT_STATE_POLL_STATUS_1;
1348  }
1349  }
1350  else if(context->state == ACME_CLIENT_STATE_POLL_STATUS_1)
1351  {
1352  //The client should then send a POST-as-GET request to the order
1353  //resource to obtain its current state refer to RFC 8555, section 7.4)
1354  error = acmeClientSendOrderStatusRequest(context);
1355 
1356  //Check status code
1357  if(!error)
1358  {
1359  //Check the status of the order
1360  if(context->order.status == ACME_ORDER_STATUS_PENDING ||
1361  context->order.status == ACME_ORDER_STATUS_INVALID)
1362  {
1363  //Update ACME client state
1364  context->state = ACME_CLIENT_STATE_CONNECTED;
1365  break;
1366  }
1367  else if(context->order.status == ACME_ORDER_STATUS_READY)
1368  {
1369  //Once all of the authorizations listed in the order object are
1370  //in the "valid" state, the order transitions to the "ready" state
1371  context->state = ACME_CLIENT_STATE_FINALIZE;
1372  }
1373  else
1374  {
1375  //Report an error
1376  error = ERROR_WRONG_STATE;
1377  break;
1378  }
1379  }
1380  }
1381  else if(context->state == ACME_CLIENT_STATE_FINALIZE)
1382  {
1383  //Once the client believes it has fulfilled the server's requirements,
1384  //it should send a POST request to the order resource's finalize URL.
1385  //The POST body MUST include a CSR (refer to RFC 8555, section 7.4)
1386  error = acmeClientSendFinalizeOrderRequest(context);
1387 
1388  //Check status code
1389  if(!error)
1390  {
1391  //The order moves to the "processing" state after the client submits
1392  //a request to the order's finalize URL
1393  context->order.status = ACME_ORDER_STATUS_PROCESSING;
1394 
1395  //Update ACME client state
1396  context->state = ACME_CLIENT_STATE_POLL_STATUS_2;
1397  }
1398  }
1399  else if(context->state == ACME_CLIENT_STATE_POLL_STATUS_2)
1400  {
1401  //The client should then send a POST-as-GET request to the order
1402  //resource to obtain its current state refer to RFC 8555, section 7.4)
1403  error = acmeClientSendOrderStatusRequest(context);
1404 
1405  //Check status code
1406  if(!error)
1407  {
1408  //Check the status of the order
1409  if(context->order.status != ACME_ORDER_STATUS_PROCESSING &&
1410  context->order.status != ACME_ORDER_STATUS_VALID &&
1411  context->order.status != ACME_ORDER_STATUS_INVALID)
1412  {
1413  //Report an error
1414  error = ERROR_WRONG_STATE;
1415  }
1416 
1417  //Update ACME client state
1418  context->state = ACME_CLIENT_STATE_CONNECTED;
1419  break;
1420  }
1421  }
1422  else
1423  {
1424  //Invalid state
1425  error = ERROR_WRONG_STATE;
1426  }
1427  }
1428 
1429  //Always return the actual status of the order
1430  *orderStatus = context->order.status;
1431 
1432  //Unexpected HTTP response?
1433  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1434  error == ERROR_RESPONSE_TOO_LARGE)
1435  {
1436  //Revert to default state
1437  context->state = ACME_CLIENT_STATE_CONNECTED;
1438  }
1439 
1440  //Return status code
1441  return error;
1442 }
1443 
1444 
1445 /**
1446  * @brief Download the certificate
1447  * @param[in] context Pointer to the ACME client context
1448  * @param[out] buffer Pointer to the buffer where to store the certificate chain
1449  * @param[in] size Size of the buffer, in bytes
1450  * @param[out] length Actual length of the certificate chain, in bytes
1451  * @return Error code
1452  **/
1453 
1455  char_t *buffer, size_t size, size_t *length)
1456 {
1457  error_t error;
1458 
1459  //Check parameters
1460  if(context == NULL || buffer == NULL || length == NULL)
1461  return ERROR_INVALID_PARAMETER;
1462 
1463  //Initialize variables
1464  error = NO_ERROR;
1465 
1466  //Execute the sequence of HTTP requests
1467  while(!error)
1468  {
1469  //Check ACME client state
1470  if(context->state == ACME_CLIENT_STATE_CONNECTED)
1471  {
1472  //Make sure the certificate has been issued by the ACME server
1473  if(context->order.status == ACME_ORDER_STATUS_VALID)
1474  {
1475  //In order to help clients configure themselves with the right URLs
1476  //for each ACME operation, ACME servers provide a directory object
1477  //(refer to RFC 8555, section 7.1.1)
1478  context->state = ACME_CLIENT_STATE_DIRECTORY;
1479  }
1480  else
1481  {
1482  //Report an error
1483  error = ERROR_WRONG_STATE;
1484  }
1485  }
1486  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
1487  {
1488  //If the directory object is no longer valid, the client must access
1489  //the directory again by sending a GET request to the directory URL
1490  error = acmeClientSendDirectoryRequest(context);
1491 
1492  //Check status code
1493  if(!error)
1494  {
1495  //Update ACME client state
1496  context->state = ACME_CLIENT_STATE_NEW_NONCE;
1497  }
1498  }
1499  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
1500  {
1501  //Before sending a POST request to the server, an ACME client needs to
1502  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
1503  error = acmeClientSendNewNonceRequest(context);
1504 
1505  //Check status code
1506  if(!error)
1507  {
1508  //Update ACME client state
1509  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
1510  }
1511  }
1512  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
1513  {
1514  //If a client wishes to find the URL for an existing account, then
1515  //it should do so by sending a POST request to the newAccount URL
1516  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
1517  //section 7.3.1)
1518  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
1519 
1520  //Check status code
1521  if(!error)
1522  {
1523  //Update ACME client state
1524  context->state = ACME_CLIENT_STATE_DOWNLOAD_CERT;
1525  }
1526  }
1527  else if(context->state == ACME_CLIENT_STATE_DOWNLOAD_CERT)
1528  {
1529  //To download the issued certificate, the client simply sends a
1530  //POST-as-GET request to the certificate URL (refer to RFC 8555,
1531  //section 7.4.2)
1532  error = acmeClientSendDownloadCertRequest(context, buffer, size,
1533  length);
1534 
1535  //Check status code
1536  if(!error)
1537  {
1538  //Update ACME client state
1539  context->state = ACME_CLIENT_STATE_CONNECTED;
1540  break;
1541  }
1542  }
1543  else
1544  {
1545  //Invalid state
1546  error = ERROR_WRONG_STATE;
1547  }
1548  }
1549 
1550  //Unexpected HTTP response?
1551  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1552  error == ERROR_RESPONSE_TOO_LARGE)
1553  {
1554  //Revert to default state
1555  context->state = ACME_CLIENT_STATE_CONNECTED;
1556  }
1557 
1558  //Return status code
1559  return error;
1560 }
1561 
1562 
1563 /**
1564  * @brief Certificate revocation
1565  * @param[in] context Pointer to the ACME client context
1566  * @param[in] cert Certificate to be revoked (PEM format)
1567  * @param[in] certLen Length of the certificate, in bytes
1568  * @param[in] privateKey Reserved parameter (must be NULL)
1569  * @param[in] privateKeyLen Reserved parameter (must be 0)
1570  * @param[in] reason Revocation reason code
1571  * @return Error code
1572  **/
1573 
1575  const char_t *cert, size_t certLen, const char_t *privateKey,
1576  size_t privateKeyLen, AcmeReasonCode reason)
1577 {
1578  error_t error;
1579 
1580  //Check parameters
1581  if(context == NULL || cert == NULL || certLen == 0)
1582  return ERROR_INVALID_PARAMETER;
1583 
1584  //Initialize variables
1585  error = NO_ERROR;
1586 
1587  //Execute the sequence of HTTP requests
1588  while(!error)
1589  {
1590  //Check ACME client state
1591  if(context->state == ACME_CLIENT_STATE_CONNECTED)
1592  {
1593  //In order to help clients configure themselves with the right URLs for
1594  //each ACME operation, ACME servers provide a directory object (refer
1595  //to RFC 8555, section 7.1.1)
1596  context->state = ACME_CLIENT_STATE_DIRECTORY;
1597  }
1598  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
1599  {
1600  //If the directory object is no longer valid, the client must access
1601  //the directory again by sending a GET request to the directory URL
1602  error = acmeClientSendDirectoryRequest(context);
1603 
1604  //Check status code
1605  if(!error)
1606  {
1607  //Update ACME client state
1608  context->state = ACME_CLIENT_STATE_NEW_NONCE;
1609  }
1610  }
1611  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
1612  {
1613  //Before sending a POST request to the server, an ACME client needs to
1614  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
1615  error = acmeClientSendNewNonceRequest(context);
1616 
1617  //Check status code
1618  if(!error)
1619  {
1620  //Update ACME client state
1621  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
1622  }
1623  }
1624  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
1625  {
1626  //If a client wishes to find the URL for an existing account, then
1627  //it should do so by sending a POST request to the newAccount URL
1628  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
1629  //section 7.3.1)
1630  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
1631 
1632  //Check status code
1633  if(!error)
1634  {
1635  //Update ACME client state
1636  context->state = ACME_CLIENT_STATE_REVOKE_CERT;
1637  }
1638  }
1639  else if(context->state == ACME_CLIENT_STATE_REVOKE_CERT)
1640  {
1641  //To request that a certificate be revoked, the client sends a POST
1642  //request to the ACME server's revokeCert URL (refer to RFC 8555,
1643  //section 7.6)
1644  error = acmeClientSendRevokeCertRequest(context, cert, certLen, reason);
1645 
1646  //Check status code
1647  if(!error)
1648  {
1649  //Update ACME client state
1650  context->state = ACME_CLIENT_STATE_CONNECTED;
1651  break;
1652  }
1653  }
1654  else
1655  {
1656  //Invalid state
1657  error = ERROR_WRONG_STATE;
1658  }
1659  }
1660 
1661  //Unexpected HTTP response?
1662  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1663  error == ERROR_RESPONSE_TOO_LARGE)
1664  {
1665  //Revert to default state
1666  context->state = ACME_CLIENT_STATE_CONNECTED;
1667  }
1668 
1669  //Return status code
1670  return error;
1671 }
1672 
1673 
1674 /**
1675  * @brief Gracefully disconnect from the ACME server
1676  * @param[in] context Pointer to the ACME client context
1677  * @return Error code
1678  **/
1679 
1681 {
1682  error_t error;
1683 
1684  //Make sure the ACME client context is valid
1685  if(context == NULL)
1686  return ERROR_INVALID_PARAMETER;
1687 
1688  //Initialize status code
1689  error = NO_ERROR;
1690 
1691  //Gracefully disconnect from the ACME server
1692  while(!error)
1693  {
1694  //Check ACME client state
1695  if(context->state == ACME_CLIENT_STATE_CONNECTED)
1696  {
1697  //Gracefully shutdown HTTPS connection
1698  context->state = ACME_CLIENT_STATE_DISCONNECTING;
1699  }
1700  else if(context->state == ACME_CLIENT_STATE_DISCONNECTING)
1701  {
1702  //Gracefully shutdown HTTPS connection
1703  error = httpClientDisconnect(&context->httpClientContext);
1704 
1705  //Check status code
1706  if(error == NO_ERROR)
1707  {
1708  //Close HTTPS connection
1709  httpClientClose(&context->httpClientContext);
1710  //Update ACME client state
1711  context->state = ACME_CLIENT_STATE_DISCONNECTED;
1712  }
1713  }
1714  else if(context->state == ACME_CLIENT_STATE_DISCONNECTED)
1715  {
1716  //The client is disconnected from the ACME server
1717  break;
1718  }
1719  else
1720  {
1721  //Invalid state
1722  error = ERROR_WRONG_STATE;
1723  }
1724  }
1725 
1726  //Failed to gracefully disconnect from the ACME server?
1727  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
1728  {
1729  //Close HTTPS connection
1730  httpClientClose(&context->httpClientContext);
1731  //Update ACME client state
1732  context->state = ACME_CLIENT_STATE_DISCONNECTED;
1733  }
1734 
1735  //Return status code
1736  return error;
1737 }
1738 
1739 
1740 /**
1741  * @brief Close the connection with the ACME server
1742  * @param[in] context Pointer to the ACME client context
1743  * @return Error code
1744  **/
1745 
1747 {
1748  //Make sure the ACME client context is valid
1749  if(context == NULL)
1750  return ERROR_INVALID_PARAMETER;
1751 
1752  //Close HTTPS connection
1753  httpClientClose(&context->httpClientContext);
1754  //Update ACME client state
1755  context->state = ACME_CLIENT_STATE_DISCONNECTED;
1756 
1757  //Successful processing
1758  return NO_ERROR;
1759 }
1760 
1761 
1762 /**
1763  * @brief Release ACME client context
1764  * @param[in] context Pointer to the ACME client context
1765  **/
1766 
1768 {
1769  //Make sure the ACME client context is valid
1770  if(context != NULL)
1771  {
1772  //Release HTTP client context
1773  httpClientDeinit(&context->httpClientContext);
1774 
1775  //Release keys
1776  acmeClientUnloadKeyPair(&context->accountKey);
1777  acmeClientUnloadKeyPair(&context->certKey);
1778 
1779  //Clear ACME client context
1780  osMemset(context, 0, sizeof(AcmeClientContext));
1781  }
1782 }
1783 
1784 #endif
error_t acmeClientSendNewOrderRequest(AcmeClientContext *context, const AcmeOrderParams *params)
Send HTTP request (newOrder URL)
error_t acmeClientSendDownloadCertRequest(AcmeClientContext *context, char_t *buffer, size_t size, size_t *length)
Send HTTP request (certificate URL)
error_t acmeClientSetHost(AcmeClientContext *context, const char_t *host)
Set the domain name of the ACME server.
Definition: acme_client.c:198
error_t acmeClientRevokeCertificate(AcmeClientContext *context, const char_t *cert, size_t certLen, const char_t *privateKey, size_t privateKeyLen, AcmeReasonCode reason)
Certificate revocation.
Definition: acme_client.c:1574
error_t httpClientDisconnect(HttpClientContext *context)
Gracefully disconnect from the HTTP server.
Definition: http_client.c:2149
@ ACME_ORDER_STATUS_VALID
Definition: acme_client.h:325
error_t acmeClientInitOrder(AcmeClientContext *context, const AcmeOrderParams *params)
Initialize order object.
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
error_t acmeClientRegisterTlsInitCallback(AcmeClientContext *context, AcmeClientTlsInitCallback callback)
Register TLS initialization callback function.
Definition: acme_client.c:108
@ ACME_CHALLENGE_STATUS_PENDING
Definition: acme_client.h:353
IP network address.
Definition: ip.h:90
#define PrngAlgo
Definition: crypto.h:938
void acmeClientDeinit(AcmeClientContext *context)
Release ACME client context.
Definition: acme_client.c:1767
#define TRUE
Definition: os_port.h:50
Helper functions for ACME client.
error_t acmeClientSendNewNonceRequest(AcmeClientContext *context)
Send HTTP request (newNonce URL)
AcmeReasonCode
Revocation reason codes.
Definition: acme_client.h:378
error_t acmeClientBindToInterface(AcmeClientContext *context, NetInterface *interface)
Bind the ACME client to a particular network interface.
Definition: acme_client.c:249
@ ACME_CLIENT_STATE_CONNECTING
Definition: acme_client.h:263
Challenge object.
Definition: acme_client.h:547
const char_t * acmeClientGetHttpKeyAuthorization(AcmeClientContext *context, const char_t *token)
Get the key authorization that matches a given token (HTTP challenge)
Definition: acme_client.c:1033
const char_t * publicKey
Account public key.
Definition: acme_client.h:445
Anti-replay nonce management.
Certificate order parameters.
Definition: acme_client.h:469
Account object management.
error_t acmeClientSendDirectoryRequest(AcmeClientContext *context)
Send HTTP request (directory URL)
@ ACME_CHALLENGE_TYPE_DNS_01
Definition: acme_client.h:368
#define osStrcmp(s1, s2)
Definition: os_port.h:171
error_t acmeClientSetDirectoryUri(AcmeClientContext *context, const char_t *directoryUri)
Set the URI of the directory object.
Definition: acme_client.c:223
#define osStrlen(s)
Definition: os_port.h:165
Directory object.
Definition: acme_client.h:486
error_t acmeClientInit(AcmeClientContext *context)
Initialize ACME client context.
Definition: acme_client.c:69
#define ACME_CLIENT_MAX_NAME_LEN
Definition: acme_client.h:173
@ ACME_CLIENT_STATE_AUTHORIZATION
Definition: acme_client.h:272
Challenge object management.
@ ACME_CLIENT_STATE_CONNECTED
Definition: acme_client.h:264
Account object.
Definition: acme_client.h:500
error_t httpClientSetVersion(HttpClientContext *context, HttpVersion version)
Set the HTTP protocol version to be used.
Definition: http_client.c:162
#define ACME_CLIENT_MAX_URI_LEN
Definition: acme_client.h:180
error_t acmeClientCreateAccount(AcmeClientContext *context, const AcmeAccountParams *params)
Account creation.
Definition: acme_client.c:424
ACME account creation parameters.
Definition: acme_client.h:441
@ ERROR_WRONG_STATE
Definition: error.h:209
@ ACME_CLIENT_STATE_NEW_ACCOUNT
Definition: acme_client.h:267
void httpClientDeinit(HttpClientContext *context)
Release HTTP client context.
Definition: http_client.c:2245
#define ACME_CLIENT_DEFAULT_TIMEOUT
Definition: acme_client.h:145
error_t acmeClientLoadKeyPair(AcmeKeyPair *keyPair, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen)
Load public/private key pair.
@ ACME_CHALLENGE_STATUS_PROCESSING
Definition: acme_client.h:354
@ ACME_CLIENT_STATE_NEW_ORDER
Definition: acme_client.h:271
#define FALSE
Definition: os_port.h:46
@ ACME_CLIENT_STATE_FINALIZE
Definition: acme_client.h:275
#define ACME_CLIENT_MAX_NONCE_LEN
Definition: acme_client.h:201
Authorization object management.
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
@ ERROR_UNEXPECTED_STATUS
Definition: error.h:283
AcmeOrderStatus
Order status.
Definition: acme_client.h:320
error_t
Error codes.
Definition: error.h:43
Certificate management.
error_t acmeClientSendFinalizeOrderRequest(AcmeClientContext *context)
Send HTTP request (order's finalize URL)
AcmeChallengeStatus status
Status of the challenge.
Definition: acme_client.h:549
Order object management.
const char_t * acmeClientGetTlsAlpnCertificate(AcmeClientContext *context, const char_t *identifier)
Get the self-certificate that matches a given identifier (TLS-ALPN challenge)
Definition: acme_client.c:1153
@ ERROR_RESPONSE_TOO_LARGE
Definition: error.h:284
@ ACME_CHALLENGE_TYPE_HTTP_01
Definition: acme_client.h:367
error_t acmeClientSetTimeout(AcmeClientContext *context, systime_t timeout)
Set communication timeout.
Definition: acme_client.c:177
error_t httpClientRegisterTlsInitCallback(HttpClientContext *context, HttpClientTlsInitCallback callback)
Register TLS initialization callback function.
Definition: http_client.c:111
error_t acmeClientUpdateAccount(AcmeClientContext *context, const AcmeAccountParams *params)
Account information update.
Definition: acme_client.c:536
error_t acmeClientClose(AcmeClientContext *context)
Close the connection with the ACME server.
Definition: acme_client.c:1746
#define NetInterface
Definition: net.h:36
Directory object management.
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:2224
@ ERROR_INVALID_LENGTH
Definition: error.h:111
error_t acmeClientChangeAccountKey(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen)
Account key rollover.
Definition: acme_client.c:652
error_t acmeClientSendAuthorizationRequest(AcmeClientContext *context, AcmeAuthorization *authorization)
Send HTTP request (authorization URL)
@ ACME_REQ_STATE_INIT
Definition: acme_client.h:289
@ ACME_CLIENT_STATE_CHALLENGE_READY
Definition: acme_client.h:273
uint8_t length
Definition: tcp.h:368
error_t httpClientInit(HttpClientContext *context)
Initialize HTTP client context.
Definition: http_client.c:66
error_t(* AcmeClientCsrCallback)(AcmeClientContext *context, uint8_t *buffer, size_t size, size_t *length)
CSR generation callback function.
Definition: acme_client.h:404
error_t acmeClientSetPrng(AcmeClientContext *context, const PrngAlgo *prngAlgo, void *prngContext)
Set the pseudo-random number generator to be used.
Definition: acme_client.c:153
@ ACME_CLIENT_STATE_DISCONNECTED
Definition: acme_client.h:262
error_t acmeClientConnect(AcmeClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish a connection with the specified ACME server.
Definition: acme_client.c:272
@ ACME_CLIENT_STATE_UPDATE_ACCOUNT
Definition: acme_client.h:268
Authorization object.
Definition: acme_client.h:535
error_t acmeClientSendKeyChangeRequest(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen)
Send HTTP request (keyChange URL)
const char_t * status
Status of the account.
Definition: acme_client.h:449
@ ACME_CLIENT_STATE_CHANGE_KEY
Definition: acme_client.h:269
@ ACME_CLIENT_STATE_REVOKE_CERT
Definition: acme_client.h:278
@ ACME_ORDER_STATUS_PROCESSING
Definition: acme_client.h:324
uint32_t systime_t
System time.
error_t acmeClientSetAccountKey(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen)
Load account key pair.
Definition: acme_client.c:397
char char_t
Definition: compiler_port.h:48
#define AcmeClientContext
Definition: acme_client.h:248
error_t httpClientConnect(HttpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish a connection with the specified HTTP server.
Definition: http_client.c:269
error_t acmeClientSendUpdateAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params)
Send HTTP request (account URL)
size_t privateKeyLen
Length of the account private key, in bytes.
Definition: acme_client.h:448
@ HTTP_VERSION_1_1
Definition: http_common.h:63
@ ACME_CHALLENGE_TYPE_TLS_ALPN_01
Definition: acme_client.h:369
error_t acmeClientSendRevokeCertRequest(AcmeClientContext *context, const char_t *cert, size_t certLen, AcmeReasonCode reason)
Send HTTP request (revokeCert URL)
size_t publicKeyLen
Length of the account public key, in bytes.
Definition: acme_client.h:446
error_t acmeClientSendNewAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params, bool_t onlyReturnExisting)
Send HTTP request (newAccount URL)
@ ACME_CLIENT_STATE_DISCONNECTING
Definition: acme_client.h:279
@ ACME_ORDER_STATUS_READY
Definition: acme_client.h:323
error_t acmeClientSendOrderStatusRequest(AcmeClientContext *context)
Send HTTP request (order URL)
error_t acmeClientDownloadCertificate(AcmeClientContext *context, char_t *buffer, size_t size, size_t *length)
Download the certificate.
Definition: acme_client.c:1454
uint8_t identifier[]
const char_t * privateKey
Account private key.
Definition: acme_client.h:447
error_t acmeClientCreateOrder(AcmeClientContext *context, const AcmeOrderParams *params)
Begin the certificate issuance process.
Definition: acme_client.c:881
#define osStrncmp(s1, s2, length)
Definition: os_port.h:177
@ ACME_CLIENT_STATE_POLL_STATUS_1
Definition: acme_client.h:274
@ ACME_ORDER_STATUS_PENDING
Definition: acme_client.h:322
@ ACME_CLIENT_STATE_DIRECTORY
Definition: acme_client.h:265
error_t(* AcmeClientTlsInitCallback)(HttpClientContext *context, TlsContext *tlsContext)
TLS initialization callback function.
Definition: acme_client.h:396
@ ACME_ORDER_STATUS_INVALID
Definition: acme_client.h:326
@ ACME_CLIENT_STATE_NEW_NONCE
Definition: acme_client.h:266
void acmeClientUnloadKeyPair(AcmeKeyPair *keyPair)
Unload public/private key pair.
error_t acmeClientRegisterCsrCallback(AcmeClientContext *context, AcmeClientCsrCallback callback)
Register CSR generation callback function.
Definition: acme_client.c:130
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:135
error_t acmeClientCheckOrderParams(const AcmeOrderParams *params)
Check certificate order information.
error_t acmeClientPollOrderStatus(AcmeClientContext *context, AcmeOrderStatus *orderStatus)
Poll for order status.
Definition: acme_client.c:1202
#define osStrcpy(s1, s2)
Definition: os_port.h:207
const char_t * acmeClientGetDnsKeyAuthorization(AcmeClientContext *context, const char_t *identifier)
Get the key authorization digest that matches a given identifier (DNS challenge)
Definition: acme_client.c:1084
@ ERROR_INVALID_RESPONSE
Definition: error.h:71
ACME client (Automatic Certificate Management Environment)
error_t acmeClientSendChallengeReadyRequest(AcmeClientContext *context, AcmeChallenge *challenge)
Send HTTP request (challenge URL)
error_t acmeClientDeactivateAccount(AcmeClientContext *context)
ACME account deactivation.
Definition: acme_client.c:769
error_t acmeClientCheckAccountParams(const AcmeAccountParams *params)
Check account information.
@ ACME_CLIENT_STATE_DOWNLOAD_CERT
Definition: acme_client.h:277
@ ACME_CLIENT_STATE_POLL_STATUS_2
Definition: acme_client.h:276
error_t acmeClientDisconnect(AcmeClientContext *context)
Gracefully disconnect from the ACME server.
Definition: acme_client.c:1680
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
uint8_t token[]
Definition: coap_common.h:181
@ ACME_CLIENT_STATE_DEACTIVATE_ACCOUNT
Definition: acme_client.h:270