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-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneACME Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.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.privateKey,
289  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.privateKey,
509  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  * @param[in] password NULL-terminated string containing the password. This
559  * parameter is required if the private key is encrypted
560  * @return Error code
561  **/
562 
564  const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey,
565  size_t privateKeyLen, const char_t *password)
566 {
567  error_t error;
568 
569  //Initialize variables
570  error = NO_ERROR;
571 
572  //Perform HTTP request
573  while(!error)
574  {
575  //Check HTTP request state
576  if(context->requestState == ACME_REQ_STATE_INIT)
577  {
578  //Debug message
579  TRACE_DEBUG("\r\n");
580  TRACE_DEBUG("###############################################################################\r\n");
581  TRACE_DEBUG("## KEY CHANGE #################################################################\r\n");
582  TRACE_DEBUG("###############################################################################\r\n");
583  TRACE_DEBUG("\r\n");
584 
585  //Update HTTP request state
586  context->requestState = ACME_REQ_STATE_FORMAT_BODY;
587  }
588  else if(context->requestState == ACME_REQ_STATE_FORMAT_BODY)
589  {
590  //Format the body of the HTTP request
591  error = acmeClientFormatKeyChangeRequest(context, publicKey,
592  publicKeyLen, privateKey, privateKeyLen, password);
593 
594  //Check status code
595  if(!error)
596  {
597  //Update HTTP request state
598  context->requestState = ACME_REQ_STATE_FORMAT_HEADER;
599  }
600  }
601  else if(context->requestState == ACME_REQ_STATE_FORMAT_HEADER)
602  {
603  //A client can change the public key that is associated with an
604  //account, by sending a POST request to the server's keyChange
605  //URL (refer to RFC 8555, section 7.3.5)
606  error = acmeClientFormatRequestHeader(context, "POST",
607  context->directory.keyChange);
608 
609  //Check status code
610  if(!error)
611  {
612  //Update HTTP request state
613  context->requestState = ACME_REQ_STATE_SEND_HEADER;
614  }
615  }
616  else if(context->requestState == ACME_REQ_STATE_SEND_HEADER ||
617  context->requestState == ACME_REQ_STATE_SEND_BODY ||
618  context->requestState == ACME_REQ_STATE_RECEIVE_HEADER ||
619  context->requestState == ACME_REQ_STATE_PARSE_HEADER ||
620  context->requestState == ACME_REQ_STATE_RECEIVE_BODY ||
621  context->requestState == ACME_REQ_STATE_CLOSE_BODY)
622  {
623  //Perform HTTP request/response transaction
624  error = acmeClientSendRequest(context);
625  }
626  else if(context->requestState == ACME_REQ_STATE_PARSE_BODY)
627  {
628  //Parse the body of the HTTP response
629  error = acmeClientParseKeyChangeResponse(context);
630 
631  //The HTTP transaction is complete
632  context->requestState = ACME_REQ_STATE_INIT;
633  break;
634  }
635  else
636  {
637  //Invalid state
638  error = ERROR_WRONG_STATE;
639  }
640  }
641 
642  //Return status code
643  return error;
644 }
645 
646 
647 /**
648  * @brief Format HTTP request body (keyChange URL)
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  int_t ret;
665  size_t n;
666  char_t *protected;
667  char_t *payload;
668  char_t *keyChange;
669  json_t *keyChangeObj;
670  AcmeKeyPair newAccountKey;
671 
672  //Export the old public key to JWK format
673  error = acmeClientFormatJwk(&context->accountKey, context->buffer, &n,
674  FALSE);
675  //Any error to report?
676  if(error)
677  return error;
678 
679  //Load the new account key
680  error = acmeClientLoadKeyPair(&newAccountKey, publicKey, publicKeyLen,
681  privateKey, privateKeyLen, password);
682  //Any error to report?
683  if(error)
684  return error;
685 
686  //To create this request object, the client first constructs a keyChange
687  //object describing the account to be updated and its account key (refer
688  //to RFC 8555, section 7.3.5)
689  keyChangeObj = json_object();
690 
691  //The "account" field contains the URL for the account being modified
692  ret = json_object_set_new(keyChangeObj, "account",
693  json_string(context->account.url));
694 
695  //The "oldKey" field contains The JWK representation of the old key
696  ret |= json_object_set_new(keyChangeObj, "oldKey",
697  json_loads(context->buffer, 0, NULL));
698 
699  //JSON object successfully created?
700  if(ret == 0)
701  {
702  //Generate the JSON representation of the payload object
703  keyChange = json_dumps(keyChangeObj, JSON_COMPACT);
704  }
705  else
706  {
707  //An error occurred during processing
708  keyChange = NULL;
709  }
710 
711  //Valid JSON representation?
712  if(keyChange != NULL)
713  {
714  //Point to the buffer where to format the JWS protected header
715  protected = context->buffer;
716 
717  //The inner JWS must have a "jwk" header parameter containing the public
718  //key of the new key pair, must have the same "url" header parameter as
719  //the outer JWS and must omit the "nonce" header parameter
720  error = acmeClientFormatJwsProtectedHeader(&newAccountKey, NULL, NULL,
721  context->directory.keyChange, protected, &n);
722 
723  //Check status code
724  if(!error)
725  {
726  //The inner JWS is signed with the requested new account key
727  error = jwsCreate(context->prngAlgo, context->prngContext, protected,
728  keyChange, newAccountKey.alg, newAccountKey.privateKey,
729  context->buffer, &n);
730  }
731 
732  //Release JSON string
733  jsonp_free(keyChange);
734  }
735  else
736  {
737  //Report an error
738  error = ERROR_FAILURE;
739  }
740 
741  //Release JSON object
742  json_decref(keyChangeObj);
743 
744  //Unload the new account key
745  acmeClientUnloadKeyPair(&newAccountKey);
746 
747  //Check status code)
748  if(!error)
749  {
750  //The inner JWS becomes the payload for the outer JWS that is the body
751  //of the ACME request
752  payload = jsonp_strdup(context->buffer);
753 
754  //Valid JSON representation?
755  if(payload != NULL)
756  {
757  //Point to the buffer where to format the JWS protected header
758  protected = context->buffer;
759 
760  //The outer JWS must meet the normal requirements for an ACME JWS
761  //request body (refer to RFC 8555, section 7.3.5)
762  error = acmeClientFormatJwsProtectedHeader(&context->accountKey,
763  context->account.url, context->nonce, context->directory.keyChange,
764  protected, &n);
765 
766  //Check status code
767  if(!error)
768  {
769  //Generate the outer JWS
770  error = jwsCreate(context->prngAlgo, context->prngContext,
771  protected, payload, context->accountKey.alg,
772  context->accountKey.privateKey, context->buffer,
773  &context->bufferLen);
774  }
775 
776  //Release JSON string
777  jsonp_free(payload);
778  }
779  else
780  {
781  //Report an error
782  error = ERROR_FAILURE;
783  }
784  }
785 
786  //Return status code
787  return error;
788 }
789 
790 
791 /**
792  * @brief Parse HTTP response (keyChange URL)
793  * @param[in] context Pointer to the ACME client context
794  * @return Error code
795  **/
796 
798 {
799  //Check HTTP status code
800  if(!HTTP_STATUS_CODE_2YZ(context->statusCode))
802 
803  //The server must include a Replay-Nonce header field in every successful
804  //response to a POST request (refer to RFC 8555, section 6.5)
805  if(context->nonce[0] == '\0')
806  return ERROR_INVALID_RESPONSE;
807 
808  //Successful processing
809  return NO_ERROR;
810 }
811 
812 #endif
int bool_t
Definition: compiler_port.h:61
error_t acmeFormatUpdateAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params)
Format HTTP request body (account URL)
signed int int_t
Definition: compiler_port.h:56
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 HTTP_STATUS_CODE_2YZ(code)
Definition: http_common.h:44
Helper functions for ACME client.
@ ACME_REQ_STATE_PARSE_BODY
Definition: acme_client.h:297
error_t acmeClientParseUpdateAccountResponse(AcmeClientContext *context)
Parse HTTP response (account URL)
Account object management.
JOSE (JSON Object Signing and Encryption)
error_t acmeClientFormatKeyChangeRequest(AcmeClientContext *context, const char_t *publicKey, size_t publicKeyLen, const char_t *privateKey, size_t privateKeyLen, const char_t *password)
Format HTTP request body (keyChange URL)
ACME account creation parameters.
Definition: acme_client.h:439
@ ERROR_WRONG_STATE
Definition: error.h:210
@ ACME_REQ_STATE_FORMAT_HEADER
Definition: acme_client.h:290
error_t acmeClientSendRequest(AcmeClientContext *context)
Send HTTP request.
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)
#define FALSE
Definition: os_port.h:46
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
@ ERROR_UNEXPECTED_STATUS
Definition: error.h:284
error_t acmeClientFormatRequestHeader(AcmeClientContext *context, const char_t *method, const char_t *url)
Format HTTP request header.
error_t
Error codes.
Definition: error.h:43
error_t acmeClientParseKeyChangeResponse(AcmeClientContext *context)
Parse HTTP response (keyChange URL)
#define osSprintf(dest,...)
Definition: os_port.h:234
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_FAILURE
Generic error code.
Definition: error.h:45
@ ACME_REQ_STATE_CLOSE_BODY
Definition: acme_client.h:298
uint_t numContacts
Number of contact URLs.
Definition: acme_client.h:440
@ ACME_REQ_STATE_INIT
Definition: acme_client.h:289
error_t acmeClientFormatJwk(const AcmeKeyPair *keyPair, char_t *buffer, size_t *written, bool_t sort)
Export a public key to JWK format.
Public/private key pair.
Definition: acme_client.h:413
@ ACME_REQ_STATE_FORMAT_BODY
Definition: acme_client.h:292
const char_t * status
Status of the account.
Definition: acme_client.h:448
@ ACME_REQ_STATE_RECEIVE_HEADER
Definition: acme_client.h:294
#define TRACE_DEBUG(...)
Definition: debug.h:119
char char_t
Definition: compiler_port.h:55
#define AcmeClientContext
Definition: acme_client.h:248
error_t acmeClientSendUpdateAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params)
Send HTTP request (account URL)
uint8_t n
const char_t * contacts[ACME_CLIENT_MAX_CONTACTS]
Array of URLs that the server can use to contact the client.
Definition: acme_client.h:441
uint8_t payload[]
Definition: ipv6.h:286
error_t acmeClientSendNewAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params, bool_t onlyReturnExisting)
Send HTTP request (newAccount URL)
@ ACME_REQ_STATE_SEND_HEADER
Definition: acme_client.h:291
char_t alg[8]
Definition: acme_client.h:415
@ ACME_REQ_STATE_PARSE_HEADER
Definition: acme_client.h:295
#define ACME_CLIENT_MAX_CONTACTS
Definition: acme_client.h:152
error_t jwsCreate(const PrngAlgo *prngAlgo, void *prngContext, const char_t *protected, const char_t *payload, const char_t *alg, const void *privateKey, char_t *buffer, size_t *written)
Create a JSON Web Signature.
void acmeClientUnloadKeyPair(AcmeKeyPair *keyPair)
Unload public/private key pair.
@ ACME_REQ_STATE_RECEIVE_BODY
Definition: acme_client.h:296
unsigned int uint_t
Definition: compiler_port.h:57
bool_t termsOfServiceAgreed
Indicates the client's agreement with the terms of service.
Definition: acme_client.h:442
@ ACME_REQ_STATE_SEND_BODY
Definition: acme_client.h:293
const void * privateKey
Definition: acme_client.h:417
@ ERROR_INVALID_RESPONSE
Definition: error.h:71
error_t acmeClientFormatNewAccountRequest(AcmeClientContext *context, const AcmeAccountParams *params, bool_t onlyReturnExisting)
Format HTTP request body (newAccount URL)
ACME client (Automatic Certificate Management Environment)
error_t acmeClientCheckAccountParams(const AcmeAccountParams *params)
Check account information.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
error_t acmeClientParseNewAccountResponse(AcmeClientContext *context)
Parse HTTP response (newAccount URL)