acme_client_account.c
Go to the documentation of this file.
1 /**
2  * @file acme_client_account.c
3  * @brief Account object management
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneACME Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL ACME_TRACE_LEVEL
33 
34 //Dependencies
35 #include "acme/acme_client.h"
37 #include "acme/acme_client_jose.h"
38 #include "acme/acme_client_misc.h"
39 #include "jansson.h"
40 #include "jansson_private.h"
41 #include "debug.h"
42 
43 //Check TCP/IP stack configuration
44 #if (ACME_CLIENT_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief Check account information
49  * @param[in] params Account information
50  * @return Error code
51  **/
52 
54 {
55  uint_t i;
56 
57  //Sanity check
58  if(params == NULL)
60 
61  //Check the number of contacts
64 
65  //Loop through the list of contacts
66  for(i = 0; i < params->numContacts; i++)
67  {
68  //Each item must contain a valid string
69  if(params->contacts[i] == NULL)
71  }
72 
73  //The account parameters are valid
74  return NO_ERROR;
75 }
76 
77 
78 /**
79  * @brief Send HTTP request (newAccount URL)
80  * @param[in] context Pointer to the ACME client context
81  * @param[in] params Account information
82  * @param[in] onlyReturnExisting Do not create a new account if one does not
83  * already exist
84  * @return Error code
85  **/
86 
88  const AcmeAccountParams *params, bool_t onlyReturnExisting)
89 {
90  error_t error;
91 
92  //Initialize variables
93  error = NO_ERROR;
94 
95  //Perform HTTP request
96  while(!error)
97  {
98  //Check HTTP request state
99  if(context->requestState == ACME_REQ_STATE_INIT)
100  {
101  //Debug message
102  TRACE_DEBUG("\r\n");
103  TRACE_DEBUG("###############################################################################\r\n");
104  TRACE_DEBUG("## NEW ACCOUNT ################################################################\r\n");
105  TRACE_DEBUG("###############################################################################\r\n");
106  TRACE_DEBUG("\r\n");
107 
108  //Check whether the client wishes to find the URL for an existing
109  //account
110  if(onlyReturnExisting)
111  {
112  //Valid account URL?
113  if(context->account.url[0] != '\0')
114  {
115  //The client has a account key and the corresponding account URL
116  break;
117  }
118  else
119  {
120  //To recover the account URL, the client sends a POST request to
121  //the newAccount URL with "onlyReturnExisting" set to "true"
122  context->requestState = ACME_REQ_STATE_FORMAT_BODY;
123  }
124  }
125  else
126  {
127  //Update HTTP request state
128  context->requestState = ACME_REQ_STATE_FORMAT_BODY;
129  }
130  }
131  else if(context->requestState == ACME_REQ_STATE_FORMAT_BODY)
132  {
133  //Format the body of the HTTP request
134  error = acmeClientFormatNewAccountRequest(context, params,
135  onlyReturnExisting);
136 
137  //Check status code
138  if(!error)
139  {
140  //Update HTTP request state
141  context->requestState = ACME_REQ_STATE_FORMAT_HEADER;
142  }
143  }
144  else if(context->requestState == ACME_REQ_STATE_FORMAT_HEADER)
145  {
146  //A client creates a new account by sending a POST request to the
147  //server's newAccount URL (refer to RFC 8555, section 7.3)
148  error = acmeClientFormatRequestHeader(context, "POST",
149  context->directory.newAccount);
150 
151  //Check status code
152  if(!error)
153  {
154  //Update HTTP request state
155  context->requestState = ACME_REQ_STATE_SEND_HEADER;
156  }
157  }
158  else if(context->requestState == ACME_REQ_STATE_SEND_HEADER ||
159  context->requestState == ACME_REQ_STATE_SEND_BODY ||
160  context->requestState == ACME_REQ_STATE_RECEIVE_HEADER ||
161  context->requestState == ACME_REQ_STATE_PARSE_HEADER ||
162  context->requestState == ACME_REQ_STATE_RECEIVE_BODY ||
163  context->requestState == ACME_REQ_STATE_CLOSE_BODY)
164  {
165  //Perform HTTP request/response transaction
166  error = acmeClientSendRequest(context);
167  }
168  else if(context->requestState == ACME_REQ_STATE_PARSE_BODY)
169  {
170  //Parse the body of the HTTP response
171  error = acmeClientParseNewAccountResponse(context);
172 
173  //The HTTP transaction is complete
174  context->requestState = ACME_REQ_STATE_INIT;
175  break;
176  }
177  else
178  {
179  //Invalid state
180  error = ERROR_WRONG_STATE;
181  }
182  }
183 
184  //Return status code
185  return error;
186 }
187 
188 
189 /**
190  * @brief Format HTTP request body (newAccount URL)
191  * @param[in] context Pointer to the ACME client context
192  * @param[in] params Account information
193  * @param[in] onlyReturnExisting Do not create a new account if one does not
194  * already exist
195  * @return Error code
196  **/
197 
199  const AcmeAccountParams *params, bool_t onlyReturnExisting)
200 {
201  error_t error;
202  int_t ret;
203  uint_t i;
204  size_t n;
205  char_t *protected;
206  char_t *payload;
207  json_t *payloadObj;
208  json_t *contactObj;
209 
210  //Initialize status code
211  ret = 0;
212 
213  //Initialize JSON object
214  payloadObj = json_object();
215 
216  //Valid account parameters?
217  if(params != NULL)
218  {
219  //The "contact" field contains an array of URLs that the server can use
220  //to contact the client for issues related to this account
221  if(params->numContacts > 0)
222  {
223  //Initialize JSON object
224  contactObj = json_array();
225 
226  //Loop through the list of contacts
227  for(i = 0; i < params->numContacts; i++)
228  {
229  //Format email address
230  osSprintf(context->buffer, "mailto:%s", params->contacts[i]);
231 
232  //Add the email address to the array
233  ret |= json_array_append_new(contactObj,
234  json_string(context->buffer));
235  }
236 
237  //Add the "contact" field to the payload
238  ret |= json_object_set_new(payloadObj, "contact", contactObj);
239  }
240 
241  //A client can indicate its agreement with the CA's terms of service by
242  //setting the "termsOfServiceAgreed" field in its account object to "true"
243  if(params->termsOfServiceAgreed)
244  {
245  //Add the "termsOfServiceAgreed" field to the payload object
246  ret |= json_object_set_new(payloadObj, "termsOfServiceAgreed",
247  json_true());
248  }
249  }
250 
251  //If the "onlyReturnExisting" field is present with the value "true", then
252  //the server must not create a new account if one does not already exist.
253  //This allows a client to look up an account URL based on an account key
254  if(onlyReturnExisting)
255  {
256  //Add the "onlyReturnExisting" field to the payload object
257  ret |= json_object_set_new(payloadObj, "onlyReturnExisting",
258  json_true());
259  }
260 
261  //JSON object successfully created?
262  if(ret == 0)
263  {
264  //Generate the JSON representation of the payload object
265  payload = json_dumps(payloadObj, JSON_COMPACT);
266  }
267  else
268  {
269  //An error occurred during processing
270  payload = NULL;
271  }
272 
273  //Valid JSON representation?
274  if(payload != NULL)
275  {
276  //Point to the buffer where to format the JWS protected header
277  protected = context->buffer;
278 
279  //Format JWS protected header
280  error = acmeClientFormatJwsProtectedHeader(&context->accountKey, NULL,
281  context->nonce, context->directory.newAccount, protected, &n);
282 
283  //Check status code
284  if(!error)
285  {
286  //Generate the JSON Web Signature
287  error = jwsCreate(context->prngAlgo, context->prngContext, protected,
288  payload, context->accountKey.alg, context->accountKey.crv,
289  context->accountKey.privateKey, context->buffer, &context->bufferLen);
290  }
291 
292  //Release JSON string
293  jsonp_free(payload);
294  }
295  else
296  {
297  //Report an error
298  error = ERROR_FAILURE;
299  }
300 
301  //Release JSON object
302  json_decref(payloadObj);
303 
304  //Return status code
305  return error;
306 }
307 
308 
309 /**
310  * @brief Parse HTTP response (newAccount URL)
311  * @param[in] context Pointer to the ACME client context
312  * @return Error code
313  **/
314 
316 {
317  //Check HTTP status code
318  if(!HTTP_STATUS_CODE_2YZ(context->statusCode))
320 
321  //The server must include a Replay-Nonce header field in every successful
322  //response to a POST request (refer to RFC 8555, section 6.5)
323  if(context->nonce[0] == '\0')
324  return ERROR_INVALID_RESPONSE;
325 
326  //The server returns the account URL in the Location HTTP header field (refer
327  //to RFC 8555, section 7.3)
328  if(context->account.url[0] == '\0')
329  return ERROR_INVALID_RESPONSE;
330 
331  //Successful processing
332  return NO_ERROR;
333 }
334 
335 
336 /**
337  * @brief Send HTTP request (account URL)
338  * @param[in] context Pointer to the ACME client context
339  * @param[in] params Account information
340  * @return Error code
341  **/
342 
344  const AcmeAccountParams *params)
345 {
346  error_t error;
347 
348  //Initialize variables
349  error = NO_ERROR;
350 
351  //Perform HTTP request
352  while(!error)
353  {
354  //Check HTTP request state
355  if(context->requestState == ACME_REQ_STATE_INIT)
356  {
357  //Debug message
358  TRACE_DEBUG("\r\n");
359  TRACE_DEBUG("###############################################################################\r\n");
360  TRACE_DEBUG("## UPDATE ACCOUNT #############################################################\r\n");
361  TRACE_DEBUG("###############################################################################\r\n");
362  TRACE_DEBUG("\r\n");
363 
364  //Update HTTP request state
365  context->requestState = ACME_REQ_STATE_FORMAT_BODY;
366  }
367  else if(context->requestState == ACME_REQ_STATE_FORMAT_BODY)
368  {
369  //Format the body of the HTTP request
370  error = acmeFormatUpdateAccountRequest(context, params);
371 
372  //Check status code
373  if(!error)
374  {
375  //Update HTTP request state
376  context->requestState = ACME_REQ_STATE_FORMAT_HEADER;
377  }
378  }
379  else if(context->requestState == ACME_REQ_STATE_FORMAT_HEADER)
380  {
381  //A client can update or deactivate an account by sending a POST
382  //request to the account URL
383  error = acmeClientFormatRequestHeader(context, "POST",
384  context->account.url);
385 
386  //Check status code
387  if(!error)
388  {
389  //Update HTTP request state
390  context->requestState = ACME_REQ_STATE_SEND_HEADER;
391  }
392  }
393  else if(context->requestState == ACME_REQ_STATE_SEND_HEADER ||
394  context->requestState == ACME_REQ_STATE_SEND_BODY ||
395  context->requestState == ACME_REQ_STATE_RECEIVE_HEADER ||
396  context->requestState == ACME_REQ_STATE_PARSE_HEADER ||
397  context->requestState == ACME_REQ_STATE_RECEIVE_BODY ||
398  context->requestState == ACME_REQ_STATE_CLOSE_BODY)
399  {
400  //Perform HTTP request/response transaction
401  error = acmeClientSendRequest(context);
402  }
403  else if(context->requestState == ACME_REQ_STATE_PARSE_BODY)
404  {
405  //Parse the body of the HTTP response
406  error = acmeClientParseUpdateAccountResponse(context);
407 
408  //The HTTP transaction is complete
409  context->requestState = ACME_REQ_STATE_INIT;
410  break;
411  }
412  else
413  {
414  //Invalid state
415  error = ERROR_WRONG_STATE;
416  }
417  }
418 
419  //Return status code
420  return error;
421 }
422 
423 
424 /**
425  * @brief Format HTTP request body (account URL)
426  * @param[in] context Pointer to the ACME client context
427  * @param[in] params Account information
428  * @return Error code
429  **/
430 
432  const AcmeAccountParams *params)
433 {
434  error_t error;
435  int_t ret;
436  uint_t i;
437  size_t n;
438  char_t *protected;
439  char_t *payload;
440  json_t *payloadObj;
441  json_t *contactObj;
442 
443  //Initialize status code
444  ret = 0;
445 
446  //Initialize JSON object
447  payloadObj = json_object();
448 
449  //The "contact" field contains an array of URLs that the server can use
450  //to contact the client for issues related to this account
451  if(params->numContacts > 0)
452  {
453  //Initialize JSON object
454  contactObj = json_array();
455 
456  //Loop through the list of contacts
457  for(i = 0; i < params->numContacts; i++)
458  {
459  //Format email address
460  osSprintf(context->buffer, "mailto:%s", params->contacts[i]);
461 
462  //Add the email address to the array
463  ret |= json_array_append_new(contactObj,
464  json_string(context->buffer));
465  }
466 
467  //Add the "contact" field to the payload
468  ret |= json_object_set_new(payloadObj, "contact", contactObj);
469  }
470 
471  //A client can deactivate an account by posting a signed update to the
472  //account URL with a status field of "deactivated"
473  if(params->status != NULL && params->status[0] != '\0')
474  {
475  //Add the "status" field to the payload
476  ret |= json_object_set_new(payloadObj, "status",
477  json_string(params->status));
478  }
479 
480  //JSON object successfully created?
481  if(ret == 0)
482  {
483  //Generate the JSON representation of the payload object
484  payload = json_dumps(payloadObj, JSON_COMPACT);
485  }
486  else
487  {
488  //An error occurred during processing
489  payload = NULL;
490  }
491 
492  //Valid JSON representation?
493  if(payload != NULL)
494  {
495  //Point to the buffer where to format the JWS protected header
496  protected = context->buffer;
497 
498  //Format JWS protected header
499  error = acmeClientFormatJwsProtectedHeader(&context->accountKey,
500  context->account.url, context->nonce, context->account.url,
501  protected, &n);
502 
503  //Check status code
504  if(!error)
505  {
506  //Generate the JSON Web Signature
507  error = jwsCreate(context->prngAlgo, context->prngContext, protected,
508  payload, context->accountKey.alg, context->accountKey.crv,
509  context->accountKey.privateKey, context->buffer, &context->bufferLen);
510  }
511 
512  //Release JSON string
513  jsonp_free(payload);
514  }
515  else
516  {
517  //Report an error
518  error = ERROR_FAILURE;
519  }
520 
521  //Release JSON object
522  json_decref(payloadObj);
523 
524  //Return status code
525  return error;
526 }
527 
528 
529 /**
530  * @brief Parse HTTP response (account URL)
531  * @param[in] context Pointer to the ACME client context
532  * @return Error code
533  **/
534 
536 {
537  //Check HTTP status code
538  if(!HTTP_STATUS_CODE_2YZ(context->statusCode))
540 
541  //The server must include a Replay-Nonce header field in every successful
542  //response to a POST request (refer to RFC 8555, section 6.5)
543  if(context->nonce[0] == '\0')
544  return ERROR_INVALID_RESPONSE;
545 
546  //Successful processing
547  return NO_ERROR;
548 }
549 
550 
551 /**
552  * @brief Send HTTP request (keyChange URL)
553  * @param[in] context Pointer to the ACME client context
554  * @param[in] publicKey New public key (PEM format)
555  * @param[in] publicKeyLen Length of the new public key
556  * @param[in] privateKey New private key (PEM format)
557  * @param[in] privateKeyLen Length of the new private key
558  * @return Error code
559  **/
560 
562  const char_t *publicKey, size_t publicKeyLen,
563  const char_t *privateKey, size_t privateKeyLen)
564 {
565  error_t error;
566 
567  //Initialize variables
568  error = NO_ERROR;
569 
570  //Perform HTTP request
571  while(!error)
572  {
573  //Check HTTP request state
574  if(context->requestState == ACME_REQ_STATE_INIT)
575  {
576  //Debug message
577  TRACE_DEBUG("\r\n");
578  TRACE_DEBUG("###############################################################################\r\n");
579  TRACE_DEBUG("## KEY CHANGE #################################################################\r\n");
580  TRACE_DEBUG("###############################################################################\r\n");
581  TRACE_DEBUG("\r\n");
582 
583  //Update HTTP request state
584  context->requestState = ACME_REQ_STATE_FORMAT_BODY;
585  }
586  else if(context->requestState == ACME_REQ_STATE_FORMAT_BODY)
587  {
588  //Format the body of the HTTP request
589  error = acmeClientFormatKeyChangeRequest(context, publicKey,
590  publicKeyLen, privateKey, privateKeyLen);
591 
592  //Check status code
593  if(!error)
594  {
595  //Update HTTP request state
596  context->requestState = ACME_REQ_STATE_FORMAT_HEADER;
597  }
598  }
599  else if(context->requestState == ACME_REQ_STATE_FORMAT_HEADER)
600  {
601  //A client can change the public key that is associated with an
602  //account, by sending a POST request to the server's keyChange
603  //URL (refer to RFC 8555, section 7.3.5)
604  error = acmeClientFormatRequestHeader(context, "POST",
605  context->directory.keyChange);
606 
607  //Check status code
608  if(!error)
609  {
610  //Update HTTP request state
611  context->requestState = ACME_REQ_STATE_SEND_HEADER;
612  }
613  }
614  else if(context->requestState == ACME_REQ_STATE_SEND_HEADER ||
615  context->requestState == ACME_REQ_STATE_SEND_BODY ||
616  context->requestState == ACME_REQ_STATE_RECEIVE_HEADER ||
617  context->requestState == ACME_REQ_STATE_PARSE_HEADER ||
618  context->requestState == ACME_REQ_STATE_RECEIVE_BODY ||
619  context->requestState == ACME_REQ_STATE_CLOSE_BODY)
620  {
621  //Perform HTTP request/response transaction
622  error = acmeClientSendRequest(context);
623  }
624  else if(context->requestState == ACME_REQ_STATE_PARSE_BODY)
625  {
626  //Parse the body of the HTTP response
627  error = acmeClientParseKeyChangeResponse(context);
628 
629  //The HTTP transaction is complete
630  context->requestState = ACME_REQ_STATE_INIT;
631  break;
632  }
633  else
634  {
635  //Invalid state
636  error = ERROR_WRONG_STATE;
637  }
638  }
639 
640  //Return status code
641  return error;
642 }
643 
644 
645 /**
646  * @brief Format HTTP request body (keyChange URL)
647  * @param[in] context Pointer to the ACME client context
648  * @param[in] publicKey New public key (PEM format)
649  * @param[in] publicKeyLen Length of the new public key
650  * @param[in] privateKey New private key (PEM format)
651  * @param[in] privateKeyLen Length of the new private key
652  * @return Error code
653  **/
654 
656  const char_t *publicKey, size_t publicKeyLen,
657  const char_t *privateKey, size_t privateKeyLen)
658 {
659  error_t error;
660  int_t ret;
661  size_t n;
662  char_t *protected;
663  char_t *payload;
664  char_t *keyChange;
665  json_t *keyChangeObj;
666  AcmeKeyPair newAccountKey;
667 
668  //Export the old public key to JWK format
669  error = acmeClientFormatJwk(&context->accountKey, context->buffer, &n,
670  FALSE);
671  //Any error to report?
672  if(error)
673  return error;
674 
675  //Load the new account key
676  error = acmeClientLoadKeyPair(&newAccountKey, publicKey, publicKeyLen,
677  privateKey, privateKeyLen);
678  //Any error to report?
679  if(error)
680  return error;
681 
682  //To create this request object, the client first constructs a keyChange
683  //object describing the account to be updated and its account key (refer
684  //to RFC 8555, section 7.3.5)
685  keyChangeObj = json_object();
686 
687  //The "account" field contains the URL for the account being modified
688  ret = json_object_set_new(keyChangeObj, "account",
689  json_string(context->account.url));
690 
691  //The "oldKey" field contains The JWK representation of the old key
692  ret |= json_object_set_new(keyChangeObj, "oldKey",
693  json_loads(context->buffer, 0, NULL));
694 
695  //JSON object successfully created?
696  if(ret == 0)
697  {
698  //Generate the JSON representation of the payload object
699  keyChange = json_dumps(keyChangeObj, JSON_COMPACT);
700  }
701  else
702  {
703  //An error occurred during processing
704  keyChange = NULL;
705  }
706 
707  //Valid JSON representation?
708  if(keyChange != NULL)
709  {
710  //Point to the buffer where to format the JWS protected header
711  protected = context->buffer;
712 
713  //The inner JWS must have a "jwk" header parameter containing the public
714  //key of the new key pair, must have the same "url" header parameter as
715  //the outer JWS and must omit the "nonce" header parameter
716  error = acmeClientFormatJwsProtectedHeader(&newAccountKey, NULL, NULL,
717  context->directory.keyChange, protected, &n);
718 
719  //Check status code
720  if(!error)
721  {
722  //The inner JWS is signed with the requested new account key
723  error = jwsCreate(context->prngAlgo, context->prngContext,
724  protected, keyChange, newAccountKey.alg, newAccountKey.crv,
725  newAccountKey.privateKey, context->buffer, &n);
726  }
727 
728  //Release JSON string
729  jsonp_free(keyChange);
730  }
731  else
732  {
733  //Report an error
734  error = ERROR_FAILURE;
735  }
736 
737  //Release JSON object
738  json_decref(keyChangeObj);
739 
740  //Unload the new account key
741  acmeClientUnloadKeyPair(&newAccountKey);
742 
743  //Check status code)
744  if(!error)
745  {
746  //The inner JWS becomes the payload for the outer JWS that is the body
747  //of the ACME request
748  payload = jsonp_strdup(context->buffer);
749 
750  //Valid JSON representation?
751  if(payload != NULL)
752  {
753  //Point to the buffer where to format the JWS protected header
754  protected = context->buffer;
755 
756  //The outer JWS must meet the normal requirements for an ACME JWS
757  //request body (refer to RFC 8555, section 7.3.5)
758  error = acmeClientFormatJwsProtectedHeader(&context->accountKey,
759  context->account.url, context->nonce, context->directory.keyChange,
760  protected, &n);
761 
762  //Check status code
763  if(!error)
764  {
765  //Generate the outer JWS
766  error = jwsCreate(context->prngAlgo, context->prngContext,
767  protected, payload, context->accountKey.alg,
768  context->accountKey.crv, context->accountKey.privateKey,
769  context->buffer, &context->bufferLen);
770  }
771 
772  //Release JSON string
773  jsonp_free(payload);
774  }
775  else
776  {
777  //Report an error
778  error = ERROR_FAILURE;
779  }
780  }
781 
782  //Return status code
783  return error;
784 }
785 
786 
787 /**
788  * @brief Parse HTTP response (keyChange URL)
789  * @param[in] context Pointer to the ACME client context
790  * @return Error code
791  **/
792 
794 {
795  //Check HTTP status code
796  if(!HTTP_STATUS_CODE_2YZ(context->statusCode))
798 
799  //The server must include a Replay-Nonce header field in every successful
800  //response to a POST request (refer to RFC 8555, section 6.5)
801  if(context->nonce[0] == '\0')
802  return ERROR_INVALID_RESPONSE;
803 
804  //Successful processing
805  return NO_ERROR;
806 }
807 
808 #endif
ACME client (Automatic Certificate Management Environment)
@ ACME_REQ_STATE_FORMAT_BODY
Definition: acme_client.h:292
@ ACME_REQ_STATE_PARSE_BODY
Definition: acme_client.h:297
@ ACME_REQ_STATE_RECEIVE_BODY
Definition: acme_client.h:296
@ ACME_REQ_STATE_CLOSE_BODY
Definition: acme_client.h:298
@ ACME_REQ_STATE_INIT
Definition: acme_client.h:289
@ ACME_REQ_STATE_RECEIVE_HEADER
Definition: acme_client.h:294
@ ACME_REQ_STATE_SEND_HEADER
Definition: acme_client.h:291
@ ACME_REQ_STATE_FORMAT_HEADER
Definition: acme_client.h:290
@ ACME_REQ_STATE_PARSE_HEADER
Definition: acme_client.h:295
@ ACME_REQ_STATE_SEND_BODY
Definition: acme_client.h:293
#define ACME_CLIENT_MAX_CONTACTS
Definition: acme_client.h:152
#define AcmeClientContext
Definition: acme_client.h:248
error_t acmeClientCheckAccountParams(const AcmeAccountParams *params)
Check account information.
error_t acmeClientSendUpdateAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params)
Send HTTP request (account URL)
error_t acmeClientFormatNewAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params, bool_t onlyReturnExisting)
Format HTTP request body (newAccount URL)
error_t acmeClientParseNewAccountResponse(AcmeClientContext *context)
Parse HTTP response (newAccount URL)
error_t acmeClientFormatKeyChangeRequest(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen)
Format HTTP request body (keyChange URL)
error_t acmeClientParseKeyChangeResponse(AcmeClientContext *context)
Parse HTTP response (keyChange URL)
error_t acmeClientParseUpdateAccountResponse(AcmeClientContext *context)
Parse HTTP response (account URL)
error_t acmeClientSendNewAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params, bool_t onlyReturnExisting)
Send HTTP request (newAccount URL)
error_t acmeFormatUpdateAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params)
Format HTTP request body (account URL)
error_t acmeClientSendKeyChangeRequest(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen)
Send HTTP request (keyChange URL)
Account object management.
error_t jwsCreate(const PrngAlgo *prngAlgo, void *prngContext, const char_t *protected, const char_t *payload, const char_t *alg, const char_t *crv, const void *privateKey, char_t *buffer, size_t *written)
Create a JSON Web Signature.
JOSE (JSON Object Signing and Encryption)
error_t acmeClientSendRequest(AcmeClientContext *context)
Send HTTP request.
void acmeClientUnloadKeyPair(AcmeKeyPair *keyPair)
Unload public/private key pair.
error_t acmeClientFormatJwsProtectedHeader(const AcmeKeyPair *keyPair, const char_t *kid, const char_t *nonce, const char_t *url, char_t *buffer, size_t *written)
Format JWS protected header.
error_t acmeClientFormatJwk(const AcmeKeyPair *keyPair, char_t *buffer, size_t *written, bool_t sort)
Export a public key to JWK format.
error_t acmeClientFormatRequestHeader(AcmeClientContext *context, const char_t *method, const char_t *url)
Format HTTP request header.
error_t acmeClientLoadKeyPair(AcmeKeyPair *keyPair, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen)
Load public/private key pair.
Helper functions for ACME client.
signed int int_t
Definition: compiler_port.h:49
unsigned int uint_t
Definition: compiler_port.h:50
char char_t
Definition: compiler_port.h:48
int bool_t
Definition: compiler_port.h:53
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_UNEXPECTED_STATUS
Definition: error.h:282
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_WRONG_STATE
Definition: error.h:209
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
@ ERROR_INVALID_RESPONSE
Definition: error.h:71
#define HTTP_STATUS_CODE_2YZ(code)
Definition: http_common.h:44
uint8_t payload[]
Definition: ipv6.h:277
#define osSprintf(dest,...)
Definition: os_port.h:231
#define FALSE
Definition: os_port.h:46
ACME account creation parameters.
Definition: acme_client.h:441
bool_t termsOfServiceAgreed
Indicates the client's agreement with the terms of service.
Definition: acme_client.h:444
const char_t * status
Status of the account.
Definition: acme_client.h:449
const char_t * contacts[ACME_CLIENT_MAX_CONTACTS]
Array of URLs that the server can use to contact the client.
Definition: acme_client.h:443
uint_t numContacts
Number of contact URLs.
Definition: acme_client.h:442
Public/private key pair.
Definition: acme_client.h:413
char_t alg[8]
Definition: acme_client.h:415
char_t crv[8]
Definition: acme_client.h:416
const void * privateKey
Definition: acme_client.h:418