summaryrefslogtreecommitdiff
path: root/nx_secure/src/nx_secure_dtls_process_record.c
blob: 2287df2585a45fffeefd110f3e70f006b4158778 (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
/***************************************************************************
 * 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
/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _nx_secure_dtls_process_record                      PORTABLE C      */
/*                                                           6.1.10       */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Timothy Stapko, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function processes a single DTLS record, handling both         */
/*    handshake and application records. When multiple records are        */
/*    received in a single lower-level network packet (e.g. UDP), the     */
/*    "record_offset" parameter is used to offset into the packet buffer. */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    dtls_session                          Pointer to DTLS control block */
/*    packet_ptr                            NX_PACKET containing a record */
/*    record_offset                         Offset of record in packet    */
/*    bytes_processed                       Return for size of packet     */
/*    wait_option                           Control timeout options       */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    status                                Completion status             */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _nx_secure_dtls_client_handshake      DTLS Client state machine     */
/*    _nx_secure_dtls_process_header        Process record header         */
/*    _nx_secure_dtls_server_handshake      DTLS Server state machine     */
/*    _nx_secure_dtls_verify_mac            Verify record MAC checksum    */
/*    _nx_secure_tls_process_changecipherspec                             */
/*                                          Process ChangeCipherSpec      */
/*    _nx_secure_tls_record_payload_decrypt Decrypt record data           */
/*    nx_secure_tls_packet_release          Release packet                */
/*    tx_mutex_get                          Get protection mutex          */
/*    tx_mutex_put                          Put protection mutex          */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    _nx_secure_dtls_session_receive       Receive DTLS data             */
/*                                                                        */
/**************************************************************************/
UINT _nx_secure_dtls_process_record(NX_SECURE_DTLS_SESSION *dtls_session, NX_PACKET *packet_ptr,
                                    ULONG record_offset, ULONG *bytes_processed, ULONG wait_option)
{
UINT                   status;
UINT                   error_status;
USHORT                 header_length;
UCHAR                  header_data[NX_SECURE_DTLS_RECORD_HEADER_SIZE]; /* DTLS record header is larger than TLS. Allocate enough space for both. */
USHORT                 message_type;
UINT                   message_length;
UCHAR                 *packet_data;
NX_SECURE_TLS_SESSION *tls_session;
UCHAR                  epoch_seq_num[8];
ULONG                  seq_num_rewind[2];
ULONG                  window_rewind;
NX_PACKET             *decrypted_packet;

    /* Basic state machine:
     * 1. Process header, which will set the state and return some data.
     * 2. Advance the packet pointer by the size of the header (returned by header processing)
     *    and process the record.
     * 3. Take any actions necessary based on state.
     */

    /* Get a reference to basic TLS state. */
    tls_session = &dtls_session -> nx_secure_dtls_tls_session;

    header_length = NX_SECURE_DTLS_RECORD_HEADER_SIZE;

    /* Save the sequence number in case we reject a record (e.g. handshake message missing). */
    seq_num_rewind[0] = tls_session -> nx_secure_tls_remote_sequence_number[0];
    seq_num_rewind[1] = tls_session -> nx_secure_tls_remote_sequence_number[1];
    window_rewind = dtls_session -> nx_secure_dtls_sliding_window;

    /* Process the DTLS record header, which will set the state. */
    status = _nx_secure_dtls_process_header(dtls_session, packet_ptr, record_offset, &message_type, &message_length, header_data, &header_length);

    if (status == NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE || status == NX_SECURE_TLS_INVALID_EPOCH || status == NX_SECURE_TLS_REPEAT_MESSAGE_RECEIVED)
    {
        /* Received an out-of-order message. Ignore it. */
        *bytes_processed = (ULONG)header_length + message_length;

        /* Pretend this record never happened. */
        return(NX_CONTINUE);
    }

    if (status != NX_SECURE_TLS_SUCCESS)
    {
        return(status);
    }

    /* Ignore empty records. */
    if (message_length == 0)
    {
        /* Update the number of bytes we processed. */
        *bytes_processed = (ULONG)header_length + message_length;
        return(NX_CONTINUE);
    }

    if (((ULONG)(packet_ptr -> nx_packet_append_ptr) - (ULONG)(packet_ptr -> nx_packet_prepend_ptr)) <
        ((ULONG)header_length + message_length))
    {

        /* Chained packet is not supported. */
        return(NX_SECURE_TLS_INVALID_PACKET);
    }

    /* Now extract the record. */
    packet_data = &packet_ptr -> nx_packet_prepend_ptr[header_length];

    /*status = nx_packet_data_extract_offset(packet_ptr, header_length + record_offset, packet_data, message_length, &bytes_copied);*/

    /* Update the number of bytes we processed. */
    *bytes_processed = (ULONG)header_length + message_length;

    /* Check for active encryption of incoming records. If encrypted, decrypt before further processing. */
    if (tls_session -> nx_secure_tls_remote_session_active)
    {
        epoch_seq_num[0] = header_data[10];
        epoch_seq_num[1] = header_data[9];
        epoch_seq_num[2] = header_data[8];
        epoch_seq_num[3] = header_data[7];
        epoch_seq_num[4] = header_data[6];
        epoch_seq_num[5] = header_data[5];
        epoch_seq_num[6] = header_data[4];
        epoch_seq_num[7] = header_data[3];

        /* Decrypt the record data. */
        status = _nx_secure_tls_record_payload_decrypt(tls_session, packet_ptr, header_length, message_length,
                                                       &decrypted_packet, (ULONG *)epoch_seq_num, (UCHAR)message_type,
                                                       wait_option);

        /* Check the MAC hash. */
        if (status == NX_SECURE_TLS_SUCCESS)
        {

            /* Verify the hash MAC in the decrypted record. */
            packet_data = decrypted_packet -> nx_packet_prepend_ptr;
            message_length = (UINT)decrypted_packet -> nx_packet_length;
            error_status = NX_SECURE_TLS_SUCCESS;
        }
        else
        {
            
            /* Save off the error status so we can return it after the mac check. */
            error_status = status;
        }

        /* !!! NOTE - the MAC check MUST always be performed regardless of the error state of
                      the payload decryption operation. Skipping the MAC check on padding failures
                      could enable a timing-based attack allowing an attacker to determine whether
                      padding was valid or not, causing an information leak. */
        status = _nx_secure_dtls_verify_mac(dtls_session, header_data, header_length, packet_data, &message_length);

        /* Check to see if decryption or verification failed. */
        if(error_status != NX_SECURE_TLS_SUCCESS)
        {
            /* Decryption failed. Rewind the sequence number and sliding window. */
            tls_session -> nx_secure_tls_remote_sequence_number[0] = seq_num_rewind[0];
            tls_session -> nx_secure_tls_remote_sequence_number[1] = seq_num_rewind[1];
            dtls_session -> nx_secure_dtls_sliding_window = window_rewind;
            return(error_status);
        }

        /* Check to see if decryption or verification failed. */
        if (status == NX_SECURE_TLS_SUCCESS)
        {
            /* Copy data to original packet to keep IP header available. */
            /* Note: At the point of this memcpy the plaintext should never be larger than the cipher.
               Assertion check is to protect against future changes inadvertently causing an overflow. */
            NX_ASSERT((ULONG)(packet_ptr -> nx_packet_data_end - packet_ptr -> nx_packet_prepend_ptr) >= message_length);
            NX_SECURE_MEMCPY(packet_ptr -> nx_packet_prepend_ptr, packet_data, message_length); /* Use case of memcpy is verified. */
            packet_data = packet_ptr -> nx_packet_prepend_ptr;
            packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_prepend_ptr + message_length;
            packet_ptr -> nx_packet_length = message_length;
        }

        /* Release decrypted packet. */
        nx_secure_tls_packet_release(decrypted_packet);

        /* Check to see if decryption or verification failed. */
        if (status != NX_SECURE_TLS_SUCCESS)
        {
            return(status);
        }
    }

    switch (message_type)
    {
    case NX_SECURE_TLS_CHANGE_CIPHER_SPEC:
        /* Received a ChangeCipherSpec message - from now on all messages from remote host
           will be encrypted using the session keys. */
        status = _nx_secure_tls_process_changecipherspec(tls_session, packet_data, message_length);
        if(status != NX_SUCCESS)
        {
            /* Received out-of-order CCS message. */
            status = NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE;
        }
        else
        {
            /* New epoch if we receive a CCS message. */
            dtls_session -> nx_secure_dtls_remote_epoch = (USHORT)(dtls_session -> nx_secure_dtls_remote_epoch + 1);
        }

        break;
    case NX_SECURE_TLS_ALERT:
        /* We have received an alert. Check what the alert was and take appropriate action. */

        /* Save off the alert level and value for the application to access. */
        tls_session->nx_secure_tls_received_alert_level = packet_data[0];
        tls_session->nx_secure_tls_received_alert_value = packet_data[1];

        status = NX_SECURE_TLS_ALERT_RECEIVED;
        break;
    case NX_SECURE_TLS_HANDSHAKE:
#ifndef NX_SECURE_TLS_SERVER_DISABLED
        /* The socket is a TLS server, so process incoming handshake messages in that context. */
        if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_SERVER)
        {
            status = _nx_secure_dtls_server_handshake(dtls_session, packet_data, message_length, wait_option);
        }
#endif
#ifndef NX_SECURE_TLS_CLIENT_DISABLED
        /* The socket is a TLS client, so process incoming handshake messages in that context. */
        if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_CLIENT)
        {
            status = _nx_secure_dtls_client_handshake(dtls_session, packet_data, message_length, wait_option);
        }
#endif
        break;
    case NX_SECURE_TLS_APPLICATION_DATA:
        /* The remote host is sending application data records now. Pass decrypted data back
           to the networking API for the application to process. */

        /* First, check for 0-length records. These can be sent but are internal to TLS so tell the
           caller to continue receiving. 0-length records are sent as part of the BEAST attack
           mitigation by some TLS implementations (notably OpenSSL). */
        if (message_length == 0)
        {
            status = NX_CONTINUE;
        }
        break;
    default:
        /* An unrecognized message was received, likely an error, possibly an attack. */
        status = NX_SECURE_TLS_UNRECOGNIZED_MESSAGE_TYPE;
        break;
    }

    /* If we received an out-of-order handshake message, rewind the sequence number. */
    if(status == NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE)
    {
        /* Handshake message was not what we expected, so rewind the sequence number. */
        tls_session -> nx_secure_tls_remote_sequence_number[0] = seq_num_rewind[0];
        tls_session -> nx_secure_tls_remote_sequence_number[1] = seq_num_rewind[1];
        dtls_session -> nx_secure_dtls_sliding_window = window_rewind;
        status = NX_CONTINUE;
    }


    return(status);
}
#endif /* NX_SECURE_ENABLE_DTLS */