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