acme_client_order.c
Go to the documentation of this file.
1 /**
2  * @file acme_client_order.c
3  * @brief Order 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"
36 #include "acme/acme_client_order.h"
37 #include "acme/acme_client_jose.h"
38 #include "acme/acme_client_misc.h"
39 #include "encoding/base64url.h"
40 #include "jansson.h"
41 #include "jansson_private.h"
42 #include "debug.h"
43 
44 //Check TCP/IP stack configuration
45 #if (ACME_CLIENT_SUPPORT == ENABLED)
46 
47 
48 /**
49  * @brief Check certificate order information
50  * @param[in] params Certificate order information
51  * @return Error code
52  **/
53 
55 {
56  uint_t i;
57 
58  //Sanity check
59  if(params == NULL)
61 
62  //Check the number of domains
63  if(params->numDomains == 0 ||
65  {
67  }
68 
69  //Loop through the list of domains
70  for(i = 0; i < params->numDomains; i++)
71  {
72  //Each item must contain a valid domain name
73  if(params->domains[i].name == NULL)
75 
76  //Check the length of the domain name
79 
80 #if (ACME_CLIENT_HTTP_CHALLENGE_SUPPORT == ENABLED)
81  //HTTP validation method?
83  {
84  //The challenge type is valid
85  }
86  else
87 #endif
88 #if (ACME_CLIENT_DNS_CHALLENGE_SUPPORT == ENABLED)
89  //DNS validation method?
91  {
92  //The challenge type is valid
93  }
94  else
95 #endif
96 #if (ACME_CLIENT_TLS_ALPN_CHALLENGE_SUPPORT == ENABLED)
97  //TLS with ALPN validation method?
99  {
100  //The challenge type is valid
101  }
102  else
103 #endif
104  //Invalid challenge type?
105  {
106  //Report an error
108  }
109  }
110 
111  //The certificate public key is required
112  if(params->publicKey == NULL || params->publicKeyLen == 0)
114 
115  //The certificate private key is required
116  if(params->privateKey == NULL || params->privateKeyLen == 0)
118 
119  //The account parameters are valid
120  return NO_ERROR;
121 }
122 
123 
124 /**
125  * @brief Initialize order object
126  * @param[in] context Pointer to the ACME client context
127  * @param[in] params Certificate order information
128  * @return Error code
129  **/
130 
132  const AcmeOrderParams *params)
133 {
134  error_t error;
135  uint_t i;
136 
137  //Clear order object
138  osMemset(&context->order, 0, sizeof(AcmeOrder));
139  context->numIdentifiers = 0;
140  context->numAuthorizations = 0;
141  context->numChallenges = 0;
142 
143  //Clear identifier, authorization and challenge objects
144  for(i = 0; i < ACME_CLIENT_MAX_DOMAINS; i++)
145  {
146  osMemset(&context->identifiers[i], 0, sizeof(AcmeIdentifier));
147  osMemset(&context->authorizations[i], 0, sizeof(AcmeAuthorization));
148  osMemset(&context->challenges[i], 0, sizeof(AcmeChallenge));
149  }
150 
151  //An order may contain multiple identifiers
152  context->numIdentifiers = params->numDomains;
153 
154  //Save identifiers
155  for(i = 0; i < params->numDomains; i++)
156  {
157  //Save identifier value
158  osStrcpy(context->identifiers[i].value, params->domains[i].name);
159  //Save challenge type
160  context->identifiers[i].challengeType = params->domains[i].challengeType;
161  }
162 
163  //Release the current key pair, if any
164  acmeClientUnloadKeyPair(&context->certKey);
165 
166  //The public and private keys are encoded in PEM format
167  error = acmeClientLoadKeyPair(&context->certKey, params->publicKey,
168  params->publicKeyLen, params->privateKey, params->privateKeyLen);
169 
170  //Return status code
171  return error;
172 }
173 
174 
175 /**
176  * @brief Send HTTP request (newOrder URL)
177  * @param[in] context Pointer to the ACME client context
178  * @param[in] params Certificate order information
179  * @return Error code
180  **/
181 
183  const AcmeOrderParams *params)
184 {
185  error_t error;
186 
187  //Initialize variables
188  error = NO_ERROR;
189 
190  //Perform HTTP request
191  while(!error)
192  {
193  //Check HTTP request state
194  if(context->requestState == ACME_REQ_STATE_INIT)
195  {
196  //Debug message
197  TRACE_DEBUG("\r\n");
198  TRACE_DEBUG("###############################################################################\r\n");
199  TRACE_DEBUG("## NEW ORDER ##################################################################\r\n");
200  TRACE_DEBUG("###############################################################################\r\n");
201  TRACE_DEBUG("\r\n");
202 
203  //Update HTTP request state
204  context->requestState = ACME_REQ_STATE_FORMAT_BODY;
205  }
206  else if(context->requestState == ACME_REQ_STATE_FORMAT_BODY)
207  {
208  //Format the body of the HTTP request
209  error = acmeClientFormatNewOrderRequest(context, params);
210 
211  //Check status code
212  if(!error)
213  {
214  //Update HTTP request state
215  context->requestState = ACME_REQ_STATE_FORMAT_HEADER;
216  }
217  }
218  else if(context->requestState == ACME_REQ_STATE_FORMAT_HEADER)
219  {
220  //The client begins the certificate issuance process by sending a
221  //POST request to the server's newOrder resource (refer to RFC 8555,
222  //section 7.4)
223  error = acmeClientFormatRequestHeader(context, "POST",
224  context->directory.newOrder);
225 
226  //Check status code
227  if(!error)
228  {
229  //Update HTTP request state
230  context->requestState = ACME_REQ_STATE_SEND_HEADER;
231  }
232  }
233  else if(context->requestState == ACME_REQ_STATE_SEND_HEADER ||
234  context->requestState == ACME_REQ_STATE_SEND_BODY ||
235  context->requestState == ACME_REQ_STATE_RECEIVE_HEADER ||
236  context->requestState == ACME_REQ_STATE_PARSE_HEADER ||
237  context->requestState == ACME_REQ_STATE_RECEIVE_BODY ||
238  context->requestState == ACME_REQ_STATE_CLOSE_BODY)
239  {
240  //Perform HTTP request/response transaction
241  error = acmeClientSendRequest(context);
242  }
243  else if(context->requestState == ACME_REQ_STATE_PARSE_BODY)
244  {
245  //Parse the body of the HTTP response
246  error = acmeClientParseNewOrderResponse(context);
247 
248  //The HTTP transaction is complete
249  context->requestState = ACME_REQ_STATE_INIT;
250  break;
251  }
252  else
253  {
254  //Invalid state
255  error = ERROR_WRONG_STATE;
256  }
257  }
258 
259  //Return status code
260  return error;
261 }
262 
263 
264 /**
265  * @brief Format HTTP request body (newOrder URL)
266  * @param[in] context Pointer to the ACME client context
267  * @param[in] params Certificate order information
268  * @return Error code
269  **/
270 
272  const AcmeOrderParams *params)
273 {
274  error_t error;
275  int_t ret;
276  uint_t i;
277  size_t n;
278  char_t *protected;
279  char_t *payload;
280  json_t *payloadObj;
281  json_t *identifierObj;
282  json_t *identifiersObj;
283 
284  //Initialize status code
285  ret = 0;
286 
287  //Initialize JSON objects
288  payloadObj = json_object();
289  identifiersObj = json_array();
290 
291  //The body of the POST contains an array of identifier objects that the
292  //client wishes to submit an order for (refer to RFC 8555, section 7.4)
293  for(i = 0; i < params->numDomains; i++)
294  {
295  //Initialize JSON object
296  identifierObj = json_object();
297 
298  //Set the type of identifier
299  ret |= json_object_set_new(identifierObj, "type", json_string("dns"));
300 
301  //Set the identifier itself
302  ret |= json_object_set_new(identifierObj, "value",
303  json_string(params->domains[i].name));
304 
305  //Add the identifier object to the array
306  ret |= json_array_append_new(identifiersObj, identifierObj);
307  }
308 
309  //Add the "identifiers" field to the payload
310  ret |= json_object_set_new(payloadObj, "identifiers", identifiersObj);
311 
312  //The client's request may specify the value of the notBefore field in
313  //the certificate
314  if(params->notBefore.year > 0 && params->notBefore.month > 0 &&
315  params->notBefore.day > 0)
316  {
317  //The date format is specified by RFC 3339
318  osSprintf(context->buffer, "%04u-%02u-%02uT%02u:%02u:%02uZ",
319  params->notBefore.year, params->notBefore.month,
320  params->notBefore.day, params->notBefore.hours,
321  params->notBefore.minutes, params->notBefore.seconds);
322 
323  //Add the "notBefore" field to the payload
324  ret |= json_object_set_new(payloadObj, "notBefore",
325  json_string(context->buffer));
326  }
327 
328  //The client's request may specify the value of the notAfter field in
329  //the certificate
330  if(params->notAfter.year > 0 && params->notAfter.month > 0 &&
331  params->notAfter.day > 0)
332  {
333  //The date format is specified by RFC 3339
334  osSprintf(context->buffer, "%04u-%02u-%02uT%02u:%02u:%02uZ",
335  params->notAfter.year, params->notAfter.month,
336  params->notAfter.day, params->notAfter.hours,
337  params->notAfter.minutes, params->notAfter.seconds);
338 
339  //Add the "notAfter" field to the payload
340  ret |= json_object_set_new(payloadObj, "notAfter",
341  json_string(context->buffer));
342  }
343 
344  //JSON object successfully created?
345  if(ret == 0)
346  {
347  //Generate the JSON representation of the payload object
348  payload = json_dumps(payloadObj, JSON_COMPACT);
349  }
350  else
351  {
352  //An error occurred during processing
353  payload = NULL;
354  }
355 
356  //Valid JSON representation?
357  if(payload != NULL)
358  {
359  //Point to the buffer where to format the JWS protected header
360  protected = context->buffer;
361 
362  //Format JWS protected header
363  error = acmeClientFormatJwsProtectedHeader(&context->accountKey,
364  context->account.url, context->nonce, context->directory.newOrder,
365  protected, &n);
366 
367  //Check status code
368  if(!error)
369  {
370  //Generate the JSON Web Signature
371  error = jwsCreate(context->prngAlgo, context->prngContext, protected,
372  payload, context->accountKey.alg, context->accountKey.crv,
373  context->accountKey.privateKey, context->buffer, &context->bufferLen);
374  }
375 
376  //Release JSON string
377  jsonp_free(payload);
378  }
379  else
380  {
381  //Report an error
382  error = ERROR_FAILURE;
383  }
384 
385  //Release JSON object
386  json_decref(payloadObj);
387 
388  //Return status code
389  return error;
390 }
391 
392 
393 /**
394  * @brief Parse HTTP response (newOrder URL)
395  * @param[in] context Pointer to the ACME client context
396  * @return Error code
397  **/
398 
400 {
401  error_t error;
402  uint_t i;
403  uint_t n;
404  const char_t *status;
405  const char_t *authorization;
406  const char_t *finalize;
407  const char_t *certificate;
408  json_t *rootObj;
409  json_t *statusObj;
410  json_t *arrayObj;
411  json_t *authorizationObj;
412  json_t *finalizeObj;
413  json_t *certificateObj;
414 
415  //Check HTTP status code
416  if(!HTTP_STATUS_CODE_2YZ(context->statusCode))
418 
419  //The server must include a Replay-Nonce header field in every successful
420  //response to a POST request (refer to RFC 8555, section 6.5)
421  if(context->nonce[0] == '\0')
422  return ERROR_INVALID_RESPONSE;
423 
424  //The response header must contain a valid Location HTTP header field
425  if(context->order.url[0] == '\0')
426  return ERROR_INVALID_RESPONSE;
427 
428  //Invalid media type?
429  if(osStrcasecmp(context->contentType, "application/json"))
430  return ERROR_INVALID_RESPONSE;
431 
432  //Check whether the body of the response is truncated
433  if(context->bufferLen >= ACME_CLIENT_BUFFER_SIZE)
435 
436  //Initialize status code
437  error = ERROR_INVALID_RESPONSE;
438 
439  //Decode JSON string
440  rootObj = json_loads(context->buffer, 0, NULL);
441 
442  //Start of exception handling block
443  do
444  {
445  //Any parsing error?
446  if(!json_is_object(rootObj))
447  break;
448 
449  //Get "status" object
450  statusObj = json_object_get(rootObj, "status");
451 
452  //The object must be a valid string
453  if(!json_is_string(statusObj))
454  break;
455 
456  //Get the value of the string
457  status = json_string_value(statusObj);
458  //Retrieve the status of the order
459  context->order.status = acmeClientParseOrderStatus(status);
460 
461  //Get "authorizations" object
462  arrayObj = json_object_get(rootObj, "authorizations");
463 
464  //The object must be a valid array
465  if(!json_is_array(arrayObj))
466  break;
467 
468  //Retrieve the numbers of items in the array
469  n = json_array_size(arrayObj);
470  //Limit the numbers of authorizations
472 
473  //Loop through the list of authorizations
474  for(i = 0; i < n; i++)
475  {
476  //Point to the current authorization
477  authorizationObj = json_array_get(arrayObj, i);
478 
479  //The object must be a valid string
480  if(!json_is_string(authorizationObj))
481  break;
482 
483  //Retrieve the value of the string
484  authorization = json_string_value(authorizationObj);
485 
486  //Check the length of the string
487  if(osStrlen(authorization) > ACME_CLIENT_MAX_URL_LEN)
488  break;
489 
490  //Save the authorization URL
491  osStrcpy(context->authorizations[i].url, authorization);
492 
493  //Increment the number of authorizations
494  context->numAuthorizations++;
495  }
496 
497  //Any parsing error?
498  if(i < n)
499  break;
500 
501  //Get "finalize" object
502  finalizeObj = json_object_get(rootObj, "finalize");
503 
504  //The object must be a valid string
505  if(!json_is_string(finalizeObj))
506  break;
507 
508  //Retrieve the value of the string
509  finalize = json_string_value(finalizeObj);
510 
511  //Check the length of the string
512  if(osStrlen(finalize) > ACME_CLIENT_MAX_URL_LEN)
513  break;
514 
515  //Save the finalize URL
516  osStrcpy(context->order.finalize, finalize);
517 
518  //If the status of the order is "valid", the server has issued the
519  //certificate and provisioned its URL to the "certificate" field of
520  //the order
521  if(context->order.status == ACME_ORDER_STATUS_VALID)
522  {
523  //Get "certificate" object
524  certificateObj = json_object_get(rootObj, "certificate");
525 
526  //The object must be a valid string
527  if(!json_is_string(certificateObj))
528  break;
529 
530  //Retrieve the value of the string
531  certificate = json_string_value(certificateObj);
532 
533  //Check the length of the string
534  if(osStrlen(certificate) > ACME_CLIENT_MAX_URL_LEN)
535  break;
536 
537  //Save the certificate URL
538  osStrcpy(context->order.certificate, certificate);
539  }
540 
541  //Successful parsing
542  error = NO_ERROR;
543 
544  //End of exception handling block
545  } while(0);
546 
547  //Release JSON object
548  json_decref(rootObj);
549 
550  //Return status code
551  return error;
552 }
553 
554 
555 /**
556  * @brief Send HTTP request (order URL)
557  * @param[in] context Pointer to the ACME client context
558  * @return Error code
559  **/
560 
562 {
563  error_t error;
564 
565  //Initialize variables
566  error = NO_ERROR;
567 
568  //Perform HTTP request
569  while(!error)
570  {
571  //Check HTTP request state
572  if(context->requestState == ACME_REQ_STATE_INIT)
573  {
574  //Debug message
575  TRACE_DEBUG("\r\n");
576  TRACE_DEBUG("###############################################################################\r\n");
577  TRACE_DEBUG("## ORDER STATUS ###############################################################\r\n");
578  TRACE_DEBUG("###############################################################################\r\n");
579  TRACE_DEBUG("\r\n");
580 
581  //Update HTTP request state
582  context->requestState = ACME_REQ_STATE_FORMAT_BODY;
583  }
584  else if(context->requestState == ACME_REQ_STATE_FORMAT_BODY)
585  {
586  //Format the body of the HTTP request
587  error = acmeClientFormatOrderStatusRequest(context);
588 
589  //Check status code
590  if(!error)
591  {
592  //Update HTTP request state
593  context->requestState = ACME_REQ_STATE_FORMAT_HEADER;
594  }
595  }
596  else if(context->requestState == ACME_REQ_STATE_FORMAT_HEADER)
597  {
598  //The client should then send a POST-as-GET request to the order
599  //resource to obtain its current state refer to RFC 8555, section 7.4)
600  error = acmeClientFormatRequestHeader(context, "POST",
601  context->order.url);
602 
603  //Check status code
604  if(!error)
605  {
606  //Update HTTP request state
607  context->requestState = ACME_REQ_STATE_SEND_HEADER;
608  }
609  }
610  else if(context->requestState == ACME_REQ_STATE_SEND_HEADER ||
611  context->requestState == ACME_REQ_STATE_SEND_BODY ||
612  context->requestState == ACME_REQ_STATE_RECEIVE_HEADER ||
613  context->requestState == ACME_REQ_STATE_PARSE_HEADER ||
614  context->requestState == ACME_REQ_STATE_RECEIVE_BODY ||
615  context->requestState == ACME_REQ_STATE_CLOSE_BODY)
616  {
617  //Perform HTTP request/response transaction
618  error = acmeClientSendRequest(context);
619  }
620  else if(context->requestState == ACME_REQ_STATE_PARSE_BODY)
621  {
622  //Parse the body of the HTTP response
623  error = acmeClientParseOrderStatusResponse(context);
624 
625  //The HTTP transaction is complete
626  context->requestState = ACME_REQ_STATE_INIT;
627  break;
628  }
629  else
630  {
631  //Invalid state
632  error = ERROR_WRONG_STATE;
633  }
634  }
635 
636  //Return status code
637  return error;
638 }
639 
640 
641 /**
642  * @brief Format HTTP request body (order URL)
643  * @param[in] context Pointer to the ACME client context
644  * @return Error code
645  **/
646 
648 {
649  error_t error;
650  size_t n;
651  char_t *protected;
652  const char_t *payload;
653 
654  //The payload field is empty for POST-as-GET requests
655  payload = "";
656 
657  //Point to the buffer where to format the JWS protected header
658  protected = context->buffer;
659 
660  //Format JWS protected header
661  error = acmeClientFormatJwsProtectedHeader(&context->accountKey,
662  context->account.url, context->nonce, context->order.url, protected, &n);
663 
664  //Check status code
665  if(!error)
666  {
667  //Generate the JSON Web Signature
668  error = jwsCreate(context->prngAlgo, context->prngContext, protected,
669  payload, context->accountKey.alg, context->accountKey.crv,
670  context->accountKey.privateKey, context->buffer, &context->bufferLen);
671  }
672 
673  //Return status code
674  return error;
675 }
676 
677 
678 /**
679  * @brief Parse HTTP response (order URL)
680  * @param[in] context Pointer to the ACME client context
681  * @return Error code
682  **/
683 
685 {
686  error_t error;
687  const char_t *status;
688  const char_t *certificate;
689  json_t *rootObj;
690  json_t *statusObj;
691  json_t *certificateObj;
692 
693  //Check HTTP status code
694  if(!HTTP_STATUS_CODE_2YZ(context->statusCode))
696 
697  //The server must include a Replay-Nonce header field in every successful
698  //response to a POST request (refer to RFC 8555, section 6.5)
699  if(context->nonce[0] == '\0')
700  return ERROR_INVALID_RESPONSE;
701 
702  //Invalid media type?
703  if(osStrcasecmp(context->contentType, "application/json"))
704  return ERROR_INVALID_RESPONSE;
705 
706  //Check whether the body of the response is truncated
707  if(context->bufferLen >= ACME_CLIENT_BUFFER_SIZE)
709 
710  //Initialize status code
711  error = ERROR_INVALID_RESPONSE;
712 
713  //Decode JSON string
714  rootObj = json_loads(context->buffer, 0, NULL);
715 
716  //Start of exception handling block
717  do
718  {
719  //Any parsing error?
720  if(!json_is_object(rootObj))
721  break;
722 
723  //Get "status" object
724  statusObj = json_object_get(rootObj, "status");
725 
726  //The object must be a valid string
727  if(!json_is_string(statusObj))
728  break;
729 
730  //Get the value of the string
731  status = json_string_value(statusObj);
732  //Retrieve the status of the order
733  context->order.status = acmeClientParseOrderStatus(status);
734 
735  //If the status of the order is "valid", the server has issued the
736  //certificate and provisioned its URL to the "certificate" field of
737  //the order
738  if(context->order.status == ACME_ORDER_STATUS_VALID)
739  {
740  //Get "certificate" object
741  certificateObj = json_object_get(rootObj, "certificate");
742 
743  //The object must be a valid string
744  if(!json_is_string(certificateObj))
745  break;
746 
747  //Retrieve the value of the string
748  certificate = json_string_value(certificateObj);
749 
750  //Check the length of the string
751  if(osStrlen(certificate) > ACME_CLIENT_MAX_URL_LEN)
752  break;
753 
754  //Save the certificate URL
755  osStrcpy(context->order.certificate, certificate);
756  }
757 
758  //Successful parsing
759  error = NO_ERROR;
760 
761  //End of exception handling block
762  } while(0);
763 
764  //Release JSON object
765  json_decref(rootObj);
766 
767  //Return status code
768  return error;
769 }
770 
771 
772 /**
773  * @brief Send HTTP request (order's finalize URL)
774  * @param[in] context Pointer to the ACME client context
775  * @return Error code
776  **/
777 
779 {
780  error_t error;
781 
782  //Initialize variables
783  error = NO_ERROR;
784 
785  //Perform HTTP request
786  while(!error)
787  {
788  //Check HTTP request state
789  if(context->requestState == ACME_REQ_STATE_INIT)
790  {
791  //Debug message
792  TRACE_DEBUG("\r\n");
793  TRACE_DEBUG("###############################################################################\r\n");
794  TRACE_DEBUG("## FINALIZE ORDER #############################################################\r\n");
795  TRACE_DEBUG("###############################################################################\r\n");
796  TRACE_DEBUG("\r\n");
797 
798  //Update HTTP request state
799  context->requestState = ACME_REQ_STATE_FORMAT_BODY;
800  }
801  else if(context->requestState == ACME_REQ_STATE_FORMAT_BODY)
802  {
803  //Format the body of the HTTP request
804  error = acmeClientFormatFinalizeOrderRequest(context);
805 
806  //Check status code
807  if(!error)
808  {
809  //Update HTTP request state
810  context->requestState = ACME_REQ_STATE_FORMAT_HEADER;
811  }
812  }
813  else if(context->requestState == ACME_REQ_STATE_FORMAT_HEADER)
814  {
815  //Once the client believes it has fulfilled the server's requirements,
816  //it should send a POST request to the order resource's finalize URL.
817  //The POST body MUST include a CSR (refer to RFC 8555, section 7.4)
818  error = acmeClientFormatRequestHeader(context, "POST",
819  context->order.finalize);
820 
821  //Check status code
822  if(!error)
823  {
824  //Update HTTP request state
825  context->requestState = ACME_REQ_STATE_SEND_HEADER;
826  }
827  }
828  else if(context->requestState == ACME_REQ_STATE_SEND_HEADER ||
829  context->requestState == ACME_REQ_STATE_SEND_BODY ||
830  context->requestState == ACME_REQ_STATE_RECEIVE_HEADER ||
831  context->requestState == ACME_REQ_STATE_PARSE_HEADER ||
832  context->requestState == ACME_REQ_STATE_RECEIVE_BODY ||
833  context->requestState == ACME_REQ_STATE_CLOSE_BODY)
834  {
835  //Perform HTTP request/response transaction
836  error = acmeClientSendRequest(context);
837  }
838  else if(context->requestState == ACME_REQ_STATE_PARSE_BODY)
839  {
840  //Parse the body of the HTTP response
841  error = acmeClientParseFinalizeOrderResponse(context);
842 
843  //The HTTP transaction is complete
844  context->requestState = ACME_REQ_STATE_INIT;
845  break;
846  }
847  else
848  {
849  //Invalid state
850  error = ERROR_WRONG_STATE;
851  }
852  }
853 
854  //Return status code
855  return error;
856 }
857 
858 
859 /**
860  * @brief Format HTTP request body (order's finalize URL)
861  * @param[in] context Pointer to the ACME client context
862  * @return Error code
863  **/
864 
866 {
867  error_t error;
868  int_t ret;
869  size_t n;
870  char_t *protected;
871  char_t *payload;
872  json_t *payloadObj;
873 
874  //Any registered callback?
875  if(context->csrCallback != NULL)
876  {
877  //Invoke user callback function
878  error = context->csrCallback(context, (uint8_t *) context->buffer,
880  }
881  else
882  {
883  //Generate the certificate signing request
884  error = acmeClientGenerateCsr(context, (uint8_t *) context->buffer, &n);
885  }
886 
887  //Any error to report?
888  if(error)
889  return error;
890 
891  //The CSR is sent in the Base64url-encoded version of the DER format
892  base64urlEncode(context->buffer, n, context->buffer, &n);
893 
894  //Initialize JSON object
895  payloadObj = json_object();
896 
897  //The POST body must include a CSR (refer to RFC 8555, section 7.4)
898  ret = json_object_set_new(payloadObj, "csr", json_string(context->buffer));
899 
900  //JSON object successfully created?
901  if(ret == 0)
902  {
903  //Generate the JSON representation of the payload object
904  payload = json_dumps(payloadObj, JSON_COMPACT);
905  }
906  else
907  {
908  //An error occurred during processing
909  payload = NULL;
910  }
911 
912  //Valid JSON representation?
913  if(payload != NULL)
914  {
915  //Point to the buffer where to format the JWS protected header
916  protected = context->buffer;
917 
918  //Format JWS protected header
919  error = acmeClientFormatJwsProtectedHeader(&context->accountKey,
920  context->account.url, context->nonce, context->order.finalize,
921  protected, &n);
922 
923  //Check status code
924  if(!error)
925  {
926  //Generate the JSON Web Signature
927  error = jwsCreate(context->prngAlgo, context->prngContext, protected,
928  payload, context->accountKey.alg, context->accountKey.crv,
929  context->accountKey.privateKey, context->buffer, &context->bufferLen);
930  }
931 
932  //Release JSON string
933  jsonp_free(payload);
934  }
935  else
936  {
937  //Report an error
938  error = ERROR_FAILURE;
939  }
940 
941  //Release JSON object
942  json_decref(payloadObj);
943 
944  //Return status code
945  return error;
946 }
947 
948 
949 /**
950  * @brief Parse HTTP response (order's finalize URL)
951  * @param[in] context Pointer to the ACME client context
952  * @return Error code
953  **/
954 
956 {
957  //Check HTTP status code
958  if(!HTTP_STATUS_CODE_2YZ(context->statusCode))
960 
961  //The server must include a Replay-Nonce header field in every successful
962  //response to a POST request (refer to RFC 8555, section 6.5)
963  if(context->nonce[0] == '\0')
964  return ERROR_INVALID_RESPONSE;
965 
966  //Successful processing
967  return NO_ERROR;
968 }
969 
970 /**
971  * @brief Parse order status field
972  * @param[in] label Textual representation of the status
973  * @return Order status code
974  **/
975 
977 {
978  AcmeOrderStatus status;
979 
980  //Check the status of the order (refer to RFC 8555, section 7.1.6)
981  if(!osStrcmp(label, "pending"))
982  {
983  // Order objects are created in the "pending" state
984  status = ACME_ORDER_STATUS_PENDING;
985  }
986  else if(!osStrcmp(label, "ready"))
987  {
988  //Once all of the authorizations listed in the order object are in the
989  //"valid" state, the order transitions to the "ready" state
990  status = ACME_ORDER_STATUS_READY;
991  }
992  else if(!osStrcmp(label, "processing"))
993  {
994  //The order moves to the "processing" state after the client submits a
995  //request to the order's "finalize" URL
997  }
998  else if(!osStrcmp(label, "valid"))
999  {
1000  //Once the certificate is issued, the order enters the "valid" state
1001  status = ACME_ORDER_STATUS_VALID;
1002  }
1003  else if(!osStrcmp(label, "invalid"))
1004  {
1005  //The order also moves to the "invalid" state if it expires or one of
1006  //itS authorizations enters a final state other than "valid" ("expired",
1007  //"revoked", or "deactivated")
1008  status = ACME_ORDER_STATUS_INVALID;
1009  }
1010  else
1011  {
1012  //Unknown status
1013  status = ACME_ORDER_STATUS_INVALID;
1014  }
1015 
1016  //Return current status
1017  return status;
1018 }
1019 
1020 #endif
ACME client (Automatic Certificate Management Environment)
#define ACME_CLIENT_MAX_DOMAINS
Definition: acme_client.h:159
#define ACME_CLIENT_BUFFER_SIZE
Definition: acme_client.h:166
#define ACME_CLIENT_MAX_NAME_LEN
Definition: acme_client.h:173
@ 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_URL_LEN
Definition: acme_client.h:187
@ ACME_CHALLENGE_TYPE_TLS_ALPN_01
Definition: acme_client.h:369
@ ACME_CHALLENGE_TYPE_HTTP_01
Definition: acme_client.h:367
@ ACME_CHALLENGE_TYPE_DNS_01
Definition: acme_client.h:368
#define AcmeClientContext
Definition: acme_client.h:248
AcmeOrderStatus
Order status.
Definition: acme_client.h:320
@ ACME_ORDER_STATUS_PENDING
Definition: acme_client.h:322
@ ACME_ORDER_STATUS_VALID
Definition: acme_client.h:325
@ ACME_ORDER_STATUS_READY
Definition: acme_client.h:323
@ ACME_ORDER_STATUS_PROCESSING
Definition: acme_client.h:324
@ ACME_ORDER_STATUS_INVALID
Definition: acme_client.h:326
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.
error_t acmeClientGenerateCsr(AcmeClientContext *context, uint8_t *buffer, size_t *written)
Generate CSR (Certificate Signing 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 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.
error_t acmeClientSendOrderStatusRequest(AcmeClientContext *context)
Send HTTP request (order URL)
error_t acmeClientSendFinalizeOrderRequest(AcmeClientContext *context)
Send HTTP request (order's finalize URL)
error_t acmeClientFormatOrderStatusRequest(AcmeClientContext *context)
Format HTTP request body (order URL)
error_t acmeClientSendNewOrderRequest(AcmeClientContext *context, const AcmeOrderParams *params)
Send HTTP request (newOrder URL)
error_t acmeClientCheckOrderParams(const AcmeOrderParams *params)
Check certificate order information.
error_t acmeClientParseNewOrderResponse(AcmeClientContext *context)
Parse HTTP response (newOrder URL)
AcmeOrderStatus acmeClientParseOrderStatus(const char_t *label)
Parse order status field.
error_t acmeClientFormatNewOrderRequest(AcmeClientContext *context, const AcmeOrderParams *params)
Format HTTP request body (newOrder URL)
error_t acmeClientInitOrder(AcmeClientContext *context, const AcmeOrderParams *params)
Initialize order object.
error_t acmeClientFormatFinalizeOrderRequest(AcmeClientContext *context)
Format HTTP request body (order's finalize URL)
error_t acmeClientParseOrderStatusResponse(AcmeClientContext *context)
Parse HTTP response (order URL)
error_t acmeClientParseFinalizeOrderResponse(AcmeClientContext *context)
Parse HTTP response (order's finalize URL)
Order object management.
void base64urlEncode(const void *input, size_t inputLen, char_t *output, size_t *outputLen)
Base64url encoding algorithm.
Definition: base64url.c:72
Base64url encoding scheme.
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
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_RESPONSE_TOO_LARGE
Definition: error.h:283
@ 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 osMemset(p, value, length)
Definition: os_port.h:135
#define osStrcmp(s1, s2)
Definition: os_port.h:171
#define osStrcasecmp(s1, s2)
Definition: os_port.h:183
#define MIN(a, b)
Definition: os_port.h:63
#define osStrlen(s)
Definition: os_port.h:165
#define osSprintf(dest,...)
Definition: os_port.h:231
#define osStrcpy(s1, s2)
Definition: os_port.h:207
Authorization object.
Definition: acme_client.h:535
Challenge object.
Definition: acme_client.h:547
AcmeChallengeType challengeType
Challenge type.
Definition: acme_client.h:460
const char_t * name
Domain name.
Definition: acme_client.h:459
Identifier object.
Definition: acme_client.h:511
Order object.
Definition: acme_client.h:522
Certificate order parameters.
Definition: acme_client.h:469
const char_t * privateKey
Certificate private key.
Definition: acme_client.h:476
DateTime notAfter
The requested value of the notAfter field in the certificate.
Definition: acme_client.h:473
uint_t numDomains
Number of domain names.
Definition: acme_client.h:470
const char_t * publicKey
Certificate public key.
Definition: acme_client.h:474
size_t publicKeyLen
Length of the certificate public key, in bytes.
Definition: acme_client.h:475
size_t privateKeyLen
Length of the certificate private key, in bytes.
Definition: acme_client.h:477
AcmeDomainParams domains[ACME_CLIENT_MAX_DOMAINS]
Domain names that the client wishes to submit an order for.
Definition: acme_client.h:471
DateTime notBefore
The requested value of the notBefore field in the certificate.
Definition: acme_client.h:472
uint8_t hours
Definition: date_time.h:52
uint8_t month
Definition: date_time.h:49
uint8_t seconds
Definition: date_time.h:54
uint16_t year
Definition: date_time.h:48
uint8_t day
Definition: date_time.h:50
uint8_t minutes
Definition: date_time.h:53