summaryrefslogtreecommitdiff
path: root/nx_secure/src/nx_secure_tls_send_record.c
blob: 56f51e50e4be2a11a76ebb405cb9167de662da57 (plain)
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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/***************************************************************************
 * 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                                                 */
/**                                                                       */
/**    Transport Layer Security (TLS)                                     */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define NX_SECURE_SOURCE_CODE

#include "nx_secure_tls.h"

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _nx_secure_tls_send_record                          PORTABLE C      */
/*                                                           6.1.12       */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Timothy Stapko, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function encapsulates the TLS record layer send                */
/*    functionality. The incoming packet data is wrapped in               */
/*    a TLS record, which includes a header and footer (for               */
/*    encrypted data). Also, all encryption of application                */
/*    data is handled here.                                               */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    tls_session                           Pointer to TLS session        */
/*    send_packet                           Packet data to send           */
/*    record_type                           TLS record type               */
/*    wait_option                           Suspension option             */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    status                                Record send status            */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _nx_secure_tls_record_hash_calculate  Calculate hash of record      */
/*    _nx_secure_tls_record_hash_initialize Initialize hash of record     */
/*    _nx_secure_tls_record_hash_update     Update hash of record         */
/*    _nx_secure_tls_record_payload_encrypt Encrypt payload               */
/*    _nx_secure_tls_session_iv_size_get    Get IV size for this session. */
/*    nx_packet_data_append                 Append data to packet         */
/*    nx_tcp_socket_send                    Send packet                   */
/*    tx_mutex_get                          Get protection mutex          */
/*    tx_mutex_put                          Put protection mutex          */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    _nx_secure_tls_client_handshake       TLS client state machine      */
/*    _nx_secure_tls_send_handshake_record  Send TLS handshake record     */
/*    _nx_secure_tls_server_handshake       TLS server state machine      */
/*    _nx_secure_tls_session_end            End of a session              */
/*    _nx_secure_tls_session_receive_records                              */
/*                                          Receive TLS records           */
/*    _nx_secure_tls_session_send           Send session packet           */
/*                                                                        */
/**************************************************************************/
UINT _nx_secure_tls_send_record(NX_SECURE_TLS_SESSION *tls_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;
UCHAR     *hash_data;
ULONG      hash_data_length;
ULONG      length;
USHORT     iv_size = 0;
NX_PACKET *current_packet;

    /* Length of the data in the packet. */
    length = send_packet -> nx_packet_length;

    if ((tls_session -> nx_secure_tls_tcp_socket) &&
        (tls_session -> nx_secure_tls_tcp_socket -> nx_tcp_socket_ip_ptr) &&
        (tx_thread_identify() == &(tls_session -> nx_secure_tls_tcp_socket -> nx_tcp_socket_ip_ptr -> nx_ip_thread)))
    {

        /* No wait is allowed for IP thread to avoid dead lock. */
        wait_option = 0;
    }

    tx_mutex_put(&_nx_secure_tls_protection);

    /* Get transmit mutex first. */
    status = tx_mutex_get(&(tls_session -> nx_secure_tls_session_transmit_mutex), TX_WAIT_FOREVER);

    tx_mutex_get(&_nx_secure_tls_protection, TX_WAIT_FOREVER);

    if (status)
    {

        /* Unable to send due to another thread is still transmitting. */
        return(NX_SECURE_TLS_TRANSMIT_LOCKED);
    }

    /* See if this is an active session, we need to account for the IV if the session cipher
       uses one. TLS 1.3 does not use an explicit IV so don't add it.*/
    if (tls_session -> nx_secure_tls_local_session_active
#if (NX_SECURE_TLS_TLS_1_3_ENABLED)
        && !tls_session->nx_secure_tls_1_3
#endif
        )
    {

        /* 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)
        {
            tx_mutex_put(&(tls_session -> nx_secure_tls_session_transmit_mutex));
            return(status);
        }

        /* Ensure there is enough room for the IV data.  */
        if ((ULONG)(send_packet -> nx_packet_prepend_ptr - send_packet -> nx_packet_data_start) < iv_size)
        {

            /* Return an invalid packet error.  */
            tx_mutex_put(&(tls_session -> nx_secure_tls_session_transmit_mutex));
            return(NX_SECURE_TLS_INVALID_PACKET);
        }

        /* 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;
    }

    /* Ensure there is enough room for the record header.  */
    if ((ULONG)(send_packet -> nx_packet_prepend_ptr - send_packet -> nx_packet_data_start) < NX_SECURE_TLS_RECORD_HEADER_SIZE)
    {

        /* Return an invalid packet error.  */
        tx_mutex_put(&(tls_session -> nx_secure_tls_session_transmit_mutex));
        return(NX_SECURE_TLS_INVALID_PACKET);
    }

    /* Get a pointer to our record header which is now right after the prepend pointer. */
    record_header = send_packet -> nx_packet_prepend_ptr - NX_SECURE_TLS_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);

    /* Set the length of the record prior to hashing and encryption - this is because
       the hashing is done on the record as if it were not encrypted so any additional
       padding or IVs, etc. added to the length would invalidate the hash. We update
       the length following the encryption below. */
    message_length = length;
    record_header[3] = (UCHAR)((length & 0xFF00) >> 8);
    record_header[4] = (UCHAR)(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)
    {
        /*************************************************************************************************************/
        NX_ASSERT(tls_session -> nx_secure_tls_session_ciphersuite != NX_NULL)

#if (NX_SECURE_TLS_TLS_1_3_ENABLED)
        /* TLS 1.3 records have the record type appended in a single byte. */
        if(tls_session->nx_secure_tls_1_3)
        {
            /* If in a TLS 1.3 encrypted session, write the message type to the end. */
            status = nx_packet_data_append(send_packet, (UCHAR*)(&record_type), 1,
                                           tls_session -> nx_secure_tls_packet_pool, wait_option);

            if(status != NX_SUCCESS)
            {
                tx_mutex_put(&(tls_session -> nx_secure_tls_session_transmit_mutex));
                return(status);
            }

            /* Record header type is APPLICATION DATA for all TLS 1.3 encrypted records. */
            record_type = NX_SECURE_TLS_APPLICATION_DATA;
            record_header[0] = record_type;
        }
#endif

        /* TLS 1.3 does uses AEAD instead of per-record hash MACs. */
        if (
#if (NX_SECURE_TLS_TLS_1_3_ENABLED)
            !tls_session->nx_secure_tls_1_3 && 
#endif
            tls_session -> nx_secure_tls_session_ciphersuite -> nx_secure_tls_hash -> nx_crypto_operation)
        {

            /***** HASHING *****/
            /* 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;
            }

            /* Account for large records that exceed the packet size and are chained in multiple packets
               such as large certificate messages with multiple certificates.
               tls_session->nx_secure_hash_mac_metadata_area is persistent across the following calls, so it's important
               to not do anything that might change the contents of that buffer until the hash is calculated after the loop! */
            current_packet = send_packet;

            /* Initialize the hash routine with our MAC secret, sequence number, and header. */
            status = _nx_secure_tls_record_hash_initialize(tls_session, tls_session -> nx_secure_tls_local_sequence_number,
                                                           record_header, 5, &hash_length, mac_secret);

            /* Check return from hash routine initialization. */
            if (status != NX_SUCCESS)
            {
                tx_mutex_put(&(tls_session -> nx_secure_tls_session_transmit_mutex));
                return(status);
            }

            /* Start the hash data after the header and IV. */
            hash_data = current_packet -> nx_packet_prepend_ptr + iv_size;
            hash_data_length = (ULONG)(current_packet -> nx_packet_append_ptr - current_packet -> nx_packet_prepend_ptr) - iv_size;

            /* Walk packet chain. */
            do
            {
                /* Update the hash with the data. */
                status = _nx_secure_tls_record_hash_update(tls_session, hash_data,
                                                           (UINT)hash_data_length);

                /* Advance the packet pointer to the next packet in the chain. */
                current_packet = current_packet -> nx_packet_next;
                if (current_packet != NX_NULL)
                {
                    hash_data = current_packet -> nx_packet_prepend_ptr;
                    hash_data_length = (ULONG)(current_packet -> nx_packet_append_ptr - current_packet -> nx_packet_prepend_ptr);
                }
            } while (current_packet != NX_NULL);

            /* Generate the hash on the plaintext data. */
            _nx_secure_tls_record_hash_calculate(tls_session, record_hash, &hash_length);

            /* Release the protection before suspending on nx_packet_data_append. */
            tx_mutex_put(&_nx_secure_tls_protection);

            /* Append the hash to the plaintext data in the last packet before encryption. */
            status = nx_packet_data_append(send_packet, record_hash, hash_length,
                                           tls_session -> nx_secure_tls_packet_pool, wait_option);

#ifdef NX_SECURE_KEY_CLEAR
            NX_SECURE_MEMSET(record_hash, 0, sizeof(record_hash));
#endif /* NX_SECURE_KEY_CLEAR  */

            /* Get the protection after nx_packet_data_append. */
            tx_mutex_get(&_nx_secure_tls_protection, TX_WAIT_FOREVER);
        }


        /*************************************************************************************************************/
        /***** ENCRYPTION *****/

        status = _nx_secure_tls_record_payload_encrypt(tls_session, send_packet, tls_session -> nx_secure_tls_local_sequence_number, record_type);

        if (status != NX_SUCCESS)
        {
            tx_mutex_put(&(tls_session -> nx_secure_tls_session_transmit_mutex));
            return(status);
        }

        /*************************************************************************************************************/

        /* 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]++;
    }

    /* 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. */
    record_header[3] = (UCHAR)((message_length & 0xFF00) >> 8);
    record_header[4] = (UCHAR)(message_length & 0x00FF);

    /* Adjust packet length */
    send_packet -> nx_packet_prepend_ptr -= NX_SECURE_TLS_RECORD_HEADER_SIZE;
    send_packet -> nx_packet_length += NX_SECURE_TLS_RECORD_HEADER_SIZE;

    /* Release the protection before suspending on nx_tcp_socket_send. */
    tx_mutex_put(&_nx_secure_tls_protection);

    /* Send the TCP packet(s) containing our record. */
    status = nx_tcp_socket_send(tls_session -> nx_secure_tls_tcp_socket, send_packet, wait_option);

#ifdef NX_SECURE_KEY_CLEAR
    if (tls_session -> nx_secure_tls_local_session_active)
    {

        /* Clear all data in chained packet. */
        current_packet = send_packet;
        while (current_packet)
        {
            NX_SECURE_MEMSET(current_packet -> nx_packet_prepend_ptr, 0,
                   (ULONG)current_packet -> nx_packet_append_ptr -
                   (ULONG)current_packet -> nx_packet_prepend_ptr);
            current_packet = current_packet -> nx_packet_next;
        }
    }
#endif /* NX_SECURE_KEY_CLEAR  */

    /* Get the protection after nx_tcp_socket_send. */
    tx_mutex_get(&_nx_secure_tls_protection, TX_WAIT_FOREVER);

    /* Release transmit mutex. */
    tx_mutex_put(&(tls_session -> nx_secure_tls_session_transmit_mutex));

    return(status);
}