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-2025 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.5.0
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  * @param[in] password NULL-terminated string containing the password. This
395  * parameter is required if the private key is encrypted
396  * @return Error code
397  **/
398 
400  const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey,
401  size_t privateKeyLen, const char_t *password)
402 {
403  //Check parameters
404  if(context == NULL || publicKey == NULL || publicKeyLen == 0 ||
405  privateKey == NULL || privateKeyLen == 0)
406  {
408  }
409 
410  //Release the current key pair, if any
411  acmeClientUnloadKeyPair(&context->accountKey);
412 
413  //The public and private keys are encoded in PEM format
414  return acmeClientLoadKeyPair(&context->accountKey, publicKey, publicKeyLen,
415  privateKey, privateKeyLen, password);
416 }
417 
418 
419 /**
420  * @brief Account creation
421  * @param[in] context Pointer to the ACME client context
422  * @param[in] params Account information
423  * @return Error code
424  **/
425 
427  const AcmeAccountParams *params)
428 {
429  error_t error;
430 
431  //Check parameters
432  if(context == NULL || params == NULL)
434 
435  //Initialize variables
436  error = NO_ERROR;
437 
438  //Execute the sequence of HTTP requests
439  while(!error)
440  {
441  //Check ACME client state
442  if(context->state == ACME_CLIENT_STATE_CONNECTED)
443  {
444  //Check account information
445  error = acmeClientCheckAccountParams(params);
446 
447  //Check status code
448  if(!error)
449  {
450  //Initialize account object
451  osMemset(&context->account, 0, sizeof(AcmeAccount));
452 
453  //Release the current key pair, if any
454  acmeClientUnloadKeyPair(&context->accountKey);
455 
456  //The public and private keys are encoded in PEM format
457  error = acmeClientLoadKeyPair(&context->accountKey,
458  params->publicKey, params->publicKeyLen, params->privateKey,
459  params->privateKeyLen, params->password);
460  }
461 
462  //Check status code
463  if(!error)
464  {
465  //In order to help clients configure themselves with the right URLs
466  //for each ACME operation, ACME servers provide a directory object
467  //(refer to RFC 8555, section 7.1.1)
468  context->state = ACME_CLIENT_STATE_DIRECTORY;
469  }
470  }
471  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
472  {
473  //If the directory object is no longer valid, the client must access
474  //the directory again by sending a GET request to the directory URL
475  error = acmeClientSendDirectoryRequest(context);
476 
477  //Check status code
478  if(!error)
479  {
480  //Update ACME client state
481  context->state = ACME_CLIENT_STATE_NEW_NONCE;
482  }
483  }
484  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
485  {
486  //Before sending a POST request to the server, an ACME client needs to
487  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
488  error = acmeClientSendNewNonceRequest(context);
489 
490  //Check status code
491  if(!error)
492  {
493  //Update ACME client state
494  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
495  }
496  }
497  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
498  {
499  //A client creates a new account by sending a POST request to the
500  //server's newAccount URL (refer to RFC 8555, section 7.3)
501  error = acmeClientSendNewAccountRequest(context, params, FALSE);
502 
503  //Check status code
504  if(!error)
505  {
506  //Update ACME client state
507  context->state = ACME_CLIENT_STATE_CONNECTED;
508  break;
509  }
510  }
511  else
512  {
513  //Invalid state
514  error = ERROR_WRONG_STATE;
515  }
516  }
517 
518  //Unexpected HTTP response?
519  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
520  error == ERROR_RESPONSE_TOO_LARGE)
521  {
522  //Revert to default state
523  context->state = ACME_CLIENT_STATE_CONNECTED;
524  }
525 
526  //Return status code
527  return error;
528 }
529 
530 
531 /**
532  * @brief Account information update
533  * @param[in] context Pointer to the ACME client context
534  * @param[in] params Updated account information
535  * @return Error code
536  **/
537 
539  const AcmeAccountParams *params)
540 {
541  error_t error;
542 
543  //Check parameters
544  if(context == NULL || params == NULL)
546 
547  //Initialize variables
548  error = NO_ERROR;
549 
550  //Execute the sequence of HTTP requests
551  while(!error)
552  {
553  //Check ACME client state
554  if(context->state == ACME_CLIENT_STATE_CONNECTED)
555  {
556  //Check account information
557  error = acmeClientCheckAccountParams(params);
558 
559  //Check status code
560  if(!error)
561  {
562  //In order to help clients configure themselves with the right URLs
563  //for each ACME operation, ACME servers provide a directory object
564  //(refer to RFC 8555, section 7.1.1)
565  context->state = ACME_CLIENT_STATE_DIRECTORY;
566  }
567  }
568  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
569  {
570  //If the directory object is no longer valid, the client must access
571  //the directory again by sending a GET request to the directory URL
572  error = acmeClientSendDirectoryRequest(context);
573 
574  //Check status code
575  if(!error)
576  {
577  //Update ACME client state
578  context->state = ACME_CLIENT_STATE_NEW_NONCE;
579  }
580  }
581  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
582  {
583  //Before sending a POST request to the server, an ACME client needs to
584  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
585  error = acmeClientSendNewNonceRequest(context);
586 
587  //Check status code
588  if(!error)
589  {
590  //Update ACME client state
591  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
592  }
593  }
594  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
595  {
596  //If a client wishes to find the URL for an existing account, then
597  //it should do so by sending a POST request to the newAccount URL
598  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
599  //section 7.3.1)
600  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
601 
602  //Check status code
603  if(!error)
604  {
605  //Update ACME client state
606  context->state = ACME_CLIENT_STATE_UPDATE_ACCOUNT;
607  }
608  }
609  else if(context->state == ACME_CLIENT_STATE_UPDATE_ACCOUNT)
610  {
611  //If the client wishes to update the account information, it sends a
612  //POST request with updated information to the account URL (refer to
613  //RFC 8555, section 7.3.1)
614  error = acmeClientSendUpdateAccountRequest(context, params);
615 
616  //Check status code
617  if(!error)
618  {
619  //Update ACME client state
620  context->state = ACME_CLIENT_STATE_CONNECTED;
621  break;
622  }
623  }
624  else
625  {
626  //Invalid state
627  error = ERROR_WRONG_STATE;
628  }
629  }
630 
631  //Unexpected HTTP response?
632  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
633  error == ERROR_RESPONSE_TOO_LARGE)
634  {
635  //Revert to default state
636  context->state = ACME_CLIENT_STATE_CONNECTED;
637  }
638 
639  //Return status code
640  return error;
641 }
642 
643 
644 /**
645  * @brief Account key rollover
646  * @param[in] context Pointer to the ACME client context
647  * @param[in] publicKey New public key (PEM format)
648  * @param[in] publicKeyLen Length of the new public key
649  * @param[in] privateKey New private key (PEM format)
650  * @param[in] privateKeyLen Length of the new private key
651  * @param[in] password NULL-terminated string containing the password. This
652  * parameter is required if the private key is encrypted
653  * @return Error code
654  **/
655 
657  const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey,
658  size_t privateKeyLen, const char_t *password)
659 {
660  error_t error;
661 
662  //Check parameters
663  if(context == NULL || publicKey == NULL || publicKeyLen == 0 ||
664  privateKey == NULL || privateKeyLen == 0)
665  {
667  }
668 
669  //Initialize variables
670  error = NO_ERROR;
671 
672  //Execute the sequence of HTTP requests
673  while(!error)
674  {
675  //Check ACME client state
676  if(context->state == ACME_CLIENT_STATE_CONNECTED)
677  {
678  //In order to help clients configure themselves with the right URLs for
679  //each ACME operation, ACME servers provide a directory object (refer
680  //to RFC 8555, section 7.1.1)
681  context->state = ACME_CLIENT_STATE_DIRECTORY;
682  }
683  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
684  {
685  //If the directory object is no longer valid, the client must access
686  //the directory again by sending a GET request to the directory URL
687  error = acmeClientSendDirectoryRequest(context);
688 
689  //Check status code
690  if(!error)
691  {
692  //Update ACME client state
693  context->state = ACME_CLIENT_STATE_NEW_NONCE;
694  }
695  }
696  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
697  {
698  //Before sending a POST request to the server, an ACME client needs to
699  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
700  error = acmeClientSendNewNonceRequest(context);
701 
702  //Check status code
703  if(!error)
704  {
705  //Update ACME client state
706  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
707  }
708  }
709  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
710  {
711  //If a client wishes to find the URL for an existing account, then
712  //it should do so by sending a POST request to the newAccount URL
713  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
714  //section 7.3.1)
715  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
716 
717  //Check status code
718  if(!error)
719  {
720  //Update ACME client state
721  context->state = ACME_CLIENT_STATE_CHANGE_KEY;
722  }
723  }
724  else if(context->state == ACME_CLIENT_STATE_CHANGE_KEY)
725  {
726  //A client can change the public key that is associated with an
727  //account, by sending a POST request to the server's keyChange
728  //URL (refer to RFC 8555, section 7.3.5)
729  error = acmeClientSendKeyChangeRequest(context, publicKey,
730  publicKeyLen, privateKey, privateKeyLen, password);
731 
732  //Check status code
733  if(!error)
734  {
735  //Unload the old account key
736  acmeClientUnloadKeyPair(&context->accountKey);
737 
738  //Load the new account key
739  error = acmeClientLoadKeyPair(&context->accountKey, publicKey,
740  publicKeyLen, privateKey, privateKeyLen, password);
741 
742  //Update ACME client state
743  context->state = ACME_CLIENT_STATE_CONNECTED;
744  break;
745  }
746  }
747  else
748  {
749  //Invalid state
750  error = ERROR_WRONG_STATE;
751  }
752  }
753 
754  //Unexpected HTTP response?
755  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
756  error == ERROR_RESPONSE_TOO_LARGE)
757  {
758  //Revert to default state
759  context->state = ACME_CLIENT_STATE_CONNECTED;
760  }
761 
762  //Return status code
763  return error;
764 }
765 
766 
767 /**
768  * @brief ACME account deactivation
769  * @param[in] context Pointer to the ACME client context
770  * @return Error code
771  **/
772 
774 {
775  error_t error;
776  AcmeAccountParams params;
777 
778  //Make sure the ACME client context is valid
779  if(context == NULL)
781 
782  //Initialize variables
783  error = NO_ERROR;
784 
785  //Execute the sequence of HTTP requests
786  while(!error)
787  {
788  //Check ACME client state
789  if(context->state == ACME_CLIENT_STATE_CONNECTED)
790  {
791  //In order to help clients configure themselves with the right URLs for
792  //each ACME operation, ACME servers provide a directory object (refer
793  //to RFC 8555, section 7.1.1)
794  context->state = ACME_CLIENT_STATE_DIRECTORY;
795  }
796  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
797  {
798  //If the directory object is no longer valid, the client must access
799  //the directory again by sending a GET request to the directory URL
800  error = acmeClientSendDirectoryRequest(context);
801 
802  //Check status code
803  if(!error)
804  {
805  //Update ACME client state
806  context->state = ACME_CLIENT_STATE_NEW_NONCE;
807  }
808  }
809  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
810  {
811  //Before sending a POST request to the server, an ACME client needs to
812  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
813  error = acmeClientSendNewNonceRequest(context);
814 
815  //Check status code
816  if(!error)
817  {
818  //Update ACME client state
819  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
820  }
821  }
822  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
823  {
824  //If a client wishes to find the URL for an existing account, then
825  //it should do so by sending a POST request to the newAccount URL
826  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
827  //section 7.3.1)
828  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
829 
830  //Check status code
831  if(!error)
832  {
833  //Update ACME client state
834  context->state = ACME_CLIENT_STATE_DEACTIVATE_ACCOUNT;
835  }
836  }
837  else if(context->state == ACME_CLIENT_STATE_DEACTIVATE_ACCOUNT)
838  {
839  //Initialize account parameters
840  osMemset(&params, 0, sizeof(AcmeAccountParams));
841 
842  //A client can deactivate an account by posting a signed update to the
843  //account URL with a status field of "deactivated" (refer to RFC 8555,
844  //section 7.3.1)
845  params.status = "deactivated";
846 
847  //Send the POST request to the account URL
848  error = acmeClientSendUpdateAccountRequest(context, &params);
849 
850  //Check status code
851  if(!error)
852  {
853  //Update ACME client state
854  context->state = ACME_CLIENT_STATE_CONNECTED;
855  break;
856  }
857  }
858  else
859  {
860  //Invalid state
861  error = ERROR_WRONG_STATE;
862  }
863  }
864 
865  //Unexpected HTTP response?
866  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
867  error == ERROR_RESPONSE_TOO_LARGE)
868  {
869  //Revert to default state
870  context->state = ACME_CLIENT_STATE_CONNECTED;
871  }
872 
873  //Return status code
874  return error;
875 }
876 
877 
878 /**
879  * @brief Begin the certificate issuance process
880  * @param[in] context Pointer to the ACME client context
881  * @param[in] params Certificate order information
882  * @return Error code
883  **/
884 
886  const AcmeOrderParams *params)
887 {
888  error_t error;
889 
890  //Check parameters
891  if(context == NULL || params == NULL)
893 
894  //Initialize variables
895  error = NO_ERROR;
896 
897  //Execute the sequence of HTTP requests
898  while(!error)
899  {
900  //Check ACME client state
901  if(context->state == ACME_CLIENT_STATE_CONNECTED)
902  {
903  //Check certificate order information
904  error = acmeClientCheckOrderParams(params);
905 
906  //Check status code
907  if(!error)
908  {
909  //Initialize order object
910  error = acmeClientInitOrder(context, params);
911  }
912 
913  //Check status code
914  if(!error)
915  {
916  //In order to help clients configure themselves with the right URLs
917  //for each ACME operation, ACME servers provide a directory object
918  //(refer to RFC 8555, section 7.1.1)
919  context->state = ACME_CLIENT_STATE_DIRECTORY;
920  }
921  }
922  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
923  {
924  //If the directory object is no longer valid, the client must access
925  //the directory again by sending a GET request to the directory URL
926  error = acmeClientSendDirectoryRequest(context);
927 
928  //Check status code
929  if(!error)
930  {
931  //Update ACME client state
932  context->state = ACME_CLIENT_STATE_NEW_NONCE;
933  }
934  }
935  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
936  {
937  //Before sending a POST request to the server, an ACME client needs to
938  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
939  error = acmeClientSendNewNonceRequest(context);
940 
941  //Check status code
942  if(!error)
943  {
944  //Update ACME client state
945  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
946  }
947  }
948  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
949  {
950  //If a client wishes to find the URL for an existing account, then
951  //it should do so by sending a POST request to the newAccount URL
952  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
953  //section 7.3.1)
954  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
955 
956  //Check status code
957  if(!error)
958  {
959  //Update ACME client state
960  context->state = ACME_CLIENT_STATE_NEW_ORDER;
961  }
962  }
963  else if(context->state == ACME_CLIENT_STATE_NEW_ORDER)
964  {
965  //The client begins the certificate issuance process by sending a
966  //POST request to the server's newOrder resource (refer to RFC 8555,
967  //section 7.4)
968  error = acmeClientSendNewOrderRequest(context, params);
969 
970  //Check status code
971  if(!error)
972  {
973  //Point to the first authorization
974  context->index = 0;
975  //Update ACME client state
976  context->state = ACME_CLIENT_STATE_AUTHORIZATION;
977  }
978  }
979  else if(context->state == ACME_CLIENT_STATE_AUTHORIZATION)
980  {
981  //Loop through the authorizations
982  if(context->index < context->numAuthorizations)
983  {
984  AcmeAuthorization *authorization;
985 
986  //Point to the current authorization
987  authorization = &context->authorizations[context->index];
988 
989  //When a client receives an order from the server in reply to a
990  //newOrder request, it downloads the authorization resources by
991  //sending POST-as-GET requests to the indicated URLs (refer to
992  //RFC 8555, section 7.5)
993  error = acmeClientSendAuthorizationRequest(context, authorization);
994 
995  //Check status code
996  if(!error)
997  {
998  //Point the next authorization
999  context->index++;
1000  }
1001  }
1002  else
1003  {
1004  //All the authorizations have been downloaded
1005  context->state = ACME_CLIENT_STATE_CONNECTED;
1006  break;
1007  }
1008  }
1009  else
1010  {
1011  //Invalid state
1012  error = ERROR_WRONG_STATE;
1013  }
1014  }
1015 
1016  //Unexpected HTTP response?
1017  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1018  error == ERROR_RESPONSE_TOO_LARGE)
1019  {
1020  //Revert to default state
1021  context->state = ACME_CLIENT_STATE_CONNECTED;
1022  }
1023 
1024  //Return status code
1025  return error;
1026 }
1027 
1028 
1029 /**
1030  * @brief Get the key authorization that matches a given token (HTTP challenge)
1031  * @param[in] context Pointer to the ACME client context
1032  * @param[in] token NULL-terminated string that contains the token
1033  * @return The function returns a NULL-terminated string that contains the key
1034  * authorization if the token is valid. Else, the NULL pointer is returned
1035  **/
1036 
1038  const char_t *token)
1039 {
1040  const char_t *keyAuth;
1041 
1042  //Default value
1043  keyAuth = NULL;
1044 
1045 #if (ACME_CLIENT_HTTP_CHALLENGE_SUPPORT == ENABLED)
1046  //Check parameters
1047  if(context != NULL && token != NULL)
1048  {
1049  uint_t i;
1050 
1051  //Loop through the challenges
1052  for(i = 0; i < context->numChallenges; i++)
1053  {
1054  //Check the status of the challenge
1055  if(context->challenges[i].status == ACME_CHALLENGE_STATUS_PENDING ||
1056  context->challenges[i].status == ACME_CHALLENGE_STATUS_PROCESSING)
1057  {
1058  //HTTP validation method?
1059  if(context->challenges[i].type == ACME_CHALLENGE_TYPE_HTTP_01)
1060  {
1061  //Compare token values
1062  if(osStrcmp(context->challenges[i].token, token) == 0)
1063  {
1064  //Point to the key authorization
1065  keyAuth = context->challenges[i].keyAuth;
1066  break;
1067  }
1068  }
1069  }
1070  }
1071  }
1072 #endif
1073 
1074  //Return the ASCII representation of the key authorization
1075  return keyAuth;
1076 }
1077 
1078 
1079 /**
1080  * @brief Get the key authorization digest that matches a given identifier (DNS challenge)
1081  * @param[in] context Pointer to the ACME client context
1082  * @param[in] identifier NULL-terminated string that contains the domain name
1083  * @return The function returns a NULL-terminated string that contains the
1084  * Base64url-encoded digest of the key authorization if the identifier is
1085  * valid. Else, the NULL pointer is returned
1086  **/
1087 
1089  const char_t *identifier)
1090 {
1091  const char_t *keyAuth;
1092 
1093  //Default value
1094  keyAuth = NULL;
1095 
1096 #if (ACME_CLIENT_DNS_CHALLENGE_SUPPORT == ENABLED)
1097  //Check parameters
1098  if(context != NULL && identifier != NULL)
1099  {
1100  uint_t i;
1101 
1102  //Loop through the challenges
1103  for(i = 0; i < context->numChallenges; i++)
1104  {
1105  //Check the status of the challenge
1106  if(context->challenges[i].status == ACME_CHALLENGE_STATUS_PENDING ||
1107  context->challenges[i].status == ACME_CHALLENGE_STATUS_PROCESSING)
1108  {
1109  //DNS validation method?
1110  if(context->challenges[i].type == ACME_CHALLENGE_TYPE_DNS_01)
1111  {
1112  //Any identifier of type "dns" may have a wildcard domain name as
1113  //its value
1114  if(context->challenges[i].wildcard)
1115  {
1116  //A wildcard domain name consists of a single asterisk character
1117  //followed by a single full stop character ("*.") followed by a
1118  //domain name
1119  if(osStrncmp(identifier, "*.", 2) == 0 &&
1120  osStrcmp(context->challenges[i].identifier, identifier + 2) == 0)
1121  {
1122  //Point to the key authorization digest
1123  keyAuth = context->challenges[i].keyAuth;
1124  break;
1125  }
1126  }
1127  else
1128  {
1129  //Compare identifier values
1130  if(osStrcmp(context->challenges[i].identifier, identifier) == 0)
1131  {
1132  //Point to the key authorization digest
1133  keyAuth = context->challenges[i].keyAuth;
1134  break;
1135  }
1136  }
1137  }
1138  }
1139  }
1140  }
1141 #endif
1142 
1143  //Return the Base64url representation of the key authorization digest
1144  return keyAuth;
1145 }
1146 
1147 
1148 /**
1149  * @brief Get the self-certificate that matches a given identifier (TLS-ALPN challenge)
1150  * @param[in] context Pointer to the ACME client context
1151  * @param[in] identifier NULL-terminated string that contains the domain name
1152  * @return The function returns a NULL-terminated string that contains the
1153  * TLS-ALPN certificate if the identifier is valid. Else, the NULL pointer
1154  * is returned
1155  **/
1156 
1158  const char_t *identifier)
1159 {
1160  const char_t *cert;
1161 
1162  //Default value
1163  cert = NULL;
1164 
1165 #if (ACME_CLIENT_TLS_ALPN_CHALLENGE_SUPPORT == ENABLED)
1166  //Check parameters
1167  if(context != NULL && identifier != NULL)
1168  {
1169  uint_t i;
1170 
1171  //Loop through the challenges
1172  for(i = 0; i < context->numChallenges; i++)
1173  {
1174  //Check the status of the challenge
1175  if(context->challenges[i].status == ACME_CHALLENGE_STATUS_PENDING ||
1176  context->challenges[i].status == ACME_CHALLENGE_STATUS_PROCESSING)
1177  {
1178  //TLS with ALPN validation method?
1179  if(context->challenges[i].type == ACME_CHALLENGE_TYPE_TLS_ALPN_01)
1180  {
1181  //Compare identifier values
1182  if(osStrcmp(context->challenges[i].identifier, identifier) == 0)
1183  {
1184  //Point to the self-signed certificate
1185  cert = context->challenges[i].cert;
1186  break;
1187  }
1188  }
1189  }
1190  }
1191  }
1192 #endif
1193 
1194  //Return the TLS-ALPN certificate
1195  return cert;
1196 }
1197 
1198 
1199 /**
1200  * @brief Poll for order status
1201  * @param[in] context Pointer to the ACME client context
1202  * @param[out] orderStatus Order status
1203  * @return Error code
1204  **/
1205 
1207  AcmeOrderStatus *orderStatus)
1208 {
1209  error_t error;
1210 
1211  //Check parameters
1212  if(context == NULL || orderStatus == NULL)
1213  return ERROR_INVALID_PARAMETER;
1214 
1215  //Initialize variables
1216  error = NO_ERROR;
1217 
1218  //Execute the sequence of HTTP requests
1219  while(!error)
1220  {
1221  //Check ACME client state
1222  if(context->state == ACME_CLIENT_STATE_CONNECTED)
1223  {
1224  //Check the order of the order
1225  if(context->order.status == ACME_ORDER_STATUS_PENDING ||
1226  context->order.status == ACME_ORDER_STATUS_READY ||
1227  context->order.status == ACME_ORDER_STATUS_PROCESSING)
1228  {
1229  //In order to help clients configure themselves with the right URLs
1230  //for each ACME operation, ACME servers provide a directory object
1231  //(refer to RFC 8555, section 7.1.1)
1232  context->state = ACME_CLIENT_STATE_DIRECTORY;
1233  }
1234  else if(context->order.status == ACME_ORDER_STATUS_VALID ||
1235  context->order.status == ACME_ORDER_STATUS_INVALID)
1236  {
1237  //Exit immediately
1238  break;
1239  }
1240  else
1241  {
1242  //Report an error
1243  error = ERROR_WRONG_STATE;
1244  }
1245  }
1246  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
1247  {
1248  //If the directory object is no longer valid, the client must access
1249  //the directory again by sending a GET request to the directory URL
1250  error = acmeClientSendDirectoryRequest(context);
1251 
1252  //Check status code
1253  if(!error)
1254  {
1255  //Update ACME client state
1256  context->state = ACME_CLIENT_STATE_NEW_NONCE;
1257  }
1258  }
1259  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
1260  {
1261  //Before sending a POST request to the server, an ACME client needs to
1262  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
1263  error = acmeClientSendNewNonceRequest(context);
1264 
1265  //Check status code
1266  if(!error)
1267  {
1268  //Update ACME client state
1269  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
1270  }
1271  }
1272  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
1273  {
1274  //If a client wishes to find the URL for an existing account, then
1275  //it should do so by sending a POST request to the newAccount URL
1276  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
1277  //section 7.3.1)
1278  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
1279 
1280  //Check status code
1281  if(!error)
1282  {
1283  //Clients should check the status of the order to determine whether
1284  //they need to take any action (refer to RFC 8555, section 7.1.3)
1285  if(context->order.status == ACME_ORDER_STATUS_PENDING)
1286  {
1287  //Point to the first challenge
1288  context->index = 0;
1289 
1290  //The client indicate to the server that it is ready for the
1291  //challenge validation
1292  context->state = ACME_CLIENT_STATE_CHALLENGE_READY;
1293  }
1294  else if(context->order.status == ACME_ORDER_STATUS_READY)
1295  {
1296  //All the authorizations listed in the order object are in the
1297  //"valid" state
1298  context->state = ACME_CLIENT_STATE_FINALIZE;
1299  }
1300  else if(context->order.status == ACME_ORDER_STATUS_PROCESSING)
1301  {
1302  //The client has already submitted a request to the order's
1303  //"finalize" URL
1304  context->state = ACME_CLIENT_STATE_POLL_STATUS_2;
1305  }
1306  else
1307  {
1308  //Report an error
1309  error = ERROR_WRONG_STATE;
1310  }
1311  }
1312  }
1313  else if(context->state == ACME_CLIENT_STATE_CHALLENGE_READY)
1314  {
1315  //Loop through the authorizations
1316  if(context->index < context->numChallenges)
1317  {
1318  AcmeChallenge *challenge;
1319 
1320  //Point to the current challenge
1321  challenge = &context->challenges[context->index];
1322 
1323  //Check the status of the challenge
1324  if(challenge->status == ACME_CHALLENGE_STATUS_PENDING)
1325  {
1326  //The client indicates to the server that it is ready for the
1327  //challenge validation by sending an empty JSON body carried in
1328  //a POST request to the challenge URL (refer to RFC 8555,
1329  //section 7.5.1)
1330  error = acmeClientSendChallengeReadyRequest(context, challenge);
1331 
1332  //Check status code
1333  if(!error)
1334  {
1335  //The challenge transitions to the "processing" state when
1336  //the client responds to the challenge
1338  }
1339  }
1340 
1341  //Check status code
1342  if(!error)
1343  {
1344  //Point the next challenge
1345  context->index++;
1346  }
1347  }
1348  else
1349  {
1350  //Update ACME client state
1351  context->state = ACME_CLIENT_STATE_POLL_STATUS_1;
1352  }
1353  }
1354  else if(context->state == ACME_CLIENT_STATE_POLL_STATUS_1)
1355  {
1356  //The client should then send a POST-as-GET request to the order
1357  //resource to obtain its current state refer to RFC 8555, section 7.4)
1358  error = acmeClientSendOrderStatusRequest(context);
1359 
1360  //Check status code
1361  if(!error)
1362  {
1363  //Check the status of the order
1364  if(context->order.status == ACME_ORDER_STATUS_PENDING ||
1365  context->order.status == ACME_ORDER_STATUS_INVALID)
1366  {
1367  //Update ACME client state
1368  context->state = ACME_CLIENT_STATE_CONNECTED;
1369  break;
1370  }
1371  else if(context->order.status == ACME_ORDER_STATUS_READY)
1372  {
1373  //Once all of the authorizations listed in the order object are
1374  //in the "valid" state, the order transitions to the "ready" state
1375  context->state = ACME_CLIENT_STATE_FINALIZE;
1376  }
1377  else
1378  {
1379  //Report an error
1380  error = ERROR_WRONG_STATE;
1381  break;
1382  }
1383  }
1384  }
1385  else if(context->state == ACME_CLIENT_STATE_FINALIZE)
1386  {
1387  //Once the client believes it has fulfilled the server's requirements,
1388  //it should send a POST request to the order resource's finalize URL.
1389  //The POST body MUST include a CSR (refer to RFC 8555, section 7.4)
1390  error = acmeClientSendFinalizeOrderRequest(context);
1391 
1392  //Check status code
1393  if(!error)
1394  {
1395  //The order moves to the "processing" state after the client submits
1396  //a request to the order's finalize URL
1397  context->order.status = ACME_ORDER_STATUS_PROCESSING;
1398 
1399  //Update ACME client state
1400  context->state = ACME_CLIENT_STATE_POLL_STATUS_2;
1401  }
1402  }
1403  else if(context->state == ACME_CLIENT_STATE_POLL_STATUS_2)
1404  {
1405  //The client should then send a POST-as-GET request to the order
1406  //resource to obtain its current state refer to RFC 8555, section 7.4)
1407  error = acmeClientSendOrderStatusRequest(context);
1408 
1409  //Check status code
1410  if(!error)
1411  {
1412  //Check the status of the order
1413  if(context->order.status != ACME_ORDER_STATUS_PROCESSING &&
1414  context->order.status != ACME_ORDER_STATUS_VALID &&
1415  context->order.status != ACME_ORDER_STATUS_INVALID)
1416  {
1417  //Report an error
1418  error = ERROR_WRONG_STATE;
1419  }
1420 
1421  //Update ACME client state
1422  context->state = ACME_CLIENT_STATE_CONNECTED;
1423  break;
1424  }
1425  }
1426  else
1427  {
1428  //Invalid state
1429  error = ERROR_WRONG_STATE;
1430  }
1431  }
1432 
1433  //Always return the actual status of the order
1434  *orderStatus = context->order.status;
1435 
1436  //Unexpected HTTP response?
1437  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1438  error == ERROR_RESPONSE_TOO_LARGE)
1439  {
1440  //Revert to default state
1441  context->state = ACME_CLIENT_STATE_CONNECTED;
1442  }
1443 
1444  //Return status code
1445  return error;
1446 }
1447 
1448 
1449 /**
1450  * @brief Download the certificate
1451  * @param[in] context Pointer to the ACME client context
1452  * @param[out] buffer Pointer to the buffer where to store the certificate chain
1453  * @param[in] size Size of the buffer, in bytes
1454  * @param[out] length Actual length of the certificate chain, in bytes
1455  * @return Error code
1456  **/
1457 
1459  char_t *buffer, size_t size, size_t *length)
1460 {
1461  error_t error;
1462 
1463  //Check parameters
1464  if(context == NULL || buffer == NULL || length == NULL)
1465  return ERROR_INVALID_PARAMETER;
1466 
1467  //Initialize variables
1468  error = NO_ERROR;
1469 
1470  //Execute the sequence of HTTP requests
1471  while(!error)
1472  {
1473  //Check ACME client state
1474  if(context->state == ACME_CLIENT_STATE_CONNECTED)
1475  {
1476  //Make sure the certificate has been issued by the ACME server
1477  if(context->order.status == ACME_ORDER_STATUS_VALID)
1478  {
1479  //In order to help clients configure themselves with the right URLs
1480  //for each ACME operation, ACME servers provide a directory object
1481  //(refer to RFC 8555, section 7.1.1)
1482  context->state = ACME_CLIENT_STATE_DIRECTORY;
1483  }
1484  else
1485  {
1486  //Report an error
1487  error = ERROR_WRONG_STATE;
1488  }
1489  }
1490  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
1491  {
1492  //If the directory object is no longer valid, the client must access
1493  //the directory again by sending a GET request to the directory URL
1494  error = acmeClientSendDirectoryRequest(context);
1495 
1496  //Check status code
1497  if(!error)
1498  {
1499  //Update ACME client state
1500  context->state = ACME_CLIENT_STATE_NEW_NONCE;
1501  }
1502  }
1503  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
1504  {
1505  //Before sending a POST request to the server, an ACME client needs to
1506  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
1507  error = acmeClientSendNewNonceRequest(context);
1508 
1509  //Check status code
1510  if(!error)
1511  {
1512  //Update ACME client state
1513  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
1514  }
1515  }
1516  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
1517  {
1518  //If a client wishes to find the URL for an existing account, then
1519  //it should do so by sending a POST request to the newAccount URL
1520  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
1521  //section 7.3.1)
1522  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
1523 
1524  //Check status code
1525  if(!error)
1526  {
1527  //Update ACME client state
1528  context->state = ACME_CLIENT_STATE_DOWNLOAD_CERT;
1529  }
1530  }
1531  else if(context->state == ACME_CLIENT_STATE_DOWNLOAD_CERT)
1532  {
1533  //To download the issued certificate, the client simply sends a
1534  //POST-as-GET request to the certificate URL (refer to RFC 8555,
1535  //section 7.4.2)
1536  error = acmeClientSendDownloadCertRequest(context, buffer, size,
1537  length);
1538 
1539  //Check status code
1540  if(!error)
1541  {
1542  //Update ACME client state
1543  context->state = ACME_CLIENT_STATE_CONNECTED;
1544  break;
1545  }
1546  }
1547  else
1548  {
1549  //Invalid state
1550  error = ERROR_WRONG_STATE;
1551  }
1552  }
1553 
1554  //Unexpected HTTP response?
1555  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1556  error == ERROR_RESPONSE_TOO_LARGE)
1557  {
1558  //Revert to default state
1559  context->state = ACME_CLIENT_STATE_CONNECTED;
1560  }
1561 
1562  //Return status code
1563  return error;
1564 }
1565 
1566 
1567 /**
1568  * @brief Certificate revocation
1569  * @param[in] context Pointer to the ACME client context
1570  * @param[in] cert Certificate to be revoked (PEM format)
1571  * @param[in] certLen Length of the certificate, in bytes
1572  * @param[in] privateKey Private key associated with the certificate (PEM
1573  * format). This parameter is required if the certificate key, rather than
1574  * the account key, is to be used to sign the revocation request
1575  * @param[in] privateKeyLen Length of the private key
1576  * @param[in] password NULL-terminated string containing the password. This
1577  * parameter is required if the private key is encrypted
1578  * @param[in] reason Revocation reason code
1579  * @return Error code
1580  **/
1581 
1583  const char_t *cert, size_t certLen, const char_t *privateKey,
1584  size_t privateKeyLen, const char_t *password, AcmeReasonCode reason)
1585 {
1586  error_t error;
1587 
1588  //Check parameters
1589  if(context == NULL || cert == NULL || certLen == 0)
1590  return ERROR_INVALID_PARAMETER;
1591 
1592  //Initialize variables
1593  error = NO_ERROR;
1594 
1595  //Execute the sequence of HTTP requests
1596  while(!error)
1597  {
1598  //Check ACME client state
1599  if(context->state == ACME_CLIENT_STATE_CONNECTED)
1600  {
1601  //In order to help clients configure themselves with the right URLs for
1602  //each ACME operation, ACME servers provide a directory object (refer
1603  //to RFC 8555, section 7.1.1)
1604  context->state = ACME_CLIENT_STATE_DIRECTORY;
1605  }
1606  else if(context->state == ACME_CLIENT_STATE_DIRECTORY)
1607  {
1608  //If the directory object is no longer valid, the client must access
1609  //the directory again by sending a GET request to the directory URL
1610  error = acmeClientSendDirectoryRequest(context);
1611 
1612  //Check status code
1613  if(!error)
1614  {
1615  //Update ACME client state
1616  context->state = ACME_CLIENT_STATE_NEW_NONCE;
1617  }
1618  }
1619  else if(context->state == ACME_CLIENT_STATE_NEW_NONCE)
1620  {
1621  //Before sending a POST request to the server, an ACME client needs to
1622  //have a fresh anti-replay nonce (refer to RFC 8555, section 7.2)
1623  error = acmeClientSendNewNonceRequest(context);
1624 
1625  //Check status code
1626  if(!error)
1627  {
1628  //Revocation requests are different from other ACME requests in
1629  //that they can be signed with either an account key pair or the
1630  //key pair in the certificate (refer to RFC 8555, section 7.6)
1631  if(privateKey != NULL && privateKeyLen > 0)
1632  {
1633  //Use the certificate key pair
1634  context->state = ACME_CLIENT_STATE_REVOKE_CERT;
1635  }
1636  else if(context->accountKey.type != X509_KEY_TYPE_UNKNOWN)
1637  {
1638  //Use the account key pair
1639  context->state = ACME_CLIENT_STATE_NEW_ACCOUNT;
1640  }
1641  else
1642  {
1643  //Report an error
1644  error = ERROR_INVALID_KEY;
1645  }
1646  }
1647  }
1648  else if(context->state == ACME_CLIENT_STATE_NEW_ACCOUNT)
1649  {
1650  //If a client wishes to find the URL for an existing account, then
1651  //it should do so by sending a POST request to the newAccount URL
1652  //with an "onlyReturnExisting" field set to "true" (refer to RFC 8555,
1653  //section 7.3.1)
1654  error = acmeClientSendNewAccountRequest(context, NULL, TRUE);
1655 
1656  //Check status code
1657  if(!error)
1658  {
1659  //Update ACME client state
1660  context->state = ACME_CLIENT_STATE_REVOKE_CERT;
1661  }
1662  }
1663  else if(context->state == ACME_CLIENT_STATE_REVOKE_CERT)
1664  {
1665  //To request that a certificate be revoked, the client sends a POST
1666  //request to the ACME server's revokeCert URL (refer to RFC 8555,
1667  //section 7.6)
1668  error = acmeClientSendRevokeCertRequest(context, cert, certLen,
1669  privateKey, privateKeyLen, password, reason);
1670 
1671  //Check status code
1672  if(!error)
1673  {
1674  //Update ACME client state
1675  context->state = ACME_CLIENT_STATE_CONNECTED;
1676  break;
1677  }
1678  }
1679  else
1680  {
1681  //Invalid state
1682  error = ERROR_WRONG_STATE;
1683  }
1684  }
1685 
1686  //Unexpected HTTP response?
1687  if(error == ERROR_UNEXPECTED_STATUS || error == ERROR_INVALID_RESPONSE ||
1688  error == ERROR_RESPONSE_TOO_LARGE)
1689  {
1690  //Revert to default state
1691  context->state = ACME_CLIENT_STATE_CONNECTED;
1692  }
1693 
1694  //Return status code
1695  return error;
1696 }
1697 
1698 
1699 /**
1700  * @brief Gracefully disconnect from the ACME server
1701  * @param[in] context Pointer to the ACME client context
1702  * @return Error code
1703  **/
1704 
1706 {
1707  error_t error;
1708 
1709  //Make sure the ACME client context is valid
1710  if(context == NULL)
1711  return ERROR_INVALID_PARAMETER;
1712 
1713  //Initialize status code
1714  error = NO_ERROR;
1715 
1716  //Gracefully disconnect from the ACME server
1717  while(!error)
1718  {
1719  //Check ACME client state
1720  if(context->state == ACME_CLIENT_STATE_CONNECTED)
1721  {
1722  //Gracefully shutdown HTTPS connection
1723  context->state = ACME_CLIENT_STATE_DISCONNECTING;
1724  }
1725  else if(context->state == ACME_CLIENT_STATE_DISCONNECTING)
1726  {
1727  //Gracefully shutdown HTTPS connection
1728  error = httpClientDisconnect(&context->httpClientContext);
1729 
1730  //Check status code
1731  if(error == NO_ERROR)
1732  {
1733  //Close HTTPS connection
1734  httpClientClose(&context->httpClientContext);
1735  //Update ACME client state
1736  context->state = ACME_CLIENT_STATE_DISCONNECTED;
1737  }
1738  }
1739  else if(context->state == ACME_CLIENT_STATE_DISCONNECTED)
1740  {
1741  //The client is disconnected from the ACME server
1742  break;
1743  }
1744  else
1745  {
1746  //Invalid state
1747  error = ERROR_WRONG_STATE;
1748  }
1749  }
1750 
1751  //Failed to gracefully disconnect from the ACME server?
1752  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
1753  {
1754  //Close HTTPS connection
1755  httpClientClose(&context->httpClientContext);
1756  //Update ACME client state
1757  context->state = ACME_CLIENT_STATE_DISCONNECTED;
1758  }
1759 
1760  //Return status code
1761  return error;
1762 }
1763 
1764 
1765 /**
1766  * @brief Close the connection with the ACME server
1767  * @param[in] context Pointer to the ACME client context
1768  * @return Error code
1769  **/
1770 
1772 {
1773  //Make sure the ACME client context is valid
1774  if(context == NULL)
1775  return ERROR_INVALID_PARAMETER;
1776 
1777  //Close HTTPS connection
1778  httpClientClose(&context->httpClientContext);
1779  //Update ACME client state
1780  context->state = ACME_CLIENT_STATE_DISCONNECTED;
1781 
1782  //Successful processing
1783  return NO_ERROR;
1784 }
1785 
1786 
1787 /**
1788  * @brief Release ACME client context
1789  * @param[in] context Pointer to the ACME client context
1790  **/
1791 
1793 {
1794  //Make sure the ACME client context is valid
1795  if(context != NULL)
1796  {
1797  //Release HTTP client context
1798  httpClientDeinit(&context->httpClientContext);
1799 
1800  //Release keys
1801  acmeClientUnloadKeyPair(&context->accountKey);
1802  acmeClientUnloadKeyPair(&context->certKey);
1803 
1804  //Clear ACME client context
1805  osMemset(context, 0, sizeof(AcmeClientContext));
1806  }
1807 }
1808 
1809 #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 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
error_t acmeClientLoadKeyPair(AcmeKeyPair *keyPair, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen, const char_t *password)
Load public/private key pair.
#define PrngAlgo
Definition: crypto.h:973
void acmeClientDeinit(AcmeClientContext *context)
Release ACME client context.
Definition: acme_client.c:1792
#define TRUE
Definition: os_port.h:50
Helper functions for ACME client.
const char_t * password
Password (required if the private key is encrypted)
Definition: acme_client.h:447
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
error_t acmeClientSetAccountKey(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen, const char_t *password)
Load account key pair.
Definition: acme_client.c:399
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:1037
const char_t * publicKey
Account public key.
Definition: acme_client.h:443
Anti-replay nonce management.
Certificate order parameters.
Definition: acme_client.h:468
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:174
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:168
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:426
ACME account creation parameters.
Definition: acme_client.h:439
@ ERROR_WRONG_STATE
Definition: error.h:210
@ 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 acmeClientSendKeyChangeRequest(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen, const char_t *password)
Send HTTP request (keyChange URL)
@ 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:284
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:1157
@ ERROR_RESPONSE_TOO_LARGE
Definition: error.h:285
@ 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:538
error_t acmeClientClose(AcmeClientContext *context)
Close the connection with the ACME server.
Definition: acme_client.c:1771
#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 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:375
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
const char_t * status
Status of the account.
Definition: acme_client.h:448
@ 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.
char char_t
Definition: compiler_port.h:55
#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:446
@ HTTP_VERSION_1_1
Definition: http_common.h:63
@ ACME_CHALLENGE_TYPE_TLS_ALPN_01
Definition: acme_client.h:369
size_t publicKeyLen
Length of the account public key, in bytes.
Definition: acme_client.h:444
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 acmeClientChangeAccountKey(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen, const char_t *password)
Account key rollover.
Definition: acme_client.c:656
error_t acmeClientDownloadCertificate(AcmeClientContext *context, char_t *buffer, size_t size, size_t *length)
Download the certificate.
Definition: acme_client.c:1458
uint8_t identifier[]
const char_t * privateKey
Account private key.
Definition: acme_client.h:445
error_t acmeClientCreateOrder(AcmeClientContext *context, const AcmeOrderParams *params)
Begin the certificate issuance process.
Definition: acme_client.c:885
#define osStrncmp(s1, s2, length)
Definition: os_port.h:180
@ ACME_CLIENT_STATE_POLL_STATUS_1
Definition: acme_client.h:274
@ ACME_ORDER_STATUS_PENDING
Definition: acme_client.h:322
@ X509_KEY_TYPE_UNKNOWN
Definition: x509_common.h:634
@ 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:57
#define osMemset(p, value, length)
Definition: os_port.h:138
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:1206
#define osStrcpy(s1, s2)
Definition: os_port.h:210
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:1088
@ 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:773
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:1705
error_t acmeClientRevokeCertificate(AcmeClientContext *context, const char_t *cert, size_t certLen, const char_t *privateKey, size_t privateKeyLen, const char_t *password, AcmeReasonCode reason)
Certificate revocation.
Definition: acme_client.c:1582
@ ERROR_INVALID_KEY
Definition: error.h:106
@ 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
error_t acmeClientSendRevokeCertRequest(AcmeClientContext *context, const char_t *cert, size_t certLen, const char_t *privateKey, size_t privateKeyLen, const char_t *password, AcmeReasonCode reason)
Send HTTP request (revokeCert URL)