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