1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
|
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
* Copyright (c) 2025-present Eclipse ThreadX Contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* SPDX-License-Identifier: MIT
**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** NetX Secure Component */
/** */
/** Datagram Transport Layer Security (DTLS) */
/** */
/**************************************************************************/
/**************************************************************************/
#define NX_SECURE_SOURCE_CODE
#include "nx_secure_dtls.h"
#ifdef NX_SECURE_ENABLE_DTLS
#include "nx_packet.h"
#include "nx_udp.h"
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_secure_dtls_send_record PORTABLE C */
/* 6.4.3 */
/* AUTHOR */
/* */
/* Timothy Stapko, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function encapsulates the DTLS record layer send functionality */
/* The incoming packet data is wrapped in a DTLS record, which includes*/
/* a header and footer (for encrypted data). Also, all encryption of */
/* application data is handled here. */
/* */
/* INPUT */
/* */
/* dtls_session Pointer to DTLS session */
/* send_packet Packet data to send */
/* record_type DTLS record type */
/* wait_option Suspension option */
/* */
/* OUTPUT */
/* */
/* status Record send status */
/* */
/* CALLS */
/* */
/* _nx_secure_dtls_hash_record Generate hash of payload */
/* _nx_secure_tls_record_payload_encrypt Encrypt payload */
/* _nx_secure_tls_session_iv_size_get Get IV size for this session */
/* _nxd_udp_socket_send Send UDP packet */
/* _nxd_udp_socket_source_send Send UDP packet with specific */
/* source address */
/* nx_secure_tls_packet_release Release packet */
/* tx_mutex_get Get protection mutex */
/* tx_mutex_put Put protection mutex */
/* */
/* CALLED BY */
/* */
/* _nx_secure_dtls_client_handshake DTLS client state machine */
/* _nx_secure_dtls_send_handshake_record Send DTLS handshake record */
/* _nx_secure_dtls_server_handshake DTLS server state machine */
/* _nx_secure_dtls_session_end Actual DTLS session end call */
/* _nx_secure_dtls_session_receive Receive DTLS data */
/* _nx_secure_dtls_session_send Actual DTLS session send call */
/* */
/**************************************************************************/
UINT _nx_secure_dtls_send_record(NX_SECURE_DTLS_SESSION *dtls_session, NX_PACKET *send_packet,
UCHAR record_type, ULONG wait_option)
{
UINT status;
UINT message_length;
UCHAR *mac_secret;
UCHAR *record_header;
UCHAR record_hash[NX_SECURE_TLS_MAX_HASH_SIZE];
UINT hash_length;
ULONG length;
USHORT iv_size;
UCHAR *data;
NX_SECURE_TLS_SESSION *tls_session;
UCHAR epoch_seq_num[8];
NX_PARAMETER_NOT_USED(wait_option);
/* Pointer to the actual packet data for hashing and encryption. */
data = send_packet -> nx_packet_prepend_ptr;
/* Length of the data in the packet. */
length = send_packet -> nx_packet_length;
/* Get a pointer to TLS state. */
tls_session = &dtls_session -> nx_secure_dtls_tls_session;
/* See if this is an active session, we need to account for the IV if the session cipher
uses one. */
if (tls_session -> nx_secure_tls_local_session_active)
{
/* Get the size of the IV used by the session cipher. */
status = _nx_secure_tls_session_iv_size_get(tls_session, &iv_size);
if (status != NX_SUCCESS)
{
return(status);
}
/* Back off the pointer to the point before the IV data allocation
(can be 0). Increases length since we are moving the prepend pointer. */
send_packet -> nx_packet_prepend_ptr -= iv_size;
send_packet -> nx_packet_length += iv_size;
}
/* Get a pointer to our record header. */
record_header = send_packet -> nx_packet_prepend_ptr - NX_SECURE_DTLS_RECORD_HEADER_SIZE;
/* Build the TLS record header. */
record_header[0] = record_type;
/* Set the version number. */
record_header[1] = (UCHAR)((tls_session -> nx_secure_tls_protocol_version & 0xFF00) >> 8);
record_header[2] = (UCHAR)(tls_session -> nx_secure_tls_protocol_version & 0x00FF);
/* DTLS Epoch counter. */
record_header[3] = (UCHAR)(dtls_session -> nx_secure_dtls_local_epoch >> 8);
record_header[4] = (UCHAR)(dtls_session -> nx_secure_dtls_local_epoch);
/* DTLS sequence number. */
record_header[5] = (UCHAR)(tls_session -> nx_secure_tls_local_sequence_number[1] >> 8);
record_header[6] = (UCHAR)(tls_session -> nx_secure_tls_local_sequence_number[1]);
record_header[7] = (UCHAR)(tls_session -> nx_secure_tls_local_sequence_number[0] >> 24);
record_header[8] = (UCHAR)(tls_session -> nx_secure_tls_local_sequence_number[0] >> 16);
record_header[9] = (UCHAR)(tls_session -> nx_secure_tls_local_sequence_number[0] >> 8);
record_header[10] = (UCHAR)(tls_session -> nx_secure_tls_local_sequence_number[0]);
epoch_seq_num[0] = record_header[10];
epoch_seq_num[1] = record_header[9];
epoch_seq_num[2] = record_header[8];
epoch_seq_num[3] = record_header[7];
epoch_seq_num[4] = record_header[6];
epoch_seq_num[5] = record_header[5];
epoch_seq_num[6] = record_header[4];
epoch_seq_num[7] = record_header[3];
/* Increment the sequence number. */
if ((tls_session -> nx_secure_tls_local_sequence_number[0] + 1) == 0)
{
/* Check for overflow of the 32-bit number. */
tls_session -> nx_secure_tls_local_sequence_number[1]++;
if (tls_session -> nx_secure_tls_local_sequence_number[1] == 0)
{
/* Check for overflow of the 64-bit unsigned number. As it should not reach here
in practical, we return a general error to prevent overflow theoretically. */
return(NX_NOT_SUCCESSFUL);
}
}
tls_session -> nx_secure_tls_local_sequence_number[0]++;
/* DTLS message length. */
message_length = length;
record_header[11] = (UCHAR)((message_length & 0xFF00) >> 8);
record_header[12] = (UCHAR)(message_length & 0x00FF);
/* If the session is active, hash and encrypt the record payload using
the session keys and chosen ciphersuite. */
if (tls_session -> nx_secure_tls_local_session_active)
{
/* Select our proper MAC secret for hashing. */
if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_SERVER)
{
/* If we are a server, we need to use the client's MAC secret. */
mac_secret = tls_session -> nx_secure_tls_key_material.nx_secure_tls_server_write_mac_secret;
}
else
{
/* We are a client, so use the server's MAC secret. */
mac_secret = tls_session -> nx_secure_tls_key_material.nx_secure_tls_client_write_mac_secret;
}
if (send_packet -> nx_packet_next)
{
/* Chained packet is not supported. */
return(NX_SECURE_TLS_PACKET_BUFFER_TOO_SMALL);
}
/* Generate the hash on the plaintext data. */
_nx_secure_dtls_hash_record(dtls_session, tls_session -> nx_secure_tls_local_sequence_number, record_header,
NX_SECURE_DTLS_RECORD_HEADER_SIZE, data, length, record_hash, &hash_length, mac_secret);
if ((hash_length > ((ULONG)(send_packet -> nx_packet_data_end) - (ULONG)(&data[length]))) ||
(hash_length > sizeof(record_hash)))
{
/* Packet buffer is too small. */
return(NX_SECURE_TLS_PACKET_BUFFER_TOO_SMALL);
}
/* Append the hash to the plaintext data before encryption. */
NX_SECURE_MEMCPY(&data[length], record_hash, hash_length); /* Use case of memcpy is verified. */
#ifdef NX_SECURE_KEY_CLEAR
NX_SECURE_MEMSET(record_hash, 0, hash_length);
#endif /* NX_SECURE_KEY_CLEAR */
send_packet -> nx_packet_append_ptr = send_packet -> nx_packet_append_ptr + hash_length;
send_packet -> nx_packet_length = send_packet -> nx_packet_length + hash_length;
/* Finally, encrypt the entire record including the hash. Note that the length
* can be changed by the encryption as IVs and padding may be added. */
_nx_secure_tls_record_payload_encrypt(tls_session, send_packet, (ULONG *)epoch_seq_num, record_type);
}
/* The encryption above may have changed the payload length, so get the length from
the packet and use it to update the record header. */
message_length = send_packet -> nx_packet_length;
/* Set the length of the record following encryption. */
record_header[11] = (UCHAR)((message_length & 0xFF00) >> 8);
record_header[12] = (UCHAR)(message_length & 0x00FF);
/* Back off the prepend_ptr for TLS Record header. Note the packet_length field is adjusted
prior to nx_tcp_socket_send() */
send_packet -> nx_packet_prepend_ptr -= NX_SECURE_DTLS_RECORD_HEADER_SIZE;
/* Adjust packet length */
send_packet -> nx_packet_length += NX_SECURE_DTLS_RECORD_HEADER_SIZE;
/* DTLS handshake re-transmit handling. */
if ((record_type == NX_SECURE_TLS_HANDSHAKE) ||
(record_type == NX_SECURE_TLS_CHANGE_CIPHER_SPEC))
{
/* See if the list already has entries. */
if (dtls_session -> nx_secure_dtls_transmit_sent_head)
{
/* Other packets are on the list already, add this one to the tail. */
(dtls_session -> nx_secure_dtls_transmit_sent_tail) -> nx_packet_union_next.nx_packet_tcp_queue_next = send_packet;
dtls_session -> nx_secure_dtls_transmit_sent_tail = send_packet;
}
else
{
/* Empty list, just setup the head and tail to the current packet. */
dtls_session -> nx_secure_dtls_transmit_sent_head = send_packet;
dtls_session -> nx_secure_dtls_transmit_sent_tail = send_packet;
/* Setup a timeout for the packet at the head of the list. */
dtls_session -> nx_secure_dtls_handshake_timeout = NX_SECURE_DTLS_RETRANSMIT_TIMEOUT;
dtls_session -> nx_secure_dtls_timeout_retries = 0;
}
/* Increase the count of packets in the transmit queue. */
dtls_session -> nx_secure_dtls_transmit_sent_count++;
/* Mark the packet as in the transmit queue so it isn't released by the send. */
send_packet -> nx_packet_union_next.nx_packet_tcp_queue_next = (NX_PACKET *)NX_PACKET_ENQUEUED;
}
/* Release the protection before suspending on nx_tcp_socket_send. */
tx_mutex_put(&_nx_secure_tls_protection);
/* If local IP address index is set, call _nxd_udp_socket_source_send
to ensure the source IP address is correct. */
if (dtls_session -> nx_secure_dtls_local_ip_address_index == 0xffffffff)
{
/* Send the UDP packet(s) containing our record. */
status = _nxd_udp_socket_send(dtls_session -> nx_secure_dtls_udp_socket, send_packet,
&dtls_session -> nx_secure_dtls_remote_ip_address,
dtls_session -> nx_secure_dtls_remote_port);
}
else
{
/* Send the UDP packet(s) containing our record. */
status = _nxd_udp_socket_source_send(dtls_session -> nx_secure_dtls_udp_socket, send_packet,
&dtls_session -> nx_secure_dtls_remote_ip_address,
dtls_session -> nx_secure_dtls_remote_port,
dtls_session -> nx_secure_dtls_local_ip_address_index);
}
/* Get the protection after nx_tcp_socket_send. */
tx_mutex_get(&_nx_secure_tls_protection, TX_WAIT_FOREVER);
if (status != NX_SUCCESS)
{
return(NX_SECURE_TLS_TCP_SEND_FAILED);
}
return(NX_SECURE_TLS_SUCCESS);
}
#endif /* NX_SECURE_ENABLE_DTLS */
|