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 Crypto Component */
16 /** */
17 /** Diffie-Hellman (DH) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_CRYPTO_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "nx_crypto_dh.h"
28 #ifndef NX_CRYPTO_SELF_TEST
29
30 /* The Diffie-Hellman group 2 modulus. */
31 /* Modulus, in byte stream, be */
32 /* ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff */
33 static const HN_UBASE _nx_dh_group_2_modulus[] =
34 {
35 HN_ULONG_TO_UBASE(0xFFFFFFFF), HN_ULONG_TO_UBASE(0xFFFFFFFF),
36 HN_ULONG_TO_UBASE(0xECE65381), HN_ULONG_TO_UBASE(0x49286651),
37 HN_ULONG_TO_UBASE(0x7C4B1FE6), HN_ULONG_TO_UBASE(0xAE9F2411),
38 HN_ULONG_TO_UBASE(0x5A899FA5), HN_ULONG_TO_UBASE(0xEE386BFB),
39 HN_ULONG_TO_UBASE(0xF406B7ED), HN_ULONG_TO_UBASE(0x0BFF5CB6),
40 HN_ULONG_TO_UBASE(0xA637ED6B), HN_ULONG_TO_UBASE(0xF44C42E9),
41 HN_ULONG_TO_UBASE(0x625E7EC6), HN_ULONG_TO_UBASE(0xE485B576),
42 HN_ULONG_TO_UBASE(0x6D51C245), HN_ULONG_TO_UBASE(0x4FE1356D),
43 HN_ULONG_TO_UBASE(0xF25F1437), HN_ULONG_TO_UBASE(0x302B0A6D),
44 HN_ULONG_TO_UBASE(0xCD3A431B), HN_ULONG_TO_UBASE(0xEF9519B3),
45 HN_ULONG_TO_UBASE(0x8E3404DD), HN_ULONG_TO_UBASE(0x514A0879),
46 HN_ULONG_TO_UBASE(0x3B139B22), HN_ULONG_TO_UBASE(0x020BBEA6),
47 HN_ULONG_TO_UBASE(0x8A67CC74), HN_ULONG_TO_UBASE(0x29024E08),
48 HN_ULONG_TO_UBASE(0x80DC1CD1), HN_ULONG_TO_UBASE(0xC4C6628B),
49 HN_ULONG_TO_UBASE(0x2168C234), HN_ULONG_TO_UBASE(0xC90FDAA2),
50 HN_ULONG_TO_UBASE(0xFFFFFFFF), HN_ULONG_TO_UBASE(0xFFFFFFFF)
51 };
52
53
54 /**************************************************************************/
55 /* */
56 /* FUNCTION RELEASE */
57 /* */
58 /* _nx_crypto_dh_setup PORTABLE C */
59 /* 6.1.7 */
60 /* AUTHOR */
61 /* */
62 /* Timothy Stapko, Microsoft Corporation */
63 /* */
64 /* DESCRIPTION */
65 /* */
66 /* This function sets up a Diffie-Hellman context by generating a */
67 /* local. */
68 /* */
69 /* Note: The scratch buffer must be able to hold a number of *bytes* */
70 /* at least equal to NX_CRYPTO_DIFFIE_HELLMAN_SCRATCH_SIZE. */
71 /* */
72 /* INPUT */
73 /* */
74 /* dh_ptr Diffie-Hellman context */
75 /* local_public_key Pointer to local public key */
76 /* local_public_key_len Local public key length */
77 /* dh_group_num Chosen DH group number */
78 /* scratch_buf_ptr Pointer to scratch buffer, */
79 /* which cannot be smaller */
80 /* than 6 times of the key */
81 /* size (in bytes). This */
82 /* scratch buffer can be */
83 /* reused after this function */
84 /* returns. */
85 /* */
86 /* OUTPUT */
87 /* */
88 /* status Completion status */
89 /* */
90 /* CALLS */
91 /* */
92 /* _nx_crypto_huge_number_adjust_size Adjust the size of a huge */
93 /* number to remove leading */
94 /* zeroes */
95 /* _nx_crypto_huge_number_mont_power_modulus */
96 /* Raise a huge number for */
97 /* montgomery reduction */
98 /* */
99 /* CALLED BY */
100 /* */
101 /* Application Code */
102 /* */
103 /* RELEASE HISTORY */
104 /* */
105 /* DATE NAME DESCRIPTION */
106 /* */
107 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
108 /* 09-30-2020 Timothy Stapko Modified comment(s), */
109 /* resulting in version 6.1 */
110 /* 06-02-2021 Bhupendra Naphade Modified comment(s), */
111 /* renamed FIPS symbol to */
112 /* self-test, */
113 /* resulting in version 6.1.7 */
114 /* */
115 /**************************************************************************/
_nx_crypto_dh_setup(NX_CRYPTO_DH * dh_ptr,UCHAR * local_public_key_ptr,UINT * local_public_key_len_ptr,ULONG dh_group_num,HN_UBASE * scratch_buf_ptr)116 NX_CRYPTO_KEEP UINT _nx_crypto_dh_setup(NX_CRYPTO_DH *dh_ptr,
117 UCHAR *local_public_key_ptr,
118 UINT *local_public_key_len_ptr,
119 ULONG dh_group_num,
120 HN_UBASE *scratch_buf_ptr)
121 {
122
123 UINT i;
124
125
126 /* Actual huge numbers used in calculations */
127 NX_CRYPTO_HUGE_NUMBER modulus, public_key, private_key, generator;
128 HN_UBASE generator_value;
129
130 NX_CRYPTO_STATE_CHECK
131
132 /* Assign the desired key size based on the chosen Diffie-Hellman group. */
133 switch (dh_group_num)
134 {
135 case NX_CRYPTO_DH_GROUP_2:
136 dh_ptr -> nx_crypto_dh_key_size = NX_CRYPTO_DIFFIE_HELLMAN_GROUP_2_KEY_SIZE;
137 dh_ptr -> nx_crypto_dh_modulus = (HN_UBASE *)_nx_dh_group_2_modulus;
138
139 NX_CRYPTO_HUGE_NUMBER_INITIALIZE_DIGIT(&generator, &generator_value,
140 NX_CRYPTO_DH_GROUP_2_GENERATOR)
141
142 /* Setup the modulus value. */
143 modulus.nx_crypto_huge_number_data = dh_ptr -> nx_crypto_dh_modulus;
144 modulus.nx_crypto_huge_number_size = dh_ptr -> nx_crypto_dh_key_size >> HN_SIZE_SHIFT;
145 modulus.nx_crypto_huge_buffer_size = dh_ptr -> nx_crypto_dh_key_size;
146 modulus.nx_crypto_huge_number_is_negative = NX_CRYPTO_FALSE;
147
148
149 break;
150
151 default:
152 /* No supported group specified - error! */
153 return(NX_CRYPTO_NOT_SUCCESSFUL);
154 }
155
156 /* Local pointer for pointer arithmetic. The power-modulus operation does not require the scratch area
157 to be "clean". Therefore no need to zero out the buffer.
158
159 Public key buffer (and scratch). */
160 NX_CRYPTO_HUGE_NUMBER_INITIALIZE(&public_key, scratch_buf_ptr, dh_ptr -> nx_crypto_dh_key_size << 1);
161
162 /* Private key buffer - note that no scratch is required for the private key. */
163 private_key.nx_crypto_huge_number_data = dh_ptr -> nx_crypto_dh_private_key_buffer;
164 private_key.nx_crypto_huge_number_size = dh_ptr -> nx_crypto_dh_key_size >> HN_SIZE_SHIFT;
165 private_key.nx_crypto_huge_buffer_size = sizeof(dh_ptr -> nx_crypto_dh_private_key_buffer);
166 private_key.nx_crypto_huge_number_is_negative = NX_CRYPTO_FALSE;
167
168
169 /* Generate the private key. */
170 for (i = 0; i < private_key.nx_crypto_huge_number_size; i++)
171 {
172 /* Grab a random value - this may be more than one byte and we want to use
173 all the bytes in the value, so we do not simply copy the random_value
174 into the buffers. */
175 dh_ptr -> nx_crypto_dh_private_key_buffer[i] = (HN_UBASE)((HN_UBASE)(NX_CRYPTO_RAND()) & HN_MASK);
176 }
177
178 /* Finally, generate the public key from the private key, modulus, and the generator.
179 The actual calculation is "public_key = (generator**private_key) % modulus"
180 where the "**" denotes exponentiation. */
181 _nx_crypto_huge_number_mont_power_modulus(&generator, &private_key,
182 &modulus, &public_key, scratch_buf_ptr);
183
184 /* Copy the public key into the return buffer. */
185 _nx_crypto_huge_number_extract(&public_key, local_public_key_ptr,
186 dh_ptr -> nx_crypto_dh_key_size, local_public_key_len_ptr);
187
188 return(NX_CRYPTO_SUCCESS);
189 }
190
191
192 /**************************************************************************/
193 /* */
194 /* FUNCTION RELEASE */
195 /* */
196 /* _nx_crypto_dh_compute_secret PORTABLE C */
197 /* 6.1 */
198 /* AUTHOR */
199 /* */
200 /* Timothy Stapko, Microsoft Corporation */
201 /* */
202 /* DESCRIPTION */
203 /* */
204 /* This function computes the Diffie-Hellman shared secret using an */
205 /* existing Diffie-Hellman context and a public key received from a */
206 /* remote entity, usually as part of an IPSEC or SSL key exchange. */
207 /* */
208 /* Note: The scratch buffer must be able to hold a number of *bytes* */
209 /* at least equal to NX_CRYPTO_DIFFIE_HELLMAN_SCRATCH_SIZE. */
210 /* */
211 /* INPUT */
212 /* */
213 /* dh_ptr Diffie-Hellman context */
214 /* share_secret_key_ptr Shared secret buffer pointer */
215 /* share_secret_key_len_ptr Length of shared secret */
216 /* remote_public_key Pointer to remote public key */
217 /* remote_public_key_len Remote public key length */
218 /* scratch_buf_ptr Pointer to scratch buffer, */
219 /* which cannot be smaller */
220 /* than 8 times of the key */
221 /* size (in bytes). This */
222 /* scratch buffer can be */
223 /* reused after this function */
224 /* returns. */
225 /* */
226 /* OUTPUT */
227 /* */
228 /* status Completion status */
229 /* */
230 /* CALLS */
231 /* */
232 /* _nx_crypto_huge_number_extract Extract huge number */
233 /* _nx_crypto_huge_number_setup Setup huge number */
234 /* _nx_crypto_huge_number_mont_power_modulus */
235 /* Raise a huge number for */
236 /* montgomery reduction */
237 /* */
238 /* CALLED BY */
239 /* */
240 /* Application Code */
241 /* */
242 /* RELEASE HISTORY */
243 /* */
244 /* DATE NAME DESCRIPTION */
245 /* */
246 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
247 /* 09-30-2020 Timothy Stapko Modified comment(s), */
248 /* resulting in version 6.1 */
249 /* */
250 /**************************************************************************/
_nx_crypto_dh_compute_secret(NX_CRYPTO_DH * dh_ptr,UCHAR * share_secret_key_ptr,ULONG * share_secret_key_len_ptr,UCHAR * remote_public_key,ULONG remote_public_key_len,HN_UBASE * scratch_buf_ptr)251 NX_CRYPTO_KEEP UINT _nx_crypto_dh_compute_secret(NX_CRYPTO_DH *dh_ptr,
252 UCHAR *share_secret_key_ptr,
253 ULONG *share_secret_key_len_ptr,
254 UCHAR *remote_public_key,
255 ULONG remote_public_key_len,
256 HN_UBASE *scratch_buf_ptr)
257 {
258
259 UINT key_size;
260
261 /* Actual huge numbers used in calculations */
262 NX_CRYPTO_HUGE_NUMBER modulus, public_key, private_key, shared_secret;
263
264 NX_CRYPTO_STATE_CHECK
265
266 /* Make sure the key size was assigned before we do anything else. Generally, this means
267 _nx_crypto_dh_setup was not called to set up the NX_DH structure prior to this call. */
268 if (0 == dh_ptr -> nx_crypto_dh_key_size)
269 {
270 return(NX_CRYPTO_NOT_SUCCESSFUL);
271 }
272
273 /* Make sure the remote public key is small enough to fit into the huge number buffer. */
274 if (remote_public_key_len > dh_ptr -> nx_crypto_dh_key_size)
275 {
276 return(NX_CRYPTO_NOT_SUCCESSFUL);
277 }
278
279 /* Figure out the sizes of our keys and buffers. We need 2X the key size for our buffer space. */
280 key_size = dh_ptr -> nx_crypto_dh_key_size;
281
282 NX_CRYPTO_HUGE_NUMBER_INITIALIZE(&public_key, scratch_buf_ptr, key_size);
283 NX_CRYPTO_HUGE_NUMBER_INITIALIZE(&shared_secret, scratch_buf_ptr, key_size << 1);
284
285 /* Copy the remote public key from the caller's buffer. */
286 _nx_crypto_huge_number_setup(&public_key, remote_public_key, remote_public_key_len);
287
288
289
290 /* Set up each of the buffers - point into the scratch buffer at increments of the DH buffer size. */
291 modulus.nx_crypto_huge_number_data = (HN_UBASE *)dh_ptr -> nx_crypto_dh_modulus;
292 modulus.nx_crypto_huge_number_size = key_size >> HN_SIZE_SHIFT;
293 modulus.nx_crypto_huge_buffer_size = key_size;
294 modulus.nx_crypto_huge_number_is_negative = NX_CRYPTO_FALSE;
295
296
297 /* Private key buffer - note that no scratch is required for the private key, but we set it in case
298 it is needed in the future. */
299 private_key.nx_crypto_huge_number_data = (HN_UBASE *)dh_ptr -> nx_crypto_dh_private_key_buffer;
300 private_key.nx_crypto_huge_number_size = key_size >> HN_SIZE_SHIFT;
301 private_key.nx_crypto_huge_buffer_size = key_size;
302 private_key.nx_crypto_huge_number_is_negative = NX_CRYPTO_FALSE;
303
304 /* Finally, generate shared secret from the remote public key, our generated private key, and the modulus, modulus.
305 The actual calculation is "shared_secret = (public_key**private_key) % modulus"
306 where the "**" denotes exponentiation. */
307 _nx_crypto_huge_number_mont_power_modulus(&public_key, &private_key, &modulus,
308 &shared_secret, scratch_buf_ptr);
309
310 /* Now we have a shared secret to return to the caller. Check to make sure the buffer is large enough to hold the public key. */
311 if (*share_secret_key_len_ptr < key_size)
312 {
313 return(NX_CRYPTO_NOT_SUCCESSFUL);
314 }
315
316 /* The public key size is simply the key size for this group. */
317
318 /* Copy the shared secret into the return buffer. */
319 _nx_crypto_huge_number_extract(&shared_secret, share_secret_key_ptr,
320 key_size, (UINT *)share_secret_key_len_ptr);
321
322 return(NX_CRYPTO_SUCCESS);
323 }
324
325 #endif
326