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_generate_premaster_secret PORTABLE C */
31 /* 6.2.1 */
32 /* AUTHOR */
33 /* */
34 /* Yanwu Cai, Microsoft Corporation */
35 /* */
36 /* DESCRIPTION */
37 /* */
38 /* This function generates the Pre-Master Secret for TLS Client */
39 /* instances. It is sent to the remote host and used as the seed for */
40 /* session key generation. */
41 /* */
42 /* INPUT */
43 /* */
44 /* ciphersuite Selected cipher suite */
45 /* protocol_version Selected TLS version */
46 /* tls_key_material TLS key material */
47 /* tls_credentials TLS credentials */
48 /* session_type Server or client session */
49 /* received_remote_credentials Indicates credentials received*/
50 /* public_cipher_metadata Metadata for public cipher */
51 /* public_cipher_metadata_size Size of public cipher metadata*/
52 /* tls_ecc_curves ECC curves */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* status Completion status */
57 /* */
58 /* CALLS */
59 /* */
60 /* _nx_secure_tls_psk_find Find PSK from store */
61 /* _nx_secure_tls_find_curve_method Find named curve used */
62 /* [nx_crypto_init] Initialize crypto */
63 /* [nx_crypto_operation] Crypto operation */
64 /* */
65 /* CALLED BY */
66 /* */
67 /* _nx_secure_tls_generate_premaster_secret */
68 /* Generate pre-master secret */
69 /* */
70 /* RELEASE HISTORY */
71 /* */
72 /* DATE NAME DESCRIPTION */
73 /* */
74 /* 10-31-2022 Yanwu Cai Initial Version 6.2.0 */
75 /* 03-08-2023 Yanwu Cai Modified comment(s), */
76 /* fixed compiler errors when */
77 /* x509 is disabled, */
78 /* resulting in version 6.2.1 */
79 /* */
80 /**************************************************************************/
_nx_secure_generate_premaster_secret(const NX_SECURE_TLS_CIPHERSUITE_INFO * ciphersuite,USHORT protocol_version,NX_SECURE_TLS_KEY_MATERIAL * tls_key_material,NX_SECURE_TLS_CREDENTIALS * tls_credentials,UINT session_type,USHORT * received_remote_credentials,VOID * public_cipher_metadata,ULONG public_cipher_metadata_size,VOID * tls_ecc_curves)81 UINT _nx_secure_generate_premaster_secret(const NX_SECURE_TLS_CIPHERSUITE_INFO *ciphersuite, USHORT protocol_version, NX_SECURE_TLS_KEY_MATERIAL *tls_key_material,
82 NX_SECURE_TLS_CREDENTIALS *tls_credentials, UINT session_type, USHORT *received_remote_credentials,
83 VOID *public_cipher_metadata, ULONG public_cipher_metadata_size, VOID *tls_ecc_curves)
84 {
85 UINT *buffer_ptr;
86 UINT i;
87 UINT status = NX_SECURE_TLS_SUCCESS;
88 #ifdef NX_SECURE_ENABLE_PSK_CIPHERSUITES
89 UCHAR *psk_data;
90 UINT psk_length;
91 UINT index;
92 #endif
93 #if defined(NX_SECURE_ENABLE_ECC_CIPHERSUITE) && !defined(NX_SECURE_DISABLE_X509)
94 NX_SECURE_X509_CERT *server_certificate;
95 const NX_CRYPTO_METHOD *curve_method_cert;
96 const NX_CRYPTO_METHOD *ecdh_method;
97 NX_SECURE_EC_PUBLIC_KEY *ec_pubkey;
98 VOID *handler = NX_NULL;
99 NX_CRYPTO_EXTENDED_OUTPUT extended_output;
100 #endif /* NX_SECURE_ENABLE_ECC_CIPHERSUITE && !NX_SECURE_DISABLE_X509 */
101
102 #if !defined(NX_SECURE_ENABLE_ECC_CIPHERSUITE) || defined(NX_SECURE_DISABLE_X509)
103 NX_PARAMETER_NOT_USED(public_cipher_metadata);
104 NX_PARAMETER_NOT_USED(public_cipher_metadata_size);
105 NX_PARAMETER_NOT_USED(tls_ecc_curves);
106 #endif
107 #ifndef NX_SECURE_ENABLE_PSK_CIPHERSUITES
108 NX_PARAMETER_NOT_USED(session_type);
109 NX_PARAMETER_NOT_USED(ciphersuite);
110 NX_PARAMETER_NOT_USED(tls_credentials);
111 NX_PARAMETER_NOT_USED(public_cipher_metadata);
112 NX_PARAMETER_NOT_USED(public_cipher_metadata_size);
113 NX_PARAMETER_NOT_USED(tls_ecc_curves);
114 #ifndef NX_SECURE_ENABLE_ECJPAKE_CIPHERSUITE
115 NX_PARAMETER_NOT_USED(received_remote_credentials);
116 #endif
117 #endif
118
119 #if defined(NX_SECURE_ENABLE_ECC_CIPHERSUITE) && !defined(NX_SECURE_DISABLE_X509)
120 if (ciphersuite -> nx_secure_tls_public_cipher -> nx_crypto_algorithm == NX_CRYPTO_KEY_EXCHANGE_ECDHE)
121 {
122
123 return(NX_SECURE_TLS_SUCCESS);
124 }
125 else if (ciphersuite -> nx_secure_tls_public_cipher -> nx_crypto_algorithm == NX_CRYPTO_KEY_EXCHANGE_ECDH)
126 {
127 /* Get reference to remote server certificate so we can find out the named curve. */
128 status = _nx_secure_x509_remote_endpoint_certificate_get(&tls_credentials -> nx_secure_tls_certificate_store,
129 &server_certificate);
130 if (status)
131 {
132 /* No certificate found, error! */
133 return(NX_SECURE_TLS_CERTIFICATE_NOT_FOUND);
134 }
135
136 ec_pubkey = &server_certificate -> nx_secure_x509_public_key.ec_public_key;
137
138 /* Find out which named curve the remote certificate is using. */
139 status = _nx_secure_tls_find_curve_method((NX_SECURE_TLS_ECC *)tls_ecc_curves, (USHORT)(ec_pubkey -> nx_secure_ec_named_curve), &curve_method_cert, NX_NULL);
140
141 if(status != NX_SUCCESS)
142 {
143 return(status);
144 }
145
146 ecdh_method = ciphersuite -> nx_secure_tls_public_cipher;
147 if (ecdh_method -> nx_crypto_operation == NX_NULL)
148 {
149 return(NX_SECURE_TLS_MISSING_CRYPTO_ROUTINE);
150 }
151
152 if (ecdh_method -> nx_crypto_init != NX_NULL)
153 {
154 status = ecdh_method -> nx_crypto_init((NX_CRYPTO_METHOD*)ecdh_method,
155 NX_NULL,
156 0,
157 &handler,
158 public_cipher_metadata,
159 public_cipher_metadata_size);
160 if(status != NX_CRYPTO_SUCCESS)
161 {
162 return(status);
163 }
164 }
165
166 status = ecdh_method -> nx_crypto_operation(NX_CRYPTO_EC_CURVE_SET, handler,
167 (NX_CRYPTO_METHOD*)ecdh_method, NX_NULL, 0,
168 (UCHAR *)curve_method_cert, sizeof(NX_CRYPTO_METHOD *), NX_NULL,
169 NX_NULL, 0,
170 public_cipher_metadata,
171 public_cipher_metadata_size,
172 NX_NULL, NX_NULL);
173 if (status != NX_CRYPTO_SUCCESS)
174 {
175 return(status);
176 }
177
178 /* Store public key in the nx_secure_tls_new_key_material_data. */
179 extended_output.nx_crypto_extended_output_data = &tls_key_material -> nx_secure_tls_new_key_material_data[1];
180 extended_output.nx_crypto_extended_output_length_in_byte = sizeof(tls_key_material -> nx_secure_tls_new_key_material_data) - 1;
181 extended_output.nx_crypto_extended_output_actual_size = 0;
182 status = ecdh_method -> nx_crypto_operation(NX_CRYPTO_DH_SETUP, handler,
183 (NX_CRYPTO_METHOD*)ecdh_method, NX_NULL, 0,
184 NX_NULL, 0, NX_NULL,
185 (UCHAR *)&extended_output,
186 sizeof(extended_output),
187 public_cipher_metadata,
188 public_cipher_metadata_size,
189 NX_NULL, NX_NULL);
190 if (status != NX_CRYPTO_SUCCESS)
191 {
192 return(status);
193 }
194
195 tls_key_material -> nx_secure_tls_new_key_material_data[0] = (UCHAR)extended_output.nx_crypto_extended_output_actual_size;
196
197 extended_output.nx_crypto_extended_output_data = tls_key_material -> nx_secure_tls_pre_master_secret;
198 extended_output.nx_crypto_extended_output_length_in_byte = sizeof(tls_key_material -> nx_secure_tls_pre_master_secret);
199 extended_output.nx_crypto_extended_output_actual_size = 0;
200 status = ecdh_method -> nx_crypto_operation(NX_CRYPTO_DH_CALCULATE, handler,
201 (NX_CRYPTO_METHOD*)ecdh_method, NX_NULL, 0,
202 (UCHAR *)ec_pubkey -> nx_secure_ec_public_key,
203 ec_pubkey -> nx_secure_ec_public_key_length, NX_NULL,
204 (UCHAR *)&extended_output,
205 sizeof(extended_output),
206 public_cipher_metadata,
207 public_cipher_metadata_size,
208 NX_NULL, NX_NULL);
209 if (status != NX_CRYPTO_SUCCESS)
210 {
211 return(status);
212 }
213
214 tls_key_material -> nx_secure_tls_pre_master_secret_size = extended_output.nx_crypto_extended_output_actual_size;
215
216 if (ecdh_method -> nx_crypto_cleanup)
217 {
218 status = ecdh_method -> nx_crypto_cleanup(public_cipher_metadata);
219 if(status != NX_CRYPTO_SUCCESS)
220 {
221 return(status);
222 }
223 }
224
225 return(NX_SECURE_TLS_SUCCESS);
226 }
227 #endif /* NX_SECURE_ENABLE_ECC_CIPHERSUITE && !NX_SECURE_DISABLE_X509 */
228
229 #ifdef NX_SECURE_ENABLE_PSK_CIPHERSUITES
230 /* Check for PSK ciphersuites. */
231 if (ciphersuite -> nx_secure_tls_public_auth -> nx_crypto_algorithm == NX_CRYPTO_KEY_EXCHANGE_PSK)
232 {
233 /* Now, using the identity as a key, find the PSK in our PSK store. */
234 if (session_type == NX_SECURE_TLS_SESSION_TYPE_SERVER)
235 {
236 /* Server just uses its PSK. */
237 psk_data = tls_credentials -> nx_secure_tls_psk_store[0].nx_secure_tls_psk_data;
238 psk_length = tls_credentials -> nx_secure_tls_psk_store[0].nx_secure_tls_psk_data_size;
239 }
240 else
241 {
242 /* Client has to search for the PSK based on the identity hint. */
243 status = _nx_secure_tls_psk_find(tls_credentials, &psk_data, &psk_length, tls_credentials -> nx_secure_tls_remote_psk_id,
244 tls_credentials -> nx_secure_tls_remote_psk_id_size, NX_NULL);
245
246 if (status != NX_SUCCESS)
247 {
248 return(status);
249 }
250 }
251
252 /* From RFC 4279:
253 The premaster secret is formed as follows: if the PSK is N octets
254 long, concatenate a uint16 with the value N, N zero octets, a second
255 uint16 with the value N, and the PSK itself.
256 | 2 | <N> | 2 | <N> |
257 | N | 0 | N | PSK |
258 */
259 index = 0;
260
261 if ((2 + psk_length + 2 + psk_length) > sizeof(tls_key_material -> nx_secure_tls_pre_master_secret))
262 {
263
264 /* No more PSK space. */
265 return(NX_SECURE_TLS_NO_MORE_PSK_SPACE);
266 }
267
268 tls_key_material -> nx_secure_tls_pre_master_secret[0] = (UCHAR)(psk_length >> 8);
269 tls_key_material -> nx_secure_tls_pre_master_secret[1] = (UCHAR)psk_length;
270 index += 2;
271
272 NX_SECURE_MEMSET(&tls_key_material -> nx_secure_tls_pre_master_secret[index], 0, psk_length);
273 index += psk_length;
274
275 tls_key_material -> nx_secure_tls_pre_master_secret[index] = (UCHAR)(psk_length >> 8);
276 tls_key_material -> nx_secure_tls_pre_master_secret[index + 1] = (UCHAR)psk_length;
277 index += 2;
278
279 NX_SECURE_MEMCPY(&tls_key_material -> nx_secure_tls_pre_master_secret[index], psk_data, psk_length); /* Use case of memcpy is verified. */
280 index += psk_length;
281
282 /* Save the pre-master secret size for later use. */
283 tls_key_material -> nx_secure_tls_pre_master_secret_size = 2 + psk_length + 2 + psk_length;
284
285 /* We are using PSK for our credentials and now that we have generated keys we can consider the
286 remote host's credentials to have been received. */
287 *received_remote_credentials = NX_TRUE;
288
289 return(NX_SECURE_TLS_SUCCESS);
290 }
291 #endif
292
293 #ifdef NX_SECURE_ENABLE_ECJPAKE_CIPHERSUITE
294 /* When using ECJ-PAKE ciphersuite, pre-master secret is already generated. */
295 if (ciphersuite -> nx_secure_tls_public_auth -> nx_crypto_algorithm == NX_CRYPTO_KEY_EXCHANGE_ECJPAKE)
296 {
297 /* If using EC-JPAKE, credentials are passed differently - if we get here credentials should be OK. */
298 *received_remote_credentials = NX_TRUE;
299
300 return(NX_SECURE_TLS_SUCCESS);
301 }
302 #endif
303
304 /* Generate the Pre-Master Secret that is used to generate the key material
305 used in the session. For TLS 1.1, the secret consists of two bytes
306 representing the highest protocol version the client supports, followed
307 by 46 random bytes. */
308 buffer_ptr = (UINT *)tls_key_material -> nx_secure_tls_pre_master_secret;
309
310 /* Generate 48 bytes of random data, fill in the version afterwards. */
311 for (i = 0; i < 12; i++)
312 {
313 /* Fill with 12 ULONG randoms, then fix first two bytes to protocol version after. */
314 *(buffer_ptr + i) = (UINT)NX_RAND();
315 }
316
317 /* First two bytes are newest version supported by client . */
318 buffer_ptr[0] = ((ULONG)protocol_version << 16) | (buffer_ptr[0] & 0x0000FFFF);
319 NX_CHANGE_ULONG_ENDIAN(buffer_ptr[0]);
320
321 tls_key_material -> nx_secure_tls_pre_master_secret_size = 48;
322
323 return(status);
324 }
325
326