1 /**************************************************************************/
2 /*                                                                        */
3 /*       Copyright (c) Microsoft Corporation. All rights reserved.        */
4 /*                                                                        */
5 /*       This software is licensed under the Microsoft Software License   */
6 /*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
7 /*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
8 /*       and in the root directory of this software.                      */
9 /*                                                                        */
10 /**************************************************************************/
11 
12 
13 /**************************************************************************/
14 /**************************************************************************/
15 /**                                                                       */
16 /** NetX Crypto Component                                                 */
17 /**                                                                       */
18 /**  HMAC-based Extract-and-Expand Key Derivation Function (HKDF)         */
19 /**                                                                       */
20 /**************************************************************************/
21 /**************************************************************************/
22 
23 #include "nx_crypto_hkdf.h"
24 #include "nx_crypto_hmac.h"
25 
26 /**************************************************************************/
27 /*                                                                        */
28 /*  FUNCTION                                               RELEASE        */
29 /*                                                                        */
30 /*    _nx_crypto_method_hkdf_init                         PORTABLE C      */
31 /*                                                           6.3.0        */
32 /*  AUTHOR                                                                */
33 /*                                                                        */
34 /*    Timothy Stapko, Microsoft Corporation                               */
35 /*                                                                        */
36 /*  DESCRIPTION                                                           */
37 /*                                                                        */
38 /*    This function is the common crypto method initialization routine    */
39 /*    for the Microsoft implementation of the HKDF cryptographic          */
40 /*    algorithm.                                                          */
41 /*                                                                        */
42 /*  INPUT                                                                 */
43 /*                                                                        */
44 /*    method                                Pointer to crypto method      */
45 /*    key                                   Pointer to key                */
46 /*    key_size_in_bits                      Length of key size in bits    */
47 /*    handler                               Returned crypto handler       */
48 /*    crypto_metadata                       Metadata area                 */
49 /*    crypto_metadata_size                  Size of the metadata area     */
50 /*                                                                        */
51 /*  OUTPUT                                                                */
52 /*                                                                        */
53 /*    status                                Completion status             */
54 /*                                                                        */
55 /*  CALLS                                                                 */
56 /*                                                                        */
57 /*    None                                                                */
58 /*                                                                        */
59 /*  CALLED BY                                                             */
60 /*                                                                        */
61 /*    Application Code                                                    */
62 /*                                                                        */
63 /*  RELEASE HISTORY                                                       */
64 /*                                                                        */
65 /*    DATE              NAME                      DESCRIPTION             */
66 /*                                                                        */
67 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
68 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
69 /*                                            resulting in version 6.1    */
70 /*  10-31-2023     Yanwu Cai                Modified comment(s),          */
71 /*                                            resulting in version 6.3.0  */
72 /*                                                                        */
73 /**************************************************************************/
_nx_crypto_method_hkdf_init(struct NX_CRYPTO_METHOD_STRUCT * method,UCHAR * key,NX_CRYPTO_KEY_SIZE key_size_in_bits,VOID ** handle,VOID * crypto_metadata,ULONG crypto_metadata_size)74 NX_CRYPTO_KEEP UINT  _nx_crypto_method_hkdf_init(struct  NX_CRYPTO_METHOD_STRUCT *method,
75                                                         UCHAR *key, NX_CRYPTO_KEY_SIZE key_size_in_bits,
76                                                         VOID  **handle,
77                                                         VOID  *crypto_metadata,
78                                                         ULONG crypto_metadata_size)
79 {
80     NX_CRYPTO_HKDF       *hkdf;
81 
82     NX_CRYPTO_PARAMETER_NOT_USED(handle);
83 
84     NX_CRYPTO_STATE_CHECK
85 
86     /* We don't need a key in the HKDF init. */
87     if ((method == NX_CRYPTO_NULL) || (crypto_metadata == NX_CRYPTO_NULL))
88     {
89         return(NX_CRYPTO_POINTER_ERROR);
90     }
91 
92     /* Verify the metadata address is 4-byte aligned. */
93     if((((ULONG)crypto_metadata) & 0x3) != 0)
94     {
95         return(NX_CRYPTO_METADATA_UNALIGNED);
96     }
97 
98     if(crypto_metadata_size < sizeof(NX_CRYPTO_HKDF))
99     {
100         return(NX_CRYPTO_INVALID_BUFFER_SIZE);
101     }
102 
103     hkdf = (NX_CRYPTO_HKDF *)crypto_metadata;
104 
105     /* Initialize IKM with key data. */
106     hkdf->nx_crypto_hkdf_ikm = key;
107     hkdf->nx_crypto_hkdf_ikm_length = (key_size_in_bits << 3);
108 
109     /* Initialize HMAC and HASH methods. */
110     hkdf->nx_crypto_hmac_method = NX_CRYPTO_NULL;
111     hkdf->nx_crypto_hash_method = NX_CRYPTO_NULL;
112 
113     return(NX_CRYPTO_SUCCESS);
114 }
115 
116 
117 /**************************************************************************/
118 /*                                                                        */
119 /*  FUNCTION                                               RELEASE        */
120 /*                                                                        */
121 /*    _nx_crypto_method_hkdf_cleanup                      PORTABLE C      */
122 /*                                                           6.1          */
123 /*  AUTHOR                                                                */
124 /*                                                                        */
125 /*    Timothy Stapko, Microsoft Corporation                               */
126 /*                                                                        */
127 /*  DESCRIPTION                                                           */
128 /*                                                                        */
129 /*    This function cleans up the crypto metadata for the HKDF operation. */
130 /*                                                                        */
131 /*  INPUT                                                                 */
132 /*                                                                        */
133 /*    crypto_metadata                       Crypto metadata               */
134 /*                                                                        */
135 /*  OUTPUT                                                                */
136 /*                                                                        */
137 /*    status                                Completion status             */
138 /*                                                                        */
139 /*  CALLS                                                                 */
140 /*                                                                        */
141 /*    NX_CRYPTO_MEMSET                      Set the memory                */
142 /*                                                                        */
143 /*  CALLED BY                                                             */
144 /*                                                                        */
145 /*    Application Code                                                    */
146 /*                                                                        */
147 /*  RELEASE HISTORY                                                       */
148 /*                                                                        */
149 /*    DATE              NAME                      DESCRIPTION             */
150 /*                                                                        */
151 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
152 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
153 /*                                            resulting in version 6.1    */
154 /*                                                                        */
155 /**************************************************************************/
_nx_crypto_method_hkdf_cleanup(VOID * crypto_metadata)156 NX_CRYPTO_KEEP UINT  _nx_crypto_method_hkdf_cleanup(VOID *crypto_metadata)
157 {
158 #ifdef NX_SECURE_KEY_CLEAR
159 NX_CRYPTO_METHOD *hmac_method;
160 NX_CRYPTO_HKDF *hkdf;
161 UINT status;
162 #endif
163     NX_CRYPTO_STATE_CHECK
164 
165 #ifdef NX_SECURE_KEY_CLEAR
166     if (!crypto_metadata)
167         return (NX_CRYPTO_SUCCESS);
168 
169     /* Clear the HMAC state. */
170     hkdf = (NX_CRYPTO_HKDF *)crypto_metadata;
171     hmac_method = hkdf->nx_crypto_hmac_method;
172 
173     if(hmac_method)
174     {
175         status = hmac_method -> nx_crypto_cleanup(hmac_method);
176 
177         if (status != NX_CRYPTO_SUCCESS)
178         {
179             return(status);
180         }
181     }
182 
183     /* Clean up the HKDF metadata.  */
184     NX_CRYPTO_MEMSET(crypto_metadata, 0, sizeof(NX_CRYPTO_HKDF));
185 #else
186     NX_CRYPTO_PARAMETER_NOT_USED(crypto_metadata);
187 #endif/* NX_SECURE_KEY_CLEAR  */
188 
189     return(NX_CRYPTO_SUCCESS);
190 }
191 
192 
193 /**************************************************************************/
194 /*                                                                        */
195 /*  FUNCTION                                               RELEASE        */
196 /*                                                                        */
197 /*    _nx_crypto_method_hkdf_operation                    PORTABLE C      */
198 /*                                                           6.3.0        */
199 /*  AUTHOR                                                                */
200 /*                                                                        */
201 /*    Timothy Stapko, Microsoft Corporation                               */
202 /*                                                                        */
203 /*  DESCRIPTION                                                           */
204 /*                                                                        */
205 /*    This function provides the generic NetX Crypto API for the HKDF     */
206 /*    operation.                                                          */
207 /*                                                                        */
208 /*  INPUT                                                                 */
209 /*                                                                        */
210 /*    op                                    Operation Type                */
211 /*                                          Encrypt, Decrypt, Authenticate*/
212 /*    handler                               Pointer to crypto context     */
213 /*    key                                   Pointer to key                */
214 /*    key_size_in_bits                      Length of key size in bits    */
215 /*    input                                 Input Stream                  */
216 /*    input_length_in_byte                  Input Stream Length           */
217 /*    iv_ptr                                Initialized Vector            */
218 /*    output                                Output Stream                 */
219 /*    output_length_in_byte                 Output Stream Length          */
220 /*    crypto_metadata                       Metadata area                 */
221 /*    crypto_metadata_size                  Size of the metadata area     */
222 /*    packet_ptr                            Pointer to packet             */
223 /*    nx_crypto_hw_process_callback         Callback function pointer     */
224 /*                                                                        */
225 /*  OUTPUT                                                                */
226 /*                                                                        */
227 /*    status                                Completion status             */
228 /*                                                                        */
229 /*  CALLS                                                                 */
230 /*                                                                        */
231 /*    _nx_crypto_hkdf_extract               Calculate the HKDF key        */
232 /*    _nx_crypto_hkdf_expand                Generate HKDF key material    */
233 /*                                                                        */
234 /*  CALLED BY                                                             */
235 /*                                                                        */
236 /*    Application Code                                                    */
237 /*                                                                        */
238 /*  RELEASE HISTORY                                                       */
239 /*                                                                        */
240 /*    DATE              NAME                      DESCRIPTION             */
241 /*                                                                        */
242 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
243 /*  09-30-2020     Timothy Stapko           Modified comment(s), improved */
244 /*                                            buffer length verification, */
245 /*                                            verified memcpy use cases,  */
246 /*                                            resulting in version 6.1    */
247 /*  10-31-2023     Yanwu Cai                Modified comment(s),          */
248 /*                                            resulting in version 6.3.0  */
249 /*                                                                        */
250 /**************************************************************************/
_nx_crypto_method_hkdf_operation(UINT op,VOID * handle,struct NX_CRYPTO_METHOD_STRUCT * method,UCHAR * key,NX_CRYPTO_KEY_SIZE key_size_in_bits,UCHAR * input,ULONG input_length_in_byte,UCHAR * iv_ptr,UCHAR * output,ULONG output_length_in_byte,VOID * crypto_metadata,ULONG crypto_metadata_size,VOID * packet_ptr,VOID (* nx_crypto_hw_process_callback)(VOID * packet_ptr,UINT status))251 NX_CRYPTO_KEEP UINT  _nx_crypto_method_hkdf_operation(UINT op,      /* Encrypt, Decrypt, Authenticate */
252                                                      VOID *handle, /* Crypto handler */
253                                                      struct NX_CRYPTO_METHOD_STRUCT *method,
254                                                      UCHAR *key,
255                                                      NX_CRYPTO_KEY_SIZE key_size_in_bits,
256                                                      UCHAR *input,
257                                                      ULONG input_length_in_byte,
258                                                      UCHAR *iv_ptr,
259                                                      UCHAR *output,
260                                                      ULONG output_length_in_byte,
261                                                      VOID *crypto_metadata,
262                                                      ULONG crypto_metadata_size,
263                                                      VOID *packet_ptr,
264                                                      VOID (*nx_crypto_hw_process_callback)(VOID *packet_ptr, UINT status))
265 {
266 NX_CRYPTO_HKDF       *hkdf;
267 UINT                 status;
268 
269     NX_CRYPTO_PARAMETER_NOT_USED(handle);
270     NX_CRYPTO_PARAMETER_NOT_USED(iv_ptr);
271     NX_CRYPTO_PARAMETER_NOT_USED(packet_ptr);
272     NX_CRYPTO_PARAMETER_NOT_USED(nx_crypto_hw_process_callback);
273 
274     NX_CRYPTO_STATE_CHECK
275 
276     /* Verify the metadata address is 4-byte aligned. */
277     if((method == NX_CRYPTO_NULL) || (crypto_metadata == NX_CRYPTO_NULL) || ((((ULONG)crypto_metadata) & 0x3) != 0))
278     {
279         return(NX_CRYPTO_POINTER_ERROR);
280     }
281 
282     if(crypto_metadata_size < sizeof(NX_CRYPTO_HKDF))
283     {
284         return(NX_CRYPTO_INVALID_BUFFER_SIZE);
285     }
286 
287     /* Get our control block for HKDF. */
288     hkdf = (NX_CRYPTO_HKDF *)(crypto_metadata);
289 
290     status = NX_CRYPTO_SUCCESS;
291     switch (op)
292     {
293     case NX_CRYPTO_HKDF_SET_HMAC:
294         hkdf->nx_crypto_hmac_method = method;
295         if(hkdf->nx_crypto_hash_method != NX_CRYPTO_NULL)
296         {
297             /* Set the HMAC hash method. */
298             status = hkdf->nx_crypto_hmac_method->nx_crypto_operation(NX_CRYPTO_HMAC_SET_HASH, NX_CRYPTO_NULL,
299                                                                       hkdf->nx_crypto_hash_method, NX_CRYPTO_NULL, 0, NX_CRYPTO_NULL, 0,
300                                                                       NX_CRYPTO_NULL, NX_CRYPTO_NULL, 0, hkdf->nx_crypto_hmac_metadata,
301                                                                       sizeof(hkdf->nx_crypto_hmac_metadata), NX_CRYPTO_NULL, NX_CRYPTO_NULL);
302         }
303         break;
304     case NX_CRYPTO_HKDF_SET_HASH:
305         hkdf->nx_crypto_hash_method = method;
306         if(hkdf->nx_crypto_hmac_method != NX_CRYPTO_NULL)
307         {
308             /* Set the HMAC hash method. */
309             status = hkdf->nx_crypto_hmac_method->nx_crypto_operation(NX_CRYPTO_HMAC_SET_HASH, NX_CRYPTO_NULL,
310                                                                       method, NX_CRYPTO_NULL, 0, NX_CRYPTO_NULL, 0,
311                                                                       NX_CRYPTO_NULL, NX_CRYPTO_NULL, 0, hkdf->nx_crypto_hmac_metadata,
312                                                                       sizeof(hkdf->nx_crypto_hmac_metadata), NX_CRYPTO_NULL, NX_CRYPTO_NULL);
313         }
314         break;
315     case NX_CRYPTO_HKDF_SET_PRK:
316         if(key == NX_CRYPTO_NULL)
317         {
318             return(NX_CRYPTO_POINTER_ERROR);
319         }
320 
321         if ((key_size_in_bits >> 3) > sizeof(hkdf->nx_crypto_hkdf_prk))
322         {
323             return(NX_CRYPTO_SIZE_ERROR);
324         }
325 
326         /* Set the PRK and return. */
327         NX_CRYPTO_MEMCPY(hkdf->nx_crypto_hkdf_prk, key, (key_size_in_bits >> 3)); /* Use case of memcpy is verified. */
328         hkdf->nx_crypto_hkdf_prk_size = (key_size_in_bits >> 3);
329 
330         break;
331     case NX_CRYPTO_HKDF_EXTRACT:
332         if(key == NX_CRYPTO_NULL)
333         {
334             return(NX_CRYPTO_POINTER_ERROR);
335         }
336 
337         if(hkdf->nx_crypto_hash_method == NX_CRYPTO_NULL || hkdf->nx_crypto_hmac_method == NX_CRYPTO_NULL)
338         {
339             return(NX_CRYPTO_METHOD_INITIALIZATION_FAILURE);
340         }
341 
342         /* Key is our "salt". The IKM should have been passed in the init function.*/
343         hkdf->nx_crypto_hkdf_salt = key;
344         hkdf->nx_crypto_hkdf_salt_length = (key_size_in_bits >> 3);
345 
346         /* Initialize IKM with input data. */
347         hkdf->nx_crypto_hkdf_ikm = input;
348         hkdf->nx_crypto_hkdf_ikm_length = input_length_in_byte;
349 
350         /* Our output size is the output size of the hash. */
351         hkdf->nx_crypto_hkdf_prk_size = hkdf->nx_crypto_hmac_method->nx_crypto_block_size_in_bytes;
352 
353         status = _nx_crypto_hkdf_extract(hkdf);
354 
355         if(status == NX_CRYPTO_SUCCESS)
356         {
357             if (output_length_in_byte < hkdf->nx_crypto_hkdf_prk_size)
358             {
359                 return(NX_CRYPTO_SIZE_ERROR);
360             }
361 
362             /* Copy the PRK into output. */
363             NX_CRYPTO_MEMCPY(output, hkdf->nx_crypto_hkdf_prk, hkdf->nx_crypto_hkdf_prk_size); /* Use case of memcpy is verified. */
364         }
365 
366         break;
367 
368     case NX_CRYPTO_HKDF_EXPAND:
369         if(key == NX_CRYPTO_NULL)
370         {
371             return(NX_CRYPTO_POINTER_ERROR);
372         }
373 
374         if(hkdf->nx_crypto_hash_method == NX_CRYPTO_NULL || hkdf->nx_crypto_hmac_method == NX_CRYPTO_NULL)
375         {
376             return(NX_CRYPTO_METHOD_INITIALIZATION_FAILURE);
377         }
378 
379         /* Key is our "info". The PRK should have been initialized by the call to NX_CRYPTO_HKDF_EXTRACT.*/
380         hkdf->nx_crypto_hkdf_info = key;
381         hkdf->nx_crypto_hkdf_info_size = (key_size_in_bits >> 3);
382 
383         status = _nx_crypto_hkdf_expand(hkdf, output, output_length_in_byte);
384         break;
385     default:
386         break;
387     }
388 
389     return(status);
390 }
391 
392 
393 /**************************************************************************/
394 /*                                                                        */
395 /*  FUNCTION                                               RELEASE        */
396 /*                                                                        */
397 /*    _nx_crypto_method_hkdf_extract                      PORTABLE C      */
398 /*                                                           6.1          */
399 /*  AUTHOR                                                                */
400 /*                                                                        */
401 /*    Timothy Stapko, Microsoft Corporation                               */
402 /*                                                                        */
403 /*  DESCRIPTION                                                           */
404 /*                                                                        */
405 /*    This function performs the HKDF-extract operation detailed in RFC   */
406 /*    5869. The output key is placed in the HKDF structure passed in.     */
407 /*                                                                        */
408 /*  INPUT                                                                 */
409 /*                                                                        */
410 /*    hkdf                                  HKDF structure                */
411 /*                                                                        */
412 /*  OUTPUT                                                                */
413 /*                                                                        */
414 /*    status                                Completion status             */
415 /*                                                                        */
416 /*  CALLS                                                                 */
417 /*                                                                        */
418 /*    [hash method]                         Perform selected HMAC hash    */
419 /*                                                                        */
420 /*  CALLED BY                                                             */
421 /*                                                                        */
422 /*    Application Code                                                    */
423 /*                                                                        */
424 /*  RELEASE HISTORY                                                       */
425 /*                                                                        */
426 /*    DATE              NAME                      DESCRIPTION             */
427 /*                                                                        */
428 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
429 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
430 /*                                            resulting in version 6.1    */
431 /*                                                                        */
432 /**************************************************************************/
_nx_crypto_hkdf_extract(NX_CRYPTO_HKDF * hkdf)433 UINT _nx_crypto_hkdf_extract(NX_CRYPTO_HKDF *hkdf)
434 {
435 UINT    status;
436 UINT    hash_size; /* the length of hmac output */
437 UCHAR   *salt;
438 UINT    salt_len;
439 UCHAR   *ikm;
440 UINT    ikm_len;
441 UCHAR   *metadata;
442 UINT    metadata_size;
443 UCHAR   *hmac_output;
444 VOID   *handler = NX_CRYPTO_NULL;
445 NX_CRYPTO_METHOD *hmac_method = hkdf -> nx_crypto_hmac_method;
446 
447     NX_CRYPTO_STATE_CHECK
448 
449     /* From RFC 5869:
450      * HKDF-Extract(salt, IKM) -> PRK
451      *
452      * Options:
453      *    Hash     a hash function; HashLen denotes the length of the
454      *             hash function output in octets
455      *
456      * Inputs:
457      *    salt     optional salt value (a non-secret random value);
458      *             if not provided, it is set to a string of HashLen zeros.
459      *    IKM      input keying material
460      *
461      * Output:
462      *    PRK      a pseudorandom key (of HashLen octets)
463      *
464      * The output PRK is calculated as follows:
465      *
466      * PRK = HMAC-Hash(salt, IKM)
467      *
468      */
469 
470 
471     /* Validate pointers. */
472     if (hmac_method == NX_CRYPTO_NULL
473         || hmac_method -> nx_crypto_operation == NX_CRYPTO_NULL
474         || hmac_method -> nx_crypto_cleanup == NX_CRYPTO_NULL
475         || hkdf->nx_crypto_hash_method == NX_CRYPTO_NULL)
476     {
477         return(NX_CRYPTO_INVALID_PARAMETER);
478     }
479 
480     /* Initialize temporary variables. */
481     salt = hkdf->nx_crypto_hkdf_salt;
482     salt_len = hkdf->nx_crypto_hkdf_salt_length;
483     ikm = hkdf->nx_crypto_hkdf_ikm;
484     ikm_len = hkdf->nx_crypto_hkdf_ikm_length;
485     hash_size = hkdf -> nx_crypto_hash_method -> nx_crypto_ICV_size_in_bits >> 3;
486 
487     metadata = hkdf->nx_crypto_hmac_metadata;
488     metadata_size = sizeof(hkdf->nx_crypto_hmac_metadata);
489     hmac_output = hkdf->nx_crypto_hkdf_prk;
490 
491     /* Make sure we can store our output key. */
492     if (hash_size > sizeof(hkdf->nx_crypto_hkdf_prk))
493     {
494         return(NX_CRYPTO_INVALID_PARAMETER);
495     }
496 
497     /* Assign the output size to our HKDF structure. */
498     hkdf->nx_crypto_hkdf_prk_size = hash_size;
499 
500     /* Initialize hash method (check key sizes, etc.). */
501     if (hmac_method -> nx_crypto_init)
502     {
503         status = hmac_method -> nx_crypto_init(hmac_method,
504                                       salt,
505                                       (NX_CRYPTO_KEY_SIZE)(salt_len << 3),
506                                       &handler,
507                                       metadata,
508                                       metadata_size);
509 
510         if(status != NX_CRYPTO_SUCCESS)
511         {
512             return(status);
513         }
514     }
515 
516     /* Generate the output PRK. */
517     status = hmac_method -> nx_crypto_operation(NX_CRYPTO_AUTHENTICATE,
518                                                 handler,
519                                                 hmac_method,
520                                                 salt,
521                                                 (NX_CRYPTO_KEY_SIZE)(salt_len << 3),
522                                                 ikm,
523                                                 ikm_len,
524                                                 NX_CRYPTO_NULL,
525                                                 hmac_output,
526                                                 hash_size,
527                                                 metadata,
528                                                 metadata_size,
529                                                 NX_CRYPTO_NULL,
530                                                 NX_CRYPTO_NULL);
531 
532 
533     return(status);
534 }
535 
536 /**************************************************************************/
537 /*                                                                        */
538 /*  FUNCTION                                               RELEASE        */
539 /*                                                                        */
540 /*    _nx_crypto_method_hkdf_expand                       PORTABLE C      */
541 /*                                                           6.1          */
542 /*  AUTHOR                                                                */
543 /*                                                                        */
544 /*    Timothy Stapko, Microsoft Corporation                               */
545 /*                                                                        */
546 /*  DESCRIPTION                                                           */
547 /*                                                                        */
548 /*    This function performs the HKDF-expand operation detailed in RFC    */
549 /*    5869. The hdkf parameter contains the input key (PRK) and other     */
550 /*    parameters needed to generate the desired output data.              */
551 /*                                                                        */
552 /*  INPUT                                                                 */
553 /*                                                                        */
554 /*    hkdf                                  HKDF structure                */
555 /*                                                                        */
556 /*  OUTPUT                                                                */
557 /*                                                                        */
558 /*    status                                Completion status             */
559 /*                                                                        */
560 /*  CALLS                                                                 */
561 /*                                                                        */
562 /*    [hash method]                         Perform selected HMAC hash    */
563 /*                                                                        */
564 /*  CALLED BY                                                             */
565 /*                                                                        */
566 /*    Application Code                                                    */
567 /*                                                                        */
568 /*  RELEASE HISTORY                                                       */
569 /*                                                                        */
570 /*    DATE              NAME                      DESCRIPTION             */
571 /*                                                                        */
572 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
573 /*  09-30-2020     Timothy Stapko           Modified comment(s), improved */
574 /*                                            buffer length verification, */
575 /*                                            verified memcpy use cases,  */
576 /*                                            resulting in version 6.1    */
577 /*                                                                        */
578 /**************************************************************************/
_nx_crypto_hkdf_expand(NX_CRYPTO_HKDF * hkdf,UCHAR * output,UINT desired_length)579 UINT _nx_crypto_hkdf_expand(NX_CRYPTO_HKDF *hkdf, UCHAR *output, UINT desired_length)
580 {
581 UINT    offset;
582 UINT    N_count;
583 UINT    hash_size; /* the length of hmac output */
584 UCHAR   *prk;
585 UINT    prk_len;
586 UCHAR   *info;
587 UINT    info_len;
588 UCHAR   *metadata;
589 UINT    metadata_size;
590 VOID   *handler = NX_CRYPTO_NULL;
591 UINT    output_len;
592 /* T(i) */
593 UCHAR   *temp_T;
594 UINT    temp_T_size, T_len, T_bytes_to_hash;
595 UINT    i;
596 UINT    status;
597 
598 NX_CRYPTO_METHOD *hmac_method = hkdf -> nx_crypto_hmac_method;
599 
600     NX_CRYPTO_STATE_CHECK
601 
602     /* From RFC 5869:
603      * HKDF-Expand(PRK, info, L) -> OKM
604      *
605      *  Options:
606      *     Hash     a hash function; HashLen denotes the length of the
607      *              hash function output in octets
608      *  Inputs:
609      *     PRK      a pseudorandom key of at least HashLen octets
610      *              (usually, the output from the extract step)
611      *     info     optional context and application specific information
612      *              (can be a zero-length string)
613      *     L        length of output keying material in octets
614      *              (<= 255*HashLen)
615      *
616      *  Output:
617      *     OKM      output keying material (of L octets)
618      *
619      *  The output OKM is calculated as follows:
620      *
621      *  N = ceil(L/HashLen)
622      *  T = T(1) | T(2) | T(3) | ... | T(N)
623      *  OKM = first L octets of T
624      *
625      *  where:
626      *  T(0) = empty string (zero length)
627      *  T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
628      *  T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
629      *  T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
630      *  ...
631      *
632      *  (where the constant concatenated to the end of each T(n) is a
633      *  single octet.)
634      *
635      */
636 
637 
638     /* Validate pointers. */
639     if (hmac_method == NX_CRYPTO_NULL
640         || hmac_method -> nx_crypto_operation == NX_CRYPTO_NULL
641         || hmac_method -> nx_crypto_cleanup == NX_CRYPTO_NULL
642         || hkdf->nx_crypto_hash_method == NX_CRYPTO_NULL
643         || output == NX_CRYPTO_NULL)
644     {
645         return(NX_CRYPTO_INVALID_PARAMETER);
646     }
647 
648     /* Initialize temporary variables. */
649     prk = hkdf->nx_crypto_hkdf_prk;
650     prk_len = hkdf->nx_crypto_hkdf_prk_size;
651     info = hkdf->nx_crypto_hkdf_info;
652     info_len = hkdf->nx_crypto_hkdf_info_size;
653     temp_T = hkdf->nx_crypto_hkdf_temp_T;
654     temp_T_size = sizeof(hkdf->nx_crypto_hkdf_temp_T);
655     hash_size = hkdf-> nx_crypto_hash_method -> nx_crypto_ICV_size_in_bits >> 3;
656     metadata = hkdf->nx_crypto_hmac_metadata;
657     metadata_size = sizeof(hkdf->nx_crypto_hmac_metadata);
658 
659     /* Assign the output size to our HKDF structure. */
660     hkdf->nx_crypto_hkdf_prk_size = hash_size;
661 
662     /* Assign T(0), the empty string. */
663     NX_CRYPTO_MEMSET(temp_T, 0, temp_T_size);
664     T_len = 0;
665 
666     /* Get our L count for our loop. */
667     N_count = 1 + ((desired_length) / hash_size);
668 
669     /* Loop through T(i) to calculate output material (OKM).
670      * NOTE: We start at 1 so the counter is correct. Add one
671      * to N_count to get the full amount of data. */
672     for (i = 1; i < N_count + 1; ++i)
673     {
674         if ((T_len + info_len + 1) > temp_T_size)
675         {
676             return(NX_CRYPTO_SIZE_ERROR);
677         }
678 
679         /* Concatenate T(i-1) (in temp_T after the hash above), info, and counter octet to feed into digest. */
680         NX_CRYPTO_MEMCPY(&temp_T[T_len], info, info_len); /* Use case of memcpy is verified. */
681 
682         /* Concatenate counter octet. */
683         temp_T[T_len + info_len] = (UCHAR)(i & 0xFF);
684 
685         /* Initialize hash method. */
686         if (hmac_method -> nx_crypto_init)
687         {
688             status = hmac_method -> nx_crypto_init(hmac_method,
689                                                    prk,
690                                                    (NX_CRYPTO_KEY_SIZE)(prk_len << 3),
691                                                    &handler,
692                                                    metadata,
693                                                    metadata_size);
694 
695             if (status != NX_CRYPTO_SUCCESS)
696             {
697                 return(status);
698             }
699         }
700 
701         /* The number of bytes we want to hash is a combination of T_len (0 or <hash size>)
702            the length of "info", and add 1 for the counter octet. */
703         T_bytes_to_hash = T_len + info_len + 1;
704 
705         /* Calculate T(i) = HMAC(PRK, T(i-1) | info | i) */
706         status = hmac_method -> nx_crypto_operation(NX_CRYPTO_AUTHENTICATE,
707                                                     handler,
708                                                     hmac_method,
709                                                     prk,
710                                                     (NX_CRYPTO_KEY_SIZE)(prk_len << 3),
711                                                     temp_T,
712                                                     T_bytes_to_hash,
713                                                     NX_CRYPTO_NULL,
714                                                     temp_T,
715                                                     temp_T_size,
716                                                     metadata,
717                                                     metadata_size,
718                                                     NX_CRYPTO_NULL,
719                                                     NX_CRYPTO_NULL);
720 
721         if (status != NX_CRYPTO_SUCCESS)
722         {
723             return(status);
724         }
725 
726         /* Updated the length of T(i) */
727         T_len = hash_size;
728 
729         /* Get our output offset. */
730         offset = (i - 1) * hash_size;
731 
732         /* Output block is the size of the digest unless the remaining
733            desired length is smaller than the digest length. */
734         if ((desired_length - offset) < hash_size)
735         {
736             output_len = (desired_length - offset);
737         }
738         else
739         {
740             output_len = hash_size;
741         }
742 
743         /* Make sure we only copy the desired data length into the output. */
744         if (hash_size > desired_length)
745         {
746             output_len  = desired_length;
747         }
748 
749         /* Copy T(i) into output. */
750         NX_CRYPTO_MEMCPY(&output[offset], temp_T, output_len); /* Use case of memcpy is verified. */
751 
752     }
753 
754     return(NX_CRYPTO_SUCCESS);
755 
756 
757 }
758 
759 
760 
761 
762