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 #include "nx_secure_tls.h"
25 
26 /**************************************************************************/
27 /*                                                                        */
28 /*  FUNCTION                                               RELEASE        */
29 /*                                                                        */
30 /*    _nx_secure_tls_hash_record                          PORTABLE C      */
31 /*                                                           6.2.0        */
32 /*  AUTHOR                                                                */
33 /*                                                                        */
34 /*    Timothy Stapko, Microsoft Corporation                               */
35 /*                                                                        */
36 /*  DESCRIPTION                                                           */
37 /*                                                                        */
38 /*    This function hashes an outgoing TLS record to generate the Message */
39 /*    Authentication Code (MAC) value that is placed at the end of all    */
40 /*    encrypted TLS messages.                                             */
41 /*                                                                        */
42 /*  INPUT                                                                 */
43 /*                                                                        */
44 /*    tls_session                           TLS control block             */
45 /*    sequence_num                          Record sequence number        */
46 /*    header                                Record header                 */
47 /*    header_length                         Length of record header       */
48 /*    packet_ptr                            TLS record packet             */
49 /*    offset                                Offset to TLS record in packet*/
50 /*    length                                Length of payload data        */
51 /*    record_hash                           Pointer to output hash buffer */
52 /*    hash_length                           Length of hash                */
53 /*    mac_secret                            Key used for MAC generation   */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    status                                Completion status             */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    [nx_crypto_operation]                 Crypto operation              */
62 /*                                                                        */
63 /*  CALLED BY                                                             */
64 /*                                                                        */
65 /*    _nx_secure_tls_verify_mac             Verify record MAC checksum    */
66 /*                                                                        */
67 /*  RELEASE HISTORY                                                       */
68 /*                                                                        */
69 /*    DATE              NAME                      DESCRIPTION             */
70 /*                                                                        */
71 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
72 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
73 /*                                            supported chained packet,   */
74 /*                                            resulting in version 6.1    */
75 /*  06-02-2021     Timothy Stapko           Modified comment(s),          */
76 /*                                            fixed compiler warning,     */
77 /*                                            resulting in version 6.1.7  */
78 /*  10-31-2022     Yanwu Cai                Modified comment(s),          */
79 /*                                            adjusted parameters list,   */
80 /*                                            resulting in version 6.2.0  */
81 /*                                                                        */
82 /**************************************************************************/
_nx_secure_tls_hash_record(const NX_SECURE_TLS_CIPHERSUITE_INFO * ciphersuite,ULONG sequence_num[NX_SECURE_TLS_SEQUENCE_NUMBER_SIZE],UCHAR * header,UINT header_length,NX_PACKET * packet_ptr,ULONG offset,UINT length,UCHAR * record_hash,UINT * hash_length,UCHAR * mac_secret,VOID * metadata,ULONG metadata_size)83 UINT _nx_secure_tls_hash_record(const NX_SECURE_TLS_CIPHERSUITE_INFO *ciphersuite,
84                                 ULONG sequence_num[NX_SECURE_TLS_SEQUENCE_NUMBER_SIZE],
85                                 UCHAR *header, UINT header_length, NX_PACKET *packet_ptr,
86                                 ULONG offset, UINT length, UCHAR *record_hash, UINT *hash_length,
87                                 UCHAR *mac_secret, VOID *metadata, ULONG metadata_size)
88 {
89 UINT                    hash_size;
90 UINT                    status = NX_SECURE_TLS_MISSING_CRYPTO_ROUTINE;
91 const NX_CRYPTO_METHOD *authentication_method;
92 UCHAR                   adjusted_sequence_num[8];
93 VOID                   *handler = NX_NULL;
94 ULONG                   current_length;
95 
96     NX_PARAMETER_NOT_USED(header_length);
97 
98     /* We need to generate a Message Authentication Code (MAC) for each record during an "active" TLS session
99        (following a ChangeCipherSpec message). The hash algorithm is determined by the ciphersuite, and HMAC
100        is used with that hash algorithm to protect the TLS record contents from tampering.
101 
102        The MAC is generated as:
103 
104        HMAC_hash(MAC_write_secret, seq_num + type + version + length + data);
105 
106      */
107 
108     /* Get our authentication method from the ciphersuite. */
109     authentication_method = ciphersuite -> nx_secure_tls_hash;
110 
111     /* Get the hash size and MAC secret for our current session. */
112     hash_size = ciphersuite -> nx_secure_tls_hash_size;
113 
114     /* Correct the endianness of our sequence number before hashing. */
115     adjusted_sequence_num[0] = (UCHAR)(sequence_num[1] >> 24);
116     adjusted_sequence_num[1] = (UCHAR)(sequence_num[1] >> 16);
117     adjusted_sequence_num[2] = (UCHAR)(sequence_num[1] >> 8);
118     adjusted_sequence_num[3] = (UCHAR)(sequence_num[1]);
119     adjusted_sequence_num[4] = (UCHAR)(sequence_num[0] >> 24);
120     adjusted_sequence_num[5] = (UCHAR)(sequence_num[0] >> 16);
121     adjusted_sequence_num[6] = (UCHAR)(sequence_num[0] >> 8);
122     adjusted_sequence_num[7] = (UCHAR)(sequence_num[0]);
123 
124     if (authentication_method -> nx_crypto_init)
125     {
126         status = authentication_method -> nx_crypto_init((NX_CRYPTO_METHOD*)authentication_method,
127                                                 mac_secret,
128                                                 (NX_CRYPTO_KEY_SIZE)(hash_size << 3),
129                                                 &handler,
130                                                 metadata,
131                                                 metadata_size);
132 
133         if(status != NX_CRYPTO_SUCCESS)
134         {
135             return(status);
136         }
137     }
138 
139     /* TLS header type, version, and length are in the proper order. */
140     if (authentication_method -> nx_crypto_operation != NX_NULL)
141     {
142         status = authentication_method -> nx_crypto_operation(NX_CRYPTO_HASH_INITIALIZE,
143                                                      handler,
144                                                      (NX_CRYPTO_METHOD*)authentication_method,
145                                                      mac_secret,
146                                                      (NX_CRYPTO_KEY_SIZE)(hash_size << 3),
147                                                      NX_NULL,
148                                                      0,
149                                                      NX_NULL,
150                                                      NX_NULL,
151                                                      0,
152                                                      metadata,
153                                                      metadata_size,
154                                                      NX_NULL,
155                                                      NX_NULL);
156 
157         if(status != NX_CRYPTO_SUCCESS)
158         {
159             return(status);
160         }
161 
162         status = authentication_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
163                                                      handler,
164                                                      (NX_CRYPTO_METHOD*)authentication_method,
165                                                      NX_NULL,
166                                                      0,
167                                                      adjusted_sequence_num,
168                                                      8,
169                                                      NX_NULL,
170                                                      NX_NULL,
171                                                      0,
172                                                      metadata,
173                                                      metadata_size,
174                                                      NX_NULL,
175                                                      NX_NULL);
176 
177         if(status != NX_CRYPTO_SUCCESS)
178         {
179             return(status);
180         }
181 
182         status = authentication_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
183                                                      handler,
184                                                      (NX_CRYPTO_METHOD*)authentication_method,
185                                                      NX_NULL,
186                                                      0,
187                                                      header,
188                                                      5,
189                                                      NX_NULL,
190                                                      NX_NULL,
191                                                      0,
192                                                      metadata,
193                                                      metadata_size,
194                                                      NX_NULL,
195                                                      NX_NULL);
196 
197         if(status != NX_CRYPTO_SUCCESS)
198         {
199             return(status);
200         }
201 
202         /* Locate to start packet of TLS record payload. */
203         while (packet_ptr)
204         {
205             current_length = (ULONG)(packet_ptr -> nx_packet_append_ptr - packet_ptr -> nx_packet_prepend_ptr);
206             if (offset >= current_length)
207             {
208 
209                 /* Move to next packet. */
210                 offset -= current_length;
211                 packet_ptr = packet_ptr -> nx_packet_next;
212             }
213             else
214             {
215 
216                 /* Found offset in current packet. */
217                 break;
218             }
219         }
220 
221         /* Hash TLS record payload. */
222         while ((length > 0) && packet_ptr)
223         {
224             current_length = (ULONG)(packet_ptr -> nx_packet_append_ptr - packet_ptr -> nx_packet_prepend_ptr);
225             current_length -= offset;
226             offset = 0;
227             if (current_length > length)
228             {
229                 current_length = length;
230             }
231             status = authentication_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
232                                                         handler,
233                                                         (NX_CRYPTO_METHOD*)authentication_method,
234                                                         NX_NULL,
235                                                         0,
236                                                         &packet_ptr -> nx_packet_prepend_ptr[offset],
237                                                         current_length,
238                                                         NX_NULL,
239                                                         NX_NULL,
240                                                         0,
241                                                         metadata,
242                                                         metadata_size,
243                                                         NX_NULL,
244                                                         NX_NULL);
245 
246             if(status != NX_CRYPTO_SUCCESS)
247             {
248                 return(status);
249             }
250 
251             length -= current_length;
252             packet_ptr = packet_ptr -> nx_packet_next;
253         }
254 
255         if (length > 0)
256         {
257 
258             /* Not all TLS record payload is hashed. */
259             return(NX_SECURE_TLS_INVALID_PACKET);
260         }
261 
262         status = authentication_method -> nx_crypto_operation(NX_CRYPTO_HASH_CALCULATE,
263                                                      handler,
264                                                      (NX_CRYPTO_METHOD*)authentication_method,
265                                                      NX_NULL,
266                                                      0,
267                                                      NX_NULL,
268                                                      0,
269                                                      NX_NULL,
270                                                      record_hash,
271                                                      NX_SECURE_TLS_MAX_HASH_SIZE,
272                                                      metadata,
273                                                      metadata_size,
274                                                      NX_NULL,
275                                                      NX_NULL);
276 
277         if(status != NX_CRYPTO_SUCCESS)
278         {
279             return(status);
280         }
281 #ifdef NX_SECURE_KEY_CLEAR
282         NX_SECURE_MEMSET(adjusted_sequence_num, 0, 8);
283 #endif /* NX_SECURE_KEY_CLEAR  */
284     }
285 
286     if (authentication_method -> nx_crypto_cleanup)
287     {
288         status = authentication_method -> nx_crypto_cleanup(metadata);
289 
290         if(status != NX_CRYPTO_SUCCESS)
291         {
292             return(status);
293         }
294     }
295 
296     /* Return how many bytes our hash is since the caller doesn't necessarily know. */
297     *hash_length = hash_size;
298 
299     return(status);
300 }
301 
302