1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX Secure Component */
16 /** */
17 /** Transport Layer Security (TLS) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SECURE_SOURCE_CODE
23
24
25 #include "nx_secure_tls.h"
26 #include "nx_secure_x509.h"
27
28 /**************************************************************************/
29 /* */
30 /* FUNCTION RELEASE */
31 /* */
32 /* _nx_secure_tls_process_remote_certificate PORTABLE C */
33 /* 6.2.1 */
34 /* AUTHOR */
35 /* */
36 /* Timothy Stapko, Microsoft Corporation */
37 /* */
38 /* DESCRIPTION */
39 /* */
40 /* This function processes an incoming TLS server certificate message, */
41 /* extracting the RSA or DH public key and verifying the validity of */
42 /* the certificate. It parses the X509 certificate and fills in the */
43 /* relevant information if the caller has allocated space for it using */
44 /* nx_secure_remote_certificate_allocate. */
45 /* */
46 /* INPUT */
47 /* */
48 /* tls_session TLS control block */
49 /* packet_buffer Pointer to message data */
50 /* message_length Length of message data (bytes)*/
51 /* data_length Length of packet buffer */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* status Completion status */
56 /* */
57 /* CALLS */
58 /* */
59 /* _nx_secure_tls_remote_certificate_verify */
60 /* Verify the server certificate */
61 /* _nx_secure_x509_certificate_list_add Add incoming cert to store */
62 /* _nx_secure_x509_certificate_parse Extract public key data */
63 /* _nx_secure_x509_free_certificate_get Get free cert for storage */
64 /* tx_mutex_get Get protection mutex */
65 /* tx_mutex_put Put protection mutex */
66 /* */
67 /* CALLED BY */
68 /* */
69 /* _nx_secure_dtls_client_handshake DTLS client state machine */
70 /* _nx_secure_tls_client_handshake TLS client state machine */
71 /* _nx_secure_tls_server_handshake TLS server state machine */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
78 /* 09-30-2020 Timothy Stapko Modified comment(s), */
79 /* verified memcpy use cases, */
80 /* fixed certificate buffer */
81 /* allocation, */
82 /* resulting in version 6.1 */
83 /* 04-02-2021 Timothy Stapko Modified comment(s), */
84 /* updated X.509 return value, */
85 /* resulting in version 6.1.6 */
86 /* 04-25-2022 Timothy Stapko Modified comment(s), */
87 /* removed unnecessary code, */
88 /* resulting in version 6.1.11 */
89 /* 03-08-2023 Yanwu Cai Modified comment(s), */
90 /* fixed compiler errors when */
91 /* x509 is disabled, */
92 /* initialized metadata for */
93 /* remote certificate, */
94 /* resulting in version 6.2.1 */
95 /* */
96 /**************************************************************************/
_nx_secure_tls_process_remote_certificate(NX_SECURE_TLS_SESSION * tls_session,UCHAR * packet_buffer,UINT message_length,UINT data_length)97 UINT _nx_secure_tls_process_remote_certificate(NX_SECURE_TLS_SESSION *tls_session,
98 UCHAR *packet_buffer, UINT message_length,
99 UINT data_length)
100 {
101 #ifndef NX_SECURE_DISABLE_X509
102 UINT length;
103 UINT total_length;
104 UINT cert_length = 0;
105 UINT status;
106 NX_SECURE_X509_CERT *certificate;
107 UCHAR *endpoint_raw_ptr;
108 UINT endpoint_length;
109 UINT bytes_processed;
110 #if (NX_SECURE_TLS_TLS_1_3_ENABLED)
111 UINT extensions_length;
112 #endif
113 UCHAR *cert_buffer;
114 ULONG cert_buf_size;
115
116
117 /* Structure:
118 * | 3 | <Total Length> |
119 * | Total length | 3 | <Cert[0] Length> | 3 + <Cert[x] Len> |
120 * | | Cert[0] Length | Certificate[0] | More certs... |
121 */
122
123 /* TLS 1.3 Structure:
124 * | 1 | <Ctx Len> | 3 | <Total Length> |
125 * | Ctx Len | Context | Total length | 3 | <Cert[x] Length> | 2 | <ExtLen> |
126 * | | Cert[x] Length | Certificate[x] | ExtLen | Extensions |
127 */
128
129
130
131 /* At this point, the packet buffer is filled with a TLS record. We can use the remainder of
132 the buffer to hold certificate structures for parsing. The remote certificates will
133 remain in the packet buffer and only the X.509 parsing structure (NX_SECURE_X509_CERT)
134 will be allocated. See _nx_secure_tls_process_remote_certificate for more info. */
135 /* Typical layout of packet buffer and certificate buffer. Cert buffer allocates top-down, X.509 parsing structures
136 are allocated and used only until the certificate chain verification step. After that, the remote certs are cleared
137 and then the endpoint certificate is copied into the cert buffer (only the endpoint) for later use. The packet buffer
138 following this function (on success) should have the following layout (assuming no user-allocated certs):
139 | Packet buffer | Certificate buffer |
140 |<-----------data------------------>|--> free space <--| Endpoint Cert 1 | X.509 |
141 */
142
143 if (data_length > tls_session -> nx_secure_tls_packet_buffer_size)
144 {
145 return(NX_SECURE_TLS_PACKET_BUFFER_TOO_SMALL);
146 }
147
148 /* Certificate buffer is at the end of the record in the record assembly buffer. */
149 cert_buffer = &tls_session->nx_secure_tls_packet_buffer[data_length];
150
151 /* The size of the buffer is the remaining space in the record assembly buffer. */
152 cert_buf_size = tls_session -> nx_secure_tls_packet_buffer_size - data_length;
153
154 /* Use our length as an index into the buffer. */
155 length = 0;
156
157 #if (NX_SECURE_TLS_TLS_1_3_ENABLED)
158 if(tls_session->nx_secure_tls_1_3)
159 {
160 /* The certificate chain in TLS 1.3 contains a "context" at
161 the beginning of the handshake record. If the first byte is non-zero
162 it means the following bytes (length given as the value of that byte)
163 should be the context. */
164 packet_buffer++;
165 message_length--;
166
167 }
168 #endif
169
170
171 /* Extract the certificate(s) from the incoming data, starting with. */
172 total_length = (UINT)((packet_buffer[0] << 16) + (packet_buffer[1] << 8) + packet_buffer[2]);
173 length = length + 3;
174
175 /* Make sure what we extracted makes sense. */
176 if (total_length > message_length)
177 {
178 return(NX_SECURE_TLS_INCORRECT_MESSAGE_LENGTH);
179 }
180
181 /* See if remote host sent an empty certificate message. */
182 if (total_length == 0)
183 {
184 /* No certificate received! */
185 return(NX_SECURE_TLS_EMPTY_REMOTE_CERTIFICATE_RECEIVED);
186 }
187
188 /* Keep subtracting from the total length until no more certificates left. */
189 while (total_length > 0)
190 {
191 /* Extract the next certificate's length. */
192 cert_length = (UINT)((packet_buffer[length] << 16) + (packet_buffer[length + 1] << 8) + packet_buffer[length + 2]);
193 length = length + 3;
194
195 /* Make sure the individual cert length makes sense. */
196 if ((cert_length + 3) > total_length)
197 {
198 return(NX_SECURE_TLS_INCORRECT_MESSAGE_LENGTH);
199 }
200
201 /* Get a reference to the remote endpoint certificate that was allocated earlier. */
202 status = _nx_secure_x509_free_certificate_get(&tls_session -> nx_secure_tls_credentials.nx_secure_tls_certificate_store,
203 &certificate);
204
205 /* If there are no free certificates, attempt to allocate from the packet reassembly buffer
206 (the certificate buffer is carved from the packet buffer in nx_secure_tls_process_record). */
207 if (status != NX_SUCCESS)
208 {
209 /* No remote certificates added. Instead try extracting space from packet buffer. */
210 if(cert_buf_size < sizeof(NX_SECURE_X509_CERT))
211 {
212 /* Not enough space to allocate the X.509 structure. */
213 return(NX_SECURE_TLS_INSUFFICIENT_CERT_SPACE);
214 }
215
216 /* Get space for the parsing structure. */
217 cert_buf_size -= sizeof(NX_SECURE_X509_CERT);
218 certificate = (NX_SECURE_X509_CERT*)(&cert_buffer[cert_buf_size]);
219 NX_SECURE_MEMSET(certificate, 0, sizeof(NX_SECURE_X509_CERT));
220
221 /* Point structure to certificate being parsed. Note that the certificate
222 structure points directly into the packet buffer where the certificate
223 is located - this certificate structure must NOT be used outside this function. */
224 certificate -> nx_secure_x509_certificate_raw_data_length = cert_length;
225 certificate -> nx_secure_x509_certificate_raw_data = &packet_buffer[length];
226 certificate -> nx_secure_x509_certificate_raw_buffer_size = cert_length;
227 certificate -> nx_secure_x509_user_allocated_cert = NX_FALSE;
228 }
229 else
230 {
231 /* Make sure we have enough space to save our certificate. */
232 if (certificate -> nx_secure_x509_certificate_raw_buffer_size < cert_length)
233 {
234 return(NX_SECURE_TLS_INSUFFICIENT_CERT_SPACE);
235 }
236
237 /* Copy the certificate from the packet buffer into our allocated certificate space. */
238 certificate -> nx_secure_x509_certificate_raw_data_length = cert_length;
239 NX_SECURE_MEMCPY(certificate -> nx_secure_x509_certificate_raw_data, &packet_buffer[length], cert_length); /* Use case of memcpy is verified. lgtm[cpp/banned-api-usage-required-any] */
240 }
241 length += cert_length;
242
243 /* Release the protection. */
244 tx_mutex_put(&_nx_secure_tls_protection);
245
246 /* Parse the DER-encoded X509 certificate to extract the public key data. */
247 status = _nx_secure_x509_certificate_parse(certificate -> nx_secure_x509_certificate_raw_data, cert_length, &bytes_processed, certificate);
248
249 /* Get the protection. */
250 tx_mutex_get(&_nx_secure_tls_protection, TX_WAIT_FOREVER);
251
252 /* Make sure we parsed a valid certificate. */
253 if (status != NX_SUCCESS)
254 {
255
256 /* Translate some X.509 return values into TLS return values. */
257 if (status == NX_SECURE_X509_UNSUPPORTED_PUBLIC_CIPHER)
258 {
259 return(NX_SECURE_TLS_UNSUPPORTED_PUBLIC_CIPHER);
260 }
261
262 return(status);
263 }
264
265 #if (NX_SECURE_TLS_TLS_1_3_ENABLED)
266 /* Check for TLS 1.3 extensions following each certificate. */
267 if(tls_session->nx_secure_tls_1_3)
268 {
269 extensions_length = (UINT)((packet_buffer[length] << 8) + packet_buffer[length + 1]);
270
271 /* Add extensions length bytes. */
272 length += 2;
273
274 /* Add extensions length to offset. */
275 length += extensions_length;
276
277 /* Adjust the total length with our extension data. */
278 total_length -= (2 + extensions_length);
279 }
280 #endif
281
282 /* Advance the variable of total_length. */
283 total_length -= (3 + cert_length);
284
285 /* Assign the TLS Session metadata areas to the certificate for later use. */
286 certificate -> nx_secure_x509_public_cipher_metadata_area = tls_session -> nx_secure_public_cipher_metadata_area;
287 certificate -> nx_secure_x509_public_cipher_metadata_size = tls_session -> nx_secure_public_cipher_metadata_size;
288
289 certificate -> nx_secure_x509_hash_metadata_area = tls_session -> nx_secure_hash_mac_metadata_area;
290 certificate -> nx_secure_x509_hash_metadata_size = tls_session -> nx_secure_hash_mac_metadata_size;
291
292 /* Add the certificate to the remote store. */
293 /* Parse and initialize the remote certificate for use in subsequent operations. */
294 status = _nx_secure_x509_certificate_list_add(&tls_session -> nx_secure_tls_credentials.nx_secure_tls_certificate_store.nx_secure_x509_remote_certificates,
295 certificate, NX_TRUE);
296
297 /* Make sure we parsed a valid certificate. */
298 if (status != NX_SUCCESS)
299 {
300
301 /* Translate some X.509 return values into TLS return values. */
302 if (status == NX_SECURE_X509_CERT_ID_DUPLICATE)
303 {
304 return(NX_SECURE_TLS_CERT_ID_DUPLICATE);
305 }
306
307 return(status);
308 }
309
310 /* Make sure the certificate has it's cipher table initialized. */
311 certificate -> nx_secure_x509_cipher_table = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_x509_cipher_table;
312 certificate -> nx_secure_x509_cipher_table_size = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_x509_cipher_table_size;
313 }
314
315 /* =============================== CERTIFICATE CHAIN VERIFICATION ======================================== */
316 /* Verify the certificates we received are valid against the trusted store. */
317 status = _nx_secure_tls_remote_certificate_verify(tls_session);
318
319 if(status != NX_SUCCESS)
320 {
321 return(status);
322 }
323
324 /* ======================== Save off the endpoint certificate for later use. =============================*/
325 /* Get the endpoint from the certificates we just parsed. */
326 status = _nx_secure_x509_remote_endpoint_certificate_get(&tls_session -> nx_secure_tls_credentials.nx_secure_tls_certificate_store,
327 &certificate);
328 NX_ASSERT(status == NX_SUCCESS);
329
330 /* If the endpoint certificate was NOT allocated by the user application, we need to
331 make a copy and save it for later use. */
332 if(!certificate -> nx_secure_x509_user_allocated_cert)
333 {
334 /* Free all certificates that we added to the packet buffer. Do this before the
335 call to nx_secure_x509_free_certificate_get so that if there are user-allocated
336 certificates the endpoint is put into one of them. */
337 status = _nx_secure_tls_remote_certificate_free_all(tls_session);
338
339 if(status != NX_SUCCESS)
340 {
341 return(status);
342 }
343
344 /* Save the raw pointer to endpoint so we can clear remote certificate state. */
345 endpoint_raw_ptr = certificate->nx_secure_x509_certificate_raw_data;
346 endpoint_length = certificate->nx_secure_x509_certificate_raw_data_length;
347
348 /* Now allocate a new certificate for the endpoint. */
349 status = _nx_secure_x509_free_certificate_get(&tls_session -> nx_secure_tls_credentials.nx_secure_tls_certificate_store,
350 &certificate);
351
352 /* If there are no free certificates, attempt to allocate from the packet reassembly buffer
353 (the certificate buffer is carved from the packet buffer in nx_secure_tls_process_record). */
354 if (status != NX_SUCCESS)
355 {
356 /* No remote certificates added. Instead try extracting space from packet buffer. */
357 cert_buffer = &tls_session -> nx_secure_tls_packet_buffer[data_length];
358 cert_buf_size = tls_session -> nx_secure_tls_packet_buffer_size - data_length;
359
360 /* Get space for the parsing structure. */
361 cert_buf_size -= sizeof(NX_SECURE_X509_CERT);
362 certificate = (NX_SECURE_X509_CERT*)(&cert_buffer[cert_buf_size]);
363 NX_SECURE_MEMSET(certificate, 0, sizeof(NX_SECURE_X509_CERT));
364
365 if(cert_buf_size < endpoint_length)
366 {
367
368 /* Not enough space to allocate the raw certificate data. */
369 return(NX_SECURE_TLS_INSUFFICIENT_CERT_SPACE);
370 }
371
372 /* Allocate space for the endpoint certificate. */
373 cert_buf_size -= endpoint_length;
374
375 /* Point structure to certificate being parsed. */
376 certificate -> nx_secure_x509_certificate_raw_data = &cert_buffer[cert_buf_size];
377 certificate -> nx_secure_x509_certificate_raw_buffer_size = endpoint_length;
378
379 /* Update total remaining size. */
380 tls_session -> nx_secure_tls_packet_buffer_size -= (sizeof(NX_SECURE_X509_CERT) + endpoint_length);
381 }
382
383 /* Copy the certificate data to the end of the certificate buffer or use an allocated certificate. */
384 certificate -> nx_secure_x509_certificate_raw_data_length = endpoint_length;
385 NX_SECURE_MEMCPY(certificate->nx_secure_x509_certificate_raw_data, endpoint_raw_ptr, endpoint_length); /* Use case of memcpy is verified. lgtm[cpp/banned-api-usage-required-any] */
386
387 /* Assign the TLS Session metadata areas to the certificate for later use. */
388 certificate -> nx_secure_x509_public_cipher_metadata_area = tls_session -> nx_secure_public_cipher_metadata_area;
389 certificate -> nx_secure_x509_public_cipher_metadata_size = tls_session -> nx_secure_public_cipher_metadata_size;
390
391 certificate -> nx_secure_x509_hash_metadata_area = tls_session -> nx_secure_hash_mac_metadata_area;
392 certificate -> nx_secure_x509_hash_metadata_size = tls_session -> nx_secure_hash_mac_metadata_size;
393
394 /* Assign the cipher table from the parent TLS session. */
395 certificate -> nx_secure_x509_cipher_table = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_x509_cipher_table;
396 certificate -> nx_secure_x509_cipher_table_size = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_x509_cipher_table_size;
397
398 /* Release the protection. */
399 tx_mutex_put(&_nx_secure_tls_protection);
400
401 /* Re-parse the certificate using the original data. */
402 status = _nx_secure_x509_certificate_parse(certificate -> nx_secure_x509_certificate_raw_data, endpoint_length, &bytes_processed, certificate);
403 NX_ASSERT(status == NX_SUCCESS);
404
405 /* Get the protection. */
406 tx_mutex_get(&_nx_secure_tls_protection, TX_WAIT_FOREVER);
407
408 /* Re-add the remote endpoint certificate for later use. */
409 status = _nx_secure_x509_certificate_list_add(&tls_session -> nx_secure_tls_credentials.nx_secure_tls_certificate_store.nx_secure_x509_remote_certificates,
410 certificate, NX_TRUE);
411 NX_ASSERT(status == NX_SUCCESS);
412
413 /* Make sure the certificate has it's cipher table initialized. */
414 certificate -> nx_secure_x509_cipher_table = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_x509_cipher_table;
415 certificate -> nx_secure_x509_cipher_table_size = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_x509_cipher_table_size;
416 }
417 #ifdef NX_SECURE_TLS_CLIENT_DISABLED
418 /* If TLS Client is disabled and we have processed a ServerCertificate message, something is wrong... */
419 tls_session -> nx_secure_tls_server_state = NX_SECURE_TLS_SERVER_STATE_ERROR;
420
421 return(NX_SECURE_TLS_INVALID_STATE);
422 #else
423 /* Set our state to indicate we successfully parsed the Certificate message. */
424 tls_session -> nx_secure_tls_client_state = NX_SECURE_TLS_CLIENT_STATE_SERVER_CERTIFICATE;
425
426 return(status);
427 #endif
428 #else
429 NX_PARAMETER_NOT_USED(tls_session);
430 NX_PARAMETER_NOT_USED(packet_buffer);
431 NX_PARAMETER_NOT_USED(message_length);
432 NX_PARAMETER_NOT_USED(data_length);
433
434 return(NX_NOT_SUPPORTED);
435 #endif
436 }
437
438