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 static UCHAR _generated_hash[NX_SECURE_TLS_MAX_HASH_SIZE];
27 static UCHAR _received_hash[NX_SECURE_TLS_MAX_HASH_SIZE];
28 
29 /**************************************************************************/
30 /*                                                                        */
31 /*  FUNCTION                                               RELEASE        */
32 /*                                                                        */
33 /*    _nx_secure_verify_mac                               PORTABLE C      */
34 /*                                                           6.2.0        */
35 /*  AUTHOR                                                                */
36 /*                                                                        */
37 /*    Yanwu Cai, Microsoft Corporation                                    */
38 /*                                                                        */
39 /*  DESCRIPTION                                                           */
40 /*                                                                        */
41 /*    This function verifies the Message Authentication Code (MAC) that   */
42 /*    is included in encrypted TLS records. It hashes the incoming        */
43 /*    message data and then compares it to the MAC in the received        */
44 /*    record. If there is a mismatch, then the record has been corrupted  */
45 /*    in transit and represents a possible security breach.               */
46 /*                                                                        */
47 /*  INPUT                                                                 */
48 /*                                                                        */
49 /*    ciphersuite                           Selected cipher suite         */
50 /*    mac_secret                            Key used for MAC generation   */
51 /*    sequence_num                          Record sequence number        */
52 /*    header_data                           TLS record header data        */
53 /*    header_length                         Length of header data         */
54 /*    packet_ptr                            TLS record packet             */
55 /*    offset                                Offset to TLS record in packet*/
56 /*    length                                Length of payload data        */
57 /*    hash_mac_metadata                     Metadata for hash mac crypto  */
58 /*    hash_mac_metadata_size                Size of hash mac metadata     */
59 /*                                                                        */
60 /*  OUTPUT                                                                */
61 /*                                                                        */
62 /*    status                                Completion status             */
63 /*                                                                        */
64 /*  CALLS                                                                 */
65 /*                                                                        */
66 /*    _nx_secure_tls_hash_record            Generate payload data hash    */
67 /*                                                                        */
68 /*  CALLED BY                                                             */
69 /*                                                                        */
70 /*    _nx_secure_tls_verify_mac             Verify record MAC checksum    */
71 /*                                                                        */
72 /*  RELEASE HISTORY                                                       */
73 /*                                                                        */
74 /*    DATE              NAME                      DESCRIPTION             */
75 /*                                                                        */
76 /*  10-31-2022     Yanwu Cai                Initial Version 6.2.0         */
77 /*                                                                        */
78 /**************************************************************************/
_nx_secure_verify_mac(const NX_SECURE_TLS_CIPHERSUITE_INFO * ciphersuite,UCHAR * mac_secret,ULONG sequence_num[NX_SECURE_TLS_SEQUENCE_NUMBER_SIZE],UCHAR * header_data,USHORT header_length,NX_PACKET * packet_ptr,ULONG offset,UINT * length,VOID * hash_mac_metadata,ULONG hash_mac_metadata_size)79 UINT _nx_secure_verify_mac(const NX_SECURE_TLS_CIPHERSUITE_INFO *ciphersuite, UCHAR *mac_secret, ULONG sequence_num[NX_SECURE_TLS_SEQUENCE_NUMBER_SIZE],
80                            UCHAR *header_data, USHORT header_length, NX_PACKET *packet_ptr, ULONG offset, UINT *length,
81                            VOID *hash_mac_metadata, ULONG hash_mac_metadata_size)
82 {
83 
84 USHORT hash_size;
85 INT    compare_result;
86 USHORT data_length;
87 UINT   hash_length;
88 UCHAR  header[6];
89 ULONG  bytes_copied;
90 
91 
92 
93     /* Get the hash size and MAC secret for our current session. */
94     hash_size = ciphersuite -> nx_secure_tls_hash_size;
95 
96     /* Check for 0-length records. With nothing to hash, don't continue to hash generation. */
97     if (hash_size >= *length)
98     {
99 
100         if (header_data[0] == (UCHAR)(NX_SECURE_TLS_APPLICATION_DATA) &&
101             *length == hash_size)
102         {
103             /* BEAST attack mitigation. In TLS 1.0 and SSLv3, the implicit IV enables the BEAST
104                attack. Some implementations counter the attack by sending an empty record which
105                has the effect of resetting the IVs. We normally don't allow empty records since there
106                is no data to hash, but in this case we want to allow it. */
107             *length = 0;
108 
109             /* Increment the sequence number. */
110             if ((sequence_num[0] + 1) == 0)
111             {
112                 /* Check for overflow of the 32-bit unsigned number. */
113                 sequence_num[1]++;
114 
115                 if (sequence_num[1] == 0)
116                 {
117 
118                     /* Check for overflow of the 64-bit unsigned number. As it should not reach here
119                        in practical, we return a general error to prevent overflow theoretically. */
120                     return(NX_NOT_SUCCESSFUL);
121                 }
122 
123             }
124             sequence_num[0]++;
125 
126             return(NX_SUCCESS);
127         }
128 
129         /* The record data was smaller than the selected hash... Error. */
130         return(NX_SECURE_TLS_HASH_MAC_VERIFY_FAILURE);
131     }
132 
133     /* Adjust our length so we only hash the record data, not the hash as well. */
134     data_length = (USHORT)(*length - hash_size);
135 
136     /* Copy the header data into our local buffer so we can change it if we need to. */
137     if (header_length > sizeof(header))
138     {
139         return(NX_SECURE_TLS_HASH_MAC_VERIFY_FAILURE);
140     }
141     NX_SECURE_MEMCPY(header, header_data, header_length); /* Use case of memcpy is verified. */
142 
143     /* Adjust the length in the header to match the length of the data before the hash was added. */
144     header[3] = (UCHAR)((data_length >> 8) & 0x00FF);
145     header[4] = (UCHAR)(data_length & 0x00FF);
146 
147     /* Generate the hash on the plaintext data. */
148     _nx_secure_tls_hash_record(ciphersuite, sequence_num, header, header_length,
149                                packet_ptr, offset, data_length, _generated_hash, &hash_length, mac_secret,
150                                hash_mac_metadata, hash_mac_metadata_size);
151 
152     /* Increment the sequence number. */
153     if ((sequence_num[0] + 1) == 0)
154     {
155         /* Check for overflow of the 32-bit unsigned number. */
156         sequence_num[1]++;
157 
158         if (sequence_num[1] == 0)
159         {
160 
161             /* Check for overflow of the 64-bit unsigned number. As it should not reach here
162                in practical, we return a general error to prevent overflow theoretically. */
163             return(NX_NOT_SUCCESSFUL);
164         }
165 
166     }
167     sequence_num[0]++;
168 
169     if (hash_size == 0)
170     {
171 
172         /* For ciphersuite without explict hash, just return success. */
173         return(NX_SECURE_TLS_SUCCESS);
174     }
175 
176     /* Now, compare the hash we generated to the one we received. */
177     if (nx_packet_data_extract_offset(packet_ptr,
178                                       offset + data_length,
179                                       _received_hash, hash_size, &bytes_copied))
180 
181     {
182 
183         /* The record data was smaller than the selected hash... Error. */
184         return(NX_SECURE_TLS_PADDING_CHECK_FAILED);
185     }
186 
187     if (bytes_copied != hash_size)
188     {
189 
190         /* The record data was smaller than the selected hash... Error. */
191         return(NX_SECURE_TLS_PADDING_CHECK_FAILED);
192     }
193 
194     compare_result = NX_SECURE_MEMCMP(_received_hash, _generated_hash, hash_size);
195 
196 #ifdef NX_SECURE_KEY_CLEAR
197     NX_SECURE_MEMSET(_generated_hash, 0, sizeof(_generated_hash));
198 #endif /* NX_SECURE_KEY_CLEAR  */
199 
200     /* Before we return, adjust our data size so the caller will only see data, not the hash. */
201     *length = data_length;
202 
203     /* If the hashes match, we are all good. Otherwise we have a problem. */
204     if (compare_result == 0)
205     {
206         return(NX_SECURE_TLS_SUCCESS);
207     }
208     else
209     {
210         return(NX_SECURE_TLS_HASH_MAC_VERIFY_FAILURE);
211     }
212 }
213 
214