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