est_client_resp_parse.c
Go to the documentation of this file.
1 /**
2  * @file est_client_resp_parse.c
3  * @brief EST response parsing
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2024-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneEST 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.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL EST_TRACE_LEVEL
33 
34 //Dependencies
35 #include "est/est_client.h"
37 #include "est/est_client_misc.h"
38 #include "pkcs7/pkcs7_parse.h"
39 #include "pkcs7/pkcs7_decrypt.h"
41 #include "pkix/x509_cert_parse.h"
42 #include "pkix/pem_export.h"
43 #include "encoding/base64.h"
44 #include "encoding/asn1.h"
45 #include "encoding/oid.h"
46 #include "str.h"
47 #include "debug.h"
48 
49 //Check crypto library configuration
50 #if (EST_CLIENT_SUPPORT == ENABLED)
51 
52 
53 /**
54  * @brief Parse "cacerts" response
55  * @param[in] context Pointer to the EST client context
56  * @return Error code
57  **/
58 
60 {
61  error_t error;
62  size_t n;
63  size_t length;
64  const uint8_t *data;
65  Asn1Tag tag;
66  Pkcs7ContentInfo contentInfo;
67  Pkcs7SignedData signedData;
68 
69  //Initialize status code
70  error = NO_ERROR;
71 
72  //The EST client should disable use of Implicit TA database entries
73  context->caCertsLen = 0;
74 
75  //The EST client must store the extracted EST CA certificate as an Explicit
76  //TA database entry for subsequent EST server authentication (refer to
77  //RFC 7030, section 4.1.3)
78  context->useExplicitTa = TRUE;
79 
80  //If successful, the server response must have an HTTP 200 response code
81  if(HTTP_STATUS_CODE_2YZ(context->statusCode))
82  {
83  //The HTTP content-type of "application/pkcs7-mime" must be used
84  if(osStrcmp(context->contentType, "application/pkcs7-mime") == 0)
85  {
86  //The Simple PKI Response is sent with a Content-Transfer-Encoding of
87  //"base64"
88  error = base64Decode((char_t *) context->buffer, context->bufferLen,
89  context->buffer, &context->bufferLen);
90 
91  //Check status code
92  if(!error)
93  {
94  //A successful response must be a certs-only CMC Simple PKI
95  //Response, as defined in RFC 5272
96  error = pkcs7ParseContentInfo(context->buffer, context->bufferLen,
97  &n, &contentInfo);
98  }
99 
100  //Check status code
101  if(!error)
102  {
103  //Parse signed-data content
104  error = pkcs7ParseSignedData(contentInfo.content.value,
105  contentInfo.content.length, &signedData);
106  }
107 
108  //Check status code
109  if(!error)
110  {
111  //Check the length of the certificate chain
112  if(signedData.certificates.raw.length > 0)
113  {
114  //Point to the first certificate of the chain
115  data = signedData.certificates.raw.value;
116  length = signedData.certificates.raw.length;
117 
118  //Parse CA certificates
119  while(length > 0 && !error)
120  {
121  //Parse certificate
122  error = asn1ReadSequence(data, length, &tag);
123 
124  //Check status code
125  if(!error)
126  {
127  //The first pass calculates the length of the PEM certificate
128  error = pemExportCertificate(data, tag.totalLength, NULL, &n);
129  }
130 
131  //Check status code
132  if(!error)
133  {
134  //Sanity check
135  if((context->caCertsLen + n) <= EST_CLIENT_MAX_CA_CERTS_LEN)
136  {
137  //The second pass exports the certificate to PEM format
139  context->caCerts + context->caCertsLen, &n);
140  }
141  else
142  {
143  //Report an error
144  error = ERROR_RESPONSE_TOO_LARGE;
145  }
146  }
147 
148  //Check status code
149  if(!error)
150  {
151  //Update the length of the CA certificates
152  context->caCertsLen += n;
153 
154  //Next DER certificate
155  data += tag.totalLength;
156  length -= tag.totalLength;
157  }
158  }
159  }
160  else
161  {
162  //Report an error
163  error = ERROR_INVALID_SYNTAX;
164  }
165  }
166  }
167  else
168  {
169  //Report an error
170  error = ERROR_INVALID_RESPONSE;
171  }
172  }
173  else
174  {
175  //Any other response code indicates an error and the client must abort
176  //the protocol (refer to RFC 7030, section 4.1.3)
177  error = ERROR_UNEXPECTED_STATUS;
178  }
179 
180  //Return status code
181  return error;
182 }
183 
184 
185 /**
186  * @brief Parse "simpleenroll" or "simplereenroll" response
187  * @param[in] context Pointer to the EST client context
188  * @return Error code
189  **/
190 
192 {
193  error_t error;
194 
195  //Check HTTP status code
196  if(context->statusCode == 202)
197  {
198  //If the server responds with an HTTP 202, this indicates that the
199  //request has been accepted for processing but that a response is not
200  //yet available (refer to RFC 7030, section 4.2.3)
201  error = ERROR_UNEXPECTED_STATUS;
202  }
203  else if(HTTP_STATUS_CODE_2YZ(context->statusCode))
204  {
205  //If the enrollment is successful, the server response must contain an
206  //HTTP 200 response code with a content-type of "application/pkcs7-mime"
207  //(refer to RFC 7030, section 4.2.3)
208  if(osStrcmp(context->contentType, "application/pkcs7-mime") == 0)
209  {
210  //A successful response must be a certs-only CMC Simple PKI Response,
211  //as defined in RFC 5272, containing only the certificate that was
212  //issued
213  error = estClientParsePkiResponse(context, context->buffer,
214  context->bufferLen);
215  }
216  else
217  {
218  //Report an error
219  error = ERROR_INVALID_RESPONSE;
220  }
221  }
222  else
223  {
224  //The server must answer with a suitable 4xx or 5xx HTTP error code when
225  //a problem occurs
226  error = ERROR_UNEXPECTED_STATUS;
227  }
228 
229  //Return status code
230  return error;
231 }
232 
233 
234 /**
235  * @brief Parse PKI message
236  * @param[in] context Pointer to the EST client context
237  * @param[in] data Pointer to the PKI message to parse
238  * @param[in] length Length of the PKI message
239  * @return Error code
240  **/
241 
243  size_t length)
244 {
245  error_t error;
246  size_t n;
247  Pkcs7ContentInfo contentInfo;
248  Pkcs7SignedData signedData;
249 
250  //The CMC Simple PKI Response is encoded in base64
251  error = base64Decode((char_t *) data, length, data, &n);
252  //Any error to report?
253  if(error)
254  return error;
255 
256  //The general syntax for content exchanged between entities associates a
257  //content type with content (refer to RFC 2315, section 7)
258  error = pkcs7ParseContentInfo(data, n, &n, &contentInfo);
259  //Any error to report?
260  if(error)
261  return error;
262 
263  //A successful response must be a certs-only CMC Simple PKI Response, as
264  //defined in RFC 5272
265  if(OID_COMP(contentInfo.contentType.value, contentInfo.contentType.length,
266  PKCS7_SIGNED_DATA_OID) != 0)
267  {
268  return ERROR_INVALID_TYPE;
269  }
270 
271  //Parse signed-data content
272  error = pkcs7ParseSignedData(contentInfo.content.value,
273  contentInfo.content.length, &signedData);
274  //Any error to report?
275  if(error)
276  return error;
277 
278  //In the degenerate case where there are no signers on the content (refer to
279  //RFC 2315, section 9.1)
280  if(signedData.contentInfo.content.length != 0 ||
281  signedData.signerInfos.numSignerInfos != 0)
282  {
283  return ERROR_INVALID_SYNTAX;
284  }
285 
286  //The reply contains only the certificate that was issued (refer to RFC 7030,
287  //section 4.2.3)
288  if(signedData.certificates.numCertificates != 1)
289  return ERROR_INVALID_SYNTAX;
290 
291  //Check the length of the issued certificate
294 
295  //Save the issued certificate
296  osMemcpy(context->cert, signedData.certificates.certificates[0].value,
297  signedData.certificates.certificates[0].length);
298 
299  //Save the length of the issued certificate
300  context->certLen = signedData.certificates.certificates[0].length;
301 
302  //Sucessful processing
303  return NO_ERROR;
304 }
305 
306 #endif
#define EST_CLIENT_MAX_CA_CERTS_LEN
Definition: est_client.h:147
X.509 certificate parsing.
String manipulation helper functions.
error_t estClientParseSimpleEnrollResponse(EstClientContext *context)
Parse "simpleenroll" or "simplereenroll" response.
Pkcs7Certificates certificates
Definition: pkcs7_common.h:355
error_t estClientParsePkiResponse(EstClientContext *context, uint8_t *data, size_t length)
Parse PKI message.
#define HTTP_STATUS_CODE_2YZ(code)
Definition: http_common.h:44
EST client.
OID (Object Identifier)
#define TRUE
Definition: os_port.h:50
uint8_t data[]
Definition: ethernet.h:224
Pkcs7ContentInfo contentInfo
Definition: pkcs7_common.h:354
X509OctetString certificates[PKCS7_MAX_CERTIFICATES]
Definition: pkcs7_common.h:193
Content information.
Definition: pkcs7_common.h:317
#define osStrcmp(s1, s2)
Definition: os_port.h:174
#define EST_CLIENT_MAX_CERT_LEN
Definition: est_client.h:140
error_t pkcs7ParseSignedData(const uint8_t *data, size_t length, Pkcs7SignedData *signedData)
Parse signed-data content.
Definition: pkcs7_parse.c:131
size_t totalLength
Definition: asn1.h:111
error_t base64Decode(const char_t *input, size_t inputLen, void *output, size_t *outputLen)
Base64 decoding algorithm.
Definition: base64.c:258
@ ERROR_UNEXPECTED_STATUS
Definition: error.h:284
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
PKCS #7 message parsing.
error_t
Error codes.
Definition: error.h:43
X509OctetString contentType
Definition: pkcs7_common.h:318
@ ERROR_RESPONSE_TOO_LARGE
Definition: error.h:285
Helper functions for EST client.
ASN.1 tag.
Definition: asn1.h:105
error_t estClientParseGetCaCertsResponse(EstClientContext *context)
Parse "cacerts" response.
Base64 encoding scheme.
@ ERROR_INVALID_TYPE
Definition: error.h:115
EST response parsing.
uint8_t length
Definition: tcp.h:375
PKCS #7 message decryption.
PEM file export functions.
Signed data content.
Definition: pkcs7_common.h:351
PKCS #7 signature verification.
char char_t
Definition: compiler_port.h:55
error_t pkcs7ParseContentInfo(const uint8_t *data, size_t length, size_t *totalLength, Pkcs7ContentInfo *contentInfo)
Parse contentInfo structure.
Definition: pkcs7_parse.c:56
#define OID_COMP(oid1, oidLen1, oid2)
Definition: oid.h:42
uint8_t n
error_t pemExportCertificate(const uint8_t *cert, size_t certLen, char_t *output, size_t *written)
Export an X.509 certificate to PEM format.
Definition: pem_export.c:53
X509OctetString content
Definition: pkcs7_common.h:319
const uint8_t PKCS7_SIGNED_DATA_OID[9]
Definition: pkcs7_common.c:52
Pkcs7SignerInfos signerInfos
Definition: pkcs7_common.h:357
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
X509OctetString raw
Definition: pkcs7_common.h:191
const uint8_t * value
Definition: x509_common.h:704
error_t asn1ReadSequence(const uint8_t *data, size_t length, Asn1Tag *tag)
Read an ASN.1 sequence from the input stream.
Definition: asn1.c:163
#define EstClientContext
Definition: est_client.h:159
@ ERROR_INVALID_RESPONSE
Definition: error.h:71
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
ASN.1 (Abstract Syntax Notation One)