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