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 /** Datagram Transport Layer Security (DTLS) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SECURE_SOURCE_CODE
23
24 #include "nx_secure_dtls.h"
25
26 #ifdef NX_SECURE_ENABLE_DTLS
27 /**************************************************************************/
28 /* */
29 /* FUNCTION RELEASE */
30 /* */
31 /* _nx_secure_dtls_process_record PORTABLE C */
32 /* 6.1.10 */
33 /* AUTHOR */
34 /* */
35 /* Timothy Stapko, Microsoft Corporation */
36 /* */
37 /* DESCRIPTION */
38 /* */
39 /* This function processes a single DTLS record, handling both */
40 /* handshake and application records. When multiple records are */
41 /* received in a single lower-level network packet (e.g. UDP), the */
42 /* "record_offset" parameter is used to offset into the packet buffer. */
43 /* */
44 /* INPUT */
45 /* */
46 /* dtls_session Pointer to DTLS control block */
47 /* packet_ptr NX_PACKET containing a record */
48 /* record_offset Offset of record in packet */
49 /* bytes_processed Return for size of packet */
50 /* wait_option Control timeout options */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* status Completion status */
55 /* */
56 /* CALLS */
57 /* */
58 /* _nx_secure_dtls_client_handshake DTLS Client state machine */
59 /* _nx_secure_dtls_process_header Process record header */
60 /* _nx_secure_dtls_server_handshake DTLS Server state machine */
61 /* _nx_secure_dtls_verify_mac Verify record MAC checksum */
62 /* _nx_secure_tls_process_changecipherspec */
63 /* Process ChangeCipherSpec */
64 /* _nx_secure_tls_record_payload_decrypt Decrypt record data */
65 /* nx_secure_tls_packet_release Release packet */
66 /* tx_mutex_get Get protection mutex */
67 /* tx_mutex_put Put protection mutex */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* _nx_secure_dtls_session_receive Receive DTLS data */
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 /* adjusted logic for TLS */
80 /* modifications, */
81 /* resulting in version 6.1 */
82 /* 12-31-2020 Timothy Stapko Modified comment(s), */
83 /* improved buffer length */
84 /* verification, */
85 /* resulting in version 6.1.3 */
86 /* 01-31-2022 Timothy Stapko Modified comment(s), */
87 /* fixed out-of-order handling,*/
88 /* resulting in version 6.1.10 */
89 /* */
90 /**************************************************************************/
_nx_secure_dtls_process_record(NX_SECURE_DTLS_SESSION * dtls_session,NX_PACKET * packet_ptr,ULONG record_offset,ULONG * bytes_processed,ULONG wait_option)91 UINT _nx_secure_dtls_process_record(NX_SECURE_DTLS_SESSION *dtls_session, NX_PACKET *packet_ptr,
92 ULONG record_offset, ULONG *bytes_processed, ULONG wait_option)
93 {
94 UINT status;
95 UINT error_status;
96 USHORT header_length;
97 UCHAR header_data[NX_SECURE_DTLS_RECORD_HEADER_SIZE]; /* DTLS record header is larger than TLS. Allocate enough space for both. */
98 USHORT message_type;
99 UINT message_length;
100 UCHAR *packet_data;
101 NX_SECURE_TLS_SESSION *tls_session;
102 UCHAR epoch_seq_num[8];
103 ULONG seq_num_rewind[2];
104 ULONG window_rewind;
105 NX_PACKET *decrypted_packet;
106
107 /* Basic state machine:
108 * 1. Process header, which will set the state and return some data.
109 * 2. Advance the packet pointer by the size of the header (returned by header processing)
110 * and process the record.
111 * 3. Take any actions necessary based on state.
112 */
113
114 /* Get a reference to basic TLS state. */
115 tls_session = &dtls_session -> nx_secure_dtls_tls_session;
116
117 header_length = NX_SECURE_DTLS_RECORD_HEADER_SIZE;
118
119 /* Save the sequence number in case we reject a record (e.g. handshake message missing). */
120 seq_num_rewind[0] = tls_session -> nx_secure_tls_remote_sequence_number[0];
121 seq_num_rewind[1] = tls_session -> nx_secure_tls_remote_sequence_number[1];
122 window_rewind = dtls_session -> nx_secure_dtls_sliding_window;
123
124 /* Process the DTLS record header, which will set the state. */
125 status = _nx_secure_dtls_process_header(dtls_session, packet_ptr, record_offset, &message_type, &message_length, header_data, &header_length);
126
127 if (status == NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE || status == NX_SECURE_TLS_INVALID_EPOCH || status == NX_SECURE_TLS_REPEAT_MESSAGE_RECEIVED)
128 {
129 /* Received an out-of-order message. Ignore it. */
130 *bytes_processed = (ULONG)header_length + message_length;
131
132 /* Pretend this record never happened. */
133 return(NX_CONTINUE);
134 }
135
136 if (status != NX_SECURE_TLS_SUCCESS)
137 {
138 return(status);
139 }
140
141 /* Ignore empty records. */
142 if (message_length == 0)
143 {
144 /* Update the number of bytes we processed. */
145 *bytes_processed = (ULONG)header_length + message_length;
146 return(NX_CONTINUE);
147 }
148
149 if (((ULONG)(packet_ptr -> nx_packet_append_ptr) - (ULONG)(packet_ptr -> nx_packet_prepend_ptr)) <
150 ((ULONG)header_length + message_length))
151 {
152
153 /* Chained packet is not supported. */
154 return(NX_SECURE_TLS_INVALID_PACKET);
155 }
156
157 /* Now extract the record. */
158 packet_data = &packet_ptr -> nx_packet_prepend_ptr[header_length];
159
160 /*status = nx_packet_data_extract_offset(packet_ptr, header_length + record_offset, packet_data, message_length, &bytes_copied);*/
161
162 /* Update the number of bytes we processed. */
163 *bytes_processed = (ULONG)header_length + message_length;
164
165 /* Check for active encryption of incoming records. If encrypted, decrypt before further processing. */
166 if (tls_session -> nx_secure_tls_remote_session_active)
167 {
168 epoch_seq_num[0] = header_data[10];
169 epoch_seq_num[1] = header_data[9];
170 epoch_seq_num[2] = header_data[8];
171 epoch_seq_num[3] = header_data[7];
172 epoch_seq_num[4] = header_data[6];
173 epoch_seq_num[5] = header_data[5];
174 epoch_seq_num[6] = header_data[4];
175 epoch_seq_num[7] = header_data[3];
176
177 /* Decrypt the record data. */
178 status = _nx_secure_tls_record_payload_decrypt(tls_session, packet_ptr, header_length, message_length,
179 &decrypted_packet, (ULONG *)epoch_seq_num, (UCHAR)message_type,
180 wait_option);
181
182 /* Check the MAC hash. */
183 if (status == NX_SECURE_TLS_SUCCESS)
184 {
185
186 /* Verify the hash MAC in the decrypted record. */
187 packet_data = decrypted_packet -> nx_packet_prepend_ptr;
188 message_length = (UINT)decrypted_packet -> nx_packet_length;
189 error_status = NX_SECURE_TLS_SUCCESS;
190 }
191 else
192 {
193
194 /* Save off the error status so we can return it after the mac check. */
195 error_status = status;
196 }
197
198 /* !!! NOTE - the MAC check MUST always be performed regardless of the error state of
199 the payload decryption operation. Skipping the MAC check on padding failures
200 could enable a timing-based attack allowing an attacker to determine whether
201 padding was valid or not, causing an information leak. */
202 status = _nx_secure_dtls_verify_mac(dtls_session, header_data, header_length, packet_data, &message_length);
203
204 /* Check to see if decryption or verification failed. */
205 if(error_status != NX_SECURE_TLS_SUCCESS)
206 {
207 /* Decryption failed. Rewind the sequence number and sliding window. */
208 tls_session -> nx_secure_tls_remote_sequence_number[0] = seq_num_rewind[0];
209 tls_session -> nx_secure_tls_remote_sequence_number[1] = seq_num_rewind[1];
210 dtls_session -> nx_secure_dtls_sliding_window = window_rewind;
211 return(error_status);
212 }
213
214 /* Check to see if decryption or verification failed. */
215 if (status == NX_SECURE_TLS_SUCCESS)
216 {
217 /* Copy data to original packet to keep IP header available. */
218 /* Note: At the point of this memcpy the plaintext should never be larger than the cipher.
219 Assertion check is to protect against future changes inadvertently causing an overflow. */
220 NX_ASSERT((ULONG)(packet_ptr -> nx_packet_data_end - packet_ptr -> nx_packet_prepend_ptr) >= message_length);
221 NX_SECURE_MEMCPY(packet_ptr -> nx_packet_prepend_ptr, packet_data, message_length); /* Use case of memcpy is verified. */
222 packet_data = packet_ptr -> nx_packet_prepend_ptr;
223 packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_prepend_ptr + message_length;
224 packet_ptr -> nx_packet_length = message_length;
225 }
226
227 /* Release decrypted packet. */
228 nx_secure_tls_packet_release(decrypted_packet);
229
230 /* Check to see if decryption or verification failed. */
231 if (status != NX_SECURE_TLS_SUCCESS)
232 {
233 return(status);
234 }
235 }
236
237 switch (message_type)
238 {
239 case NX_SECURE_TLS_CHANGE_CIPHER_SPEC:
240 /* Received a ChangeCipherSpec message - from now on all messages from remote host
241 will be encrypted using the session keys. */
242 status = _nx_secure_tls_process_changecipherspec(tls_session, packet_data, message_length);
243 if(status != NX_SUCCESS)
244 {
245 /* Received out-of-order CCS message. */
246 status = NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE;
247 }
248 else
249 {
250 /* New epoch if we receive a CCS message. */
251 dtls_session -> nx_secure_dtls_remote_epoch = (USHORT)(dtls_session -> nx_secure_dtls_remote_epoch + 1);
252 }
253
254 break;
255 case NX_SECURE_TLS_ALERT:
256 /* We have received an alert. Check what the alert was and take appropriate action. */
257
258 /* Save off the alert level and value for the application to access. */
259 tls_session->nx_secure_tls_received_alert_level = packet_data[0];
260 tls_session->nx_secure_tls_received_alert_value = packet_data[1];
261
262 status = NX_SECURE_TLS_ALERT_RECEIVED;
263 break;
264 case NX_SECURE_TLS_HANDSHAKE:
265 #ifndef NX_SECURE_TLS_SERVER_DISABLED
266 /* The socket is a TLS server, so process incoming handshake messages in that context. */
267 if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_SERVER)
268 {
269 status = _nx_secure_dtls_server_handshake(dtls_session, packet_data, message_length, wait_option);
270 }
271 #endif
272 #ifndef NX_SECURE_TLS_CLIENT_DISABLED
273 /* The socket is a TLS client, so process incoming handshake messages in that context. */
274 if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_CLIENT)
275 {
276 status = _nx_secure_dtls_client_handshake(dtls_session, packet_data, message_length, wait_option);
277 }
278 #endif
279 break;
280 case NX_SECURE_TLS_APPLICATION_DATA:
281 /* The remote host is sending application data records now. Pass decrypted data back
282 to the networking API for the application to process. */
283
284 /* First, check for 0-length records. These can be sent but are internal to TLS so tell the
285 caller to continue receiving. 0-length records are sent as part of the BEAST attack
286 mitigation by some TLS implementations (notably OpenSSL). */
287 if (message_length == 0)
288 {
289 status = NX_CONTINUE;
290 }
291 break;
292 default:
293 /* An unrecognized message was received, likely an error, possibly an attack. */
294 status = NX_SECURE_TLS_UNRECOGNIZED_MESSAGE_TYPE;
295 break;
296 }
297
298 /* If we received an out-of-order handshake message, rewind the sequence number. */
299 if(status == NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE)
300 {
301 /* Handshake message was not what we expected, so rewind the sequence number. */
302 tls_session -> nx_secure_tls_remote_sequence_number[0] = seq_num_rewind[0];
303 tls_session -> nx_secure_tls_remote_sequence_number[1] = seq_num_rewind[1];
304 dtls_session -> nx_secure_dtls_sliding_window = window_rewind;
305 status = NX_CONTINUE;
306 }
307
308
309 return(status);
310 }
311 #endif /* NX_SECURE_ENABLE_DTLS */
312
313