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
25 #include "nx_secure_tls.h"
26
27 #ifndef NX_SECURE_DISABLE_X509
28 static UCHAR _nx_secure_client_padded_pre_master[600];
29 #endif
30
31 /**************************************************************************/
32 /* */
33 /* FUNCTION RELEASE */
34 /* */
35 /* _nx_secure_generate_client_key_exchange PORTABLE C */
36 /* 6.2.1 */
37 /* AUTHOR */
38 /* */
39 /* Yanwu Cai, Microsoft Corporation */
40 /* */
41 /* DESCRIPTION */
42 /* */
43 /* This function encrypts the Pre-Master Secret (generated earlier) */
44 /* and populates the buffer with the complete ClientKeyExchange */
45 /* message (to be sent by the caller). It also will send ephemeral */
46 /* keys for ciphersuites that require them. */
47 /* */
48 /* INPUT */
49 /* */
50 /* ciphersuite Selected cipher suite */
51 /* tls_key_material TLS key material */
52 /* tls_credentials TLS credentials */
53 /* data_buffer Pointer to output buffer */
54 /* buffer_length Length of data buffer */
55 /* output_size Size of data written to buffer*/
56 /* public_cipher_metadata Metadata for public cipher */
57 /* public_cipher_metadata_size Size of public cipher metadata*/
58 /* public_auth_metadata Metadata for public auth */
59 /* public_auth_metadata_size Size of public auth metadata */
60 /* */
61 /* OUTPUT */
62 /* */
63 /* status Completion status */
64 /* */
65 /* CALLS */
66 /* */
67 /* _nx_secure_x509_remote_endpoint_certificate_get */
68 /* Get cert for remote host */
69 /* [nx_crypto_operation] Public-key ciphers */
70 /* [nx_crypto_init] Initialize Crypto Method */
71 /* */
72 /* CALLED BY */
73 /* */
74 /* _nx_secure_tls_send_client_key_exchange */
75 /* Send ClientKeyExchange */
76 /* */
77 /* RELEASE HISTORY */
78 /* */
79 /* DATE NAME DESCRIPTION */
80 /* */
81 /* 10-31-2022 Yanwu Cai Initial Version 6.2.0 */
82 /* 03-08-2023 Yanwu Cai Modified comment(s), */
83 /* fixed compiler errors when */
84 /* x509 is disabled, */
85 /* resulting in version 6.2.1 */
86 /* */
87 /**************************************************************************/
_nx_secure_generate_client_key_exchange(const NX_SECURE_TLS_CIPHERSUITE_INFO * ciphersuite,NX_SECURE_TLS_KEY_MATERIAL * tls_key_material,NX_SECURE_TLS_CREDENTIALS * tls_credentials,UCHAR * data_buffer,ULONG buffer_length,ULONG * output_size,VOID * public_cipher_metadata,ULONG public_cipher_metadata_size,VOID * public_auth_metadata,ULONG public_auth_metadata_size)88 UINT _nx_secure_generate_client_key_exchange(const NX_SECURE_TLS_CIPHERSUITE_INFO *ciphersuite,
89 NX_SECURE_TLS_KEY_MATERIAL *tls_key_material, NX_SECURE_TLS_CREDENTIALS *tls_credentials,
90 UCHAR *data_buffer, ULONG buffer_length, ULONG *output_size,
91 VOID *public_cipher_metadata, ULONG public_cipher_metadata_size,
92 VOID *public_auth_metadata, ULONG public_auth_metadata_size)
93 {
94 #if defined(NX_SECURE_ENABLE_ECJPAKE_CIPHERSUITE) || !defined(NX_SECURE_DISABLE_X509)
95 UINT status;
96 const NX_CRYPTO_METHOD *public_cipher_method;
97 VOID *handler = NX_NULL;
98 #endif
99 UINT data_size;
100 UCHAR *encrypted_data_ptr;
101 #ifndef NX_SECURE_DISABLE_X509
102 UCHAR rand_byte;
103 UINT i;
104 NX_SECURE_X509_CERT *remote_certificate;
105 #endif
106 #ifdef NX_SECURE_ENABLE_ECJPAKE_CIPHERSUITE
107 NX_CRYPTO_EXTENDED_OUTPUT extended_output;
108 #endif /* NX_SECURE_ENABLE_ECJPAKE_CIPHERSUITE */
109
110 /* Send a ClientKeyExchange message.
111 * Structure:
112 * | 2 | <Length> |
113 * | Length | Encrypted Pre-Master Secret |
114 *
115 * ECDHE ephemeral key structure:
116 * | 2 | <Length> |
117 * | Length | EC Diffie-Hellman Client Params |
118 */
119
120 #ifndef NX_SECURE_ENABLE_ECJPAKE_CIPHERSUITE
121 NX_PARAMETER_NOT_USED(public_auth_metadata);
122 NX_PARAMETER_NOT_USED(public_auth_metadata_size);
123 #endif
124 #ifdef NX_SECURE_DISABLE_X509
125 NX_PARAMETER_NOT_USED(tls_key_material);
126 NX_PARAMETER_NOT_USED(public_cipher_metadata);
127 NX_PARAMETER_NOT_USED(public_cipher_metadata_size);
128 #endif
129
130 #ifdef NX_SECURE_ENABLE_ECC_CIPHERSUITE
131 if (ciphersuite -> nx_secure_tls_public_cipher -> nx_crypto_algorithm == NX_CRYPTO_KEY_EXCHANGE_ECDH ||
132 ciphersuite -> nx_secure_tls_public_cipher -> nx_crypto_algorithm == NX_CRYPTO_KEY_EXCHANGE_ECDHE)
133 {
134 data_size = (UINT)(1 + tls_key_material -> nx_secure_tls_new_key_material_data[0]);
135
136 if ((data_size > sizeof(tls_key_material -> nx_secure_tls_new_key_material_data)) ||
137 (data_size > buffer_length))
138 {
139
140 /* Packet buffer too small. */
141 return(NX_SECURE_TLS_PACKET_BUFFER_TOO_SMALL);
142 }
143
144 NX_SECURE_MEMCPY(data_buffer, tls_key_material -> nx_secure_tls_new_key_material_data, data_size); /* Use case of memcpy is verified. */
145 }
146 else
147 #endif /* NX_SECURE_ENABLE_ECC_CIPHERSUITE */
148 {
149
150 /* Pointer to the output encrypted pre-master secret. */
151 encrypted_data_ptr = &data_buffer[2];
152
153 #ifdef NX_SECURE_ENABLE_PSK_CIPHERSUITES
154 /* Check for PSK ciphersuites. */
155 if (ciphersuite -> nx_secure_tls_public_auth -> nx_crypto_algorithm == NX_CRYPTO_KEY_EXCHANGE_PSK)
156 {
157 if ((tls_credentials -> nx_secure_tls_client_psk.nx_secure_tls_psk_id_hint_size >
158 sizeof(tls_credentials -> nx_secure_tls_client_psk.nx_secure_tls_psk_id_hint)) ||
159 (tls_credentials -> nx_secure_tls_client_psk.nx_secure_tls_psk_id_hint_size >
160 (buffer_length - 2)))
161 {
162
163 /* Packet buffer too small. */
164 return(NX_SECURE_TLS_PACKET_BUFFER_TOO_SMALL);
165 }
166
167 /* Send the PSK Identity string to the remote server along with its length. */
168 NX_SECURE_MEMCPY(encrypted_data_ptr, tls_credentials -> nx_secure_tls_client_psk.nx_secure_tls_psk_id,
169 tls_credentials -> nx_secure_tls_client_psk.nx_secure_tls_psk_id_size); /* Use case of memcpy is verified. */
170
171 /* Make sure our size is correct. */
172 data_size = tls_credentials -> nx_secure_tls_client_psk.nx_secure_tls_psk_id_size;
173
174 /* Put the length into our outgoing packet buffer. */
175 data_buffer[0] = (UCHAR)((data_size & 0xFF00) >> 8);
176 data_buffer[1] = (UCHAR)(data_size & 0x00FF);
177
178 data_size += 2;
179 }
180 else
181 #endif
182 #ifdef NX_SECURE_ENABLE_ECJPAKE_CIPHERSUITE
183 if (ciphersuite -> nx_secure_tls_public_auth -> nx_crypto_algorithm == NX_CRYPTO_KEY_EXCHANGE_ECJPAKE)
184 {
185 data_size = 0;
186 public_cipher_method = ciphersuite -> nx_secure_tls_public_auth;
187
188 extended_output.nx_crypto_extended_output_data = data_buffer;
189 extended_output.nx_crypto_extended_output_length_in_byte = buffer_length;
190 extended_output.nx_crypto_extended_output_actual_size = 0;
191 status = public_cipher_method -> nx_crypto_operation(NX_CRYPTO_ECJPAKE_CLIENT_KEY_EXCHANGE_GENERATE,
192 handler,
193 (NX_CRYPTO_METHOD*)public_cipher_method,
194 NX_NULL, 0,
195 NX_NULL, 0, NX_NULL,
196 (UCHAR *)&extended_output,
197 sizeof(extended_output),
198 public_auth_metadata,
199 public_auth_metadata_size,
200 NX_NULL, NX_NULL);
201 if (status)
202 {
203 return(status);
204 }
205
206 data_size += extended_output.nx_crypto_extended_output_actual_size;
207 }
208 else
209 #endif
210 {
211 #ifndef NX_SECURE_DISABLE_X509
212 /* Extract the data to be verified from the remote certificate processed earlier. */
213 status = _nx_secure_x509_remote_endpoint_certificate_get(&tls_credentials -> nx_secure_tls_certificate_store,
214 &remote_certificate);
215
216 if (status)
217 {
218
219 /* No certificate found, error! */
220 return(NX_SECURE_TLS_NO_CERT_SPACE_ALLOCATED);
221 }
222
223 /* Get the public cipher method from the ciphersuite. */
224 public_cipher_method = ciphersuite -> nx_secure_tls_public_cipher;
225
226 /* If using RSA, the length is equal to the key size. */
227 data_size = remote_certificate -> nx_secure_x509_public_key.rsa_public_key.nx_secure_rsa_public_modulus_length;
228
229 /* check data_size */
230 if ((data_size < NX_SECURE_TLS_RSA_PREMASTER_SIZE + 1) ||
231 (buffer_length < (2u + data_size)))
232 {
233
234 /* Invalid certificate modulus length. */
235 return(NX_SECURE_TLS_INVALID_CERTIFICATE);
236 }
237
238 /* PKCS#1 v1.5 padding. The scheme is to start with the block type (0x00, 0x02 for PKCS#1)
239 then pad with non-zero bytes (random is cryptographically more secure), followed with
240 a single 0 byte right before the payload, which comes at the end of the RSA block. */
241 NX_SECURE_MEMSET(_nx_secure_client_padded_pre_master, 0x0, sizeof(_nx_secure_client_padded_pre_master));
242 _nx_secure_client_padded_pre_master[1] = 0x2; /* Block type is 0x00, 0x02 */
243 for (i = 2; i < (data_size - NX_SECURE_TLS_RSA_PREMASTER_SIZE - 1); ++i)
244 {
245 /* PKCS#1 padding must be random, but CANNOT be 0. */
246 do
247 {
248 rand_byte = (UCHAR)NX_RAND();
249 } while (rand_byte == 0);
250 _nx_secure_client_padded_pre_master[i] = rand_byte;
251 }
252
253 /* Now put the pre-master data into the padded buffer - must be at the end. */
254 NX_SECURE_MEMCPY(&_nx_secure_client_padded_pre_master[data_size - NX_SECURE_TLS_RSA_PREMASTER_SIZE],
255 tls_key_material -> nx_secure_tls_pre_master_secret, NX_SECURE_TLS_RSA_PREMASTER_SIZE); /* Use case of memcpy is verified. */
256
257 if (public_cipher_method -> nx_crypto_init != NX_NULL)
258 {
259 /* Initialize the crypto method with public key. */
260 status = public_cipher_method -> nx_crypto_init((NX_CRYPTO_METHOD*)public_cipher_method,
261 (UCHAR *)remote_certificate -> nx_secure_x509_public_key.rsa_public_key.nx_secure_rsa_public_modulus,
262 (NX_CRYPTO_KEY_SIZE)(remote_certificate -> nx_secure_x509_public_key.rsa_public_key.nx_secure_rsa_public_modulus_length << 3),
263 &handler,
264 public_cipher_metadata,
265 public_cipher_metadata_size);
266
267 if(status != NX_CRYPTO_SUCCESS)
268 {
269 #ifdef NX_SECURE_KEY_CLEAR
270 NX_SECURE_MEMSET(_nx_secure_client_padded_pre_master, 0, sizeof(_nx_secure_client_padded_pre_master));
271 #endif /* NX_SECURE_KEY_CLEAR */
272
273 return(status);
274 }
275 }
276
277 if (public_cipher_method -> nx_crypto_operation != NX_NULL)
278 {
279 /* Now encrypt the pre-master secret using the public key provided by the remote host
280 and place the result in the outgoing packet buffer. */
281 status = public_cipher_method -> nx_crypto_operation(NX_CRYPTO_DECRYPT,
282 handler,
283 (NX_CRYPTO_METHOD*)public_cipher_method,
284 (UCHAR *)remote_certificate -> nx_secure_x509_public_key.rsa_public_key.nx_secure_rsa_public_exponent,
285 (NX_CRYPTO_KEY_SIZE)(remote_certificate -> nx_secure_x509_public_key.rsa_public_key.nx_secure_rsa_public_exponent_length << 3),
286 _nx_secure_client_padded_pre_master,
287 data_size,
288 NX_NULL,
289 encrypted_data_ptr,
290 data_size,
291 public_cipher_metadata,
292 public_cipher_metadata_size,
293 NX_NULL, NX_NULL);
294
295 if(status != NX_CRYPTO_SUCCESS)
296 {
297 #ifdef NX_SECURE_KEY_CLEAR
298 NX_SECURE_MEMSET(_nx_secure_client_padded_pre_master, 0, sizeof(_nx_secure_client_padded_pre_master));
299 #endif /* NX_SECURE_KEY_CLEAR */
300
301 return(status);
302 }
303 }
304
305 if (public_cipher_method -> nx_crypto_cleanup)
306 {
307 status = public_cipher_method -> nx_crypto_cleanup(public_cipher_metadata);
308
309 if(status != NX_CRYPTO_SUCCESS)
310 {
311 #ifdef NX_SECURE_KEY_CLEAR
312 NX_SECURE_MEMSET(_nx_secure_client_padded_pre_master, 0, sizeof(_nx_secure_client_padded_pre_master));
313 #endif /* NX_SECURE_KEY_CLEAR */
314
315 return(status);
316 }
317 }
318
319 /* Put the length into our outgoing packet buffer. */
320 data_buffer[0] = (UCHAR)((data_size & 0xFF00) >> 8);
321 data_buffer[1] = (UCHAR)(data_size & 0x00FF);
322
323 data_size += 2;
324 #else
325 return(NX_NOT_SUPPORTED);
326 #endif /* NX_SECURE_DISABLE_X509 */
327 }
328 }
329
330 /* Let the caller know how many bytes we wrote. +2 for the length we just added. */
331 *output_size = data_size;
332
333 #if defined(NX_SECURE_KEY_CLEAR) && !defined(NX_SECURE_DISABLE_X509)
334 NX_SECURE_MEMSET(_nx_secure_client_padded_pre_master, 0, sizeof(_nx_secure_client_padded_pre_master));
335 #endif /* NX_SECURE_KEY_CLEAR && !NX_SECURE_DISABLE_X509 */
336
337 return(NX_SECURE_TLS_SUCCESS);
338 }
339
340