hmac_drbg.c
Go to the documentation of this file.
1 /**
2  * @file hmac_drbg.c
3  * @brief HMAC_DRBG pseudorandom number generator
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneCRYPTO Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/crypto.h"
36 #include "rng/hmac_drbg.h"
37 #include "debug.h"
38 
39 //Check crypto library configuration
40 #if (HMAC_DRBG_SUPPORT == ENABLED)
41 
42 //Common interface for PRNG algorithms
44 {
45  "HMAC_DRBG",
46  sizeof(HmacDrbgContext),
47  (PrngAlgoInit) NULL,
52 };
53 
54 
55 /**
56  * @brief Initialize PRNG context
57  * @param[in] context Pointer to the HMAC_DRBG context to initialize
58  * @param[in] hashAlgo Approved hash function
59  * @return Error code
60  **/
61 
62 error_t hmacDrbgInit(HmacDrbgContext *context, const HashAlgo *hashAlgo)
63 {
64  //Check parameters
65  if(context == NULL || hashAlgo == NULL)
67 
68  //Clear the internal state
69  osMemset(context, 0, sizeof(HmacDrbgContext));
70 
71  //Create a mutex to prevent simultaneous access to the PRNG state
72  if(!osCreateMutex(&context->mutex))
73  {
74  //Failed to create mutex
76  }
77 
78  //HMAC_DRBG uses multiple occurrences of an approved keyed hash function,
79  //which is based on an approved hash function
80  context->hashAlgo = hashAlgo;
81 
82  //Determine the security strength (refer to SP 800-57, section 5.6.1.2)
83  if(hashAlgo->digestSize <= 20)
84  {
85  context->securityStrength = 16;
86  }
87  else if(hashAlgo->digestSize <= 28)
88  {
89  context->securityStrength = 24;
90  }
91  else
92  {
93  context->securityStrength = 32;
94  }
95 
96  //Successful initialization
97  return NO_ERROR;
98 }
99 
100 
101 /**
102  * @brief Seed the PRNG state
103  * @param[in] context Pointer to the HMAC_DRBG context
104  * @param[in] seed String of bits obtained from the randomness source
105  * @param[in] length Length of the string, in bytes
106  * @return Error code
107  **/
108 
109 error_t hmacDrbgSeed(HmacDrbgContext *context, const uint8_t *seed,
110  size_t length)
111 {
112  //Seeding process
113  return hmacDrbgSeedEx(context, seed, length, NULL, 0, NULL, 0);
114 }
115 
116 
117 /**
118  * @brief Seed the PRNG state (with nonce and personalization string)
119  * @param[in] context Pointer to the HMAC_DRBG context
120  * @param[in] entropyInput String of bits obtained from the randomness source
121  * @param[in] entropyInputLen Length of the string, in bytes
122  * @param[in] nonce Nonce
123  * @param[in] nonceLen Length of the nonce, in bytes
124  * @param[in] personalizationString Personalization string received from the
125  * consuming application
126  * @param[in] personalizationStringLen Length of the personalization string,
127  * in bytes
128  * @return Error code
129  **/
130 
131 error_t hmacDrbgSeedEx(HmacDrbgContext *context, const uint8_t *entropyInput,
132  size_t entropyInputLen, const uint8_t *nonce, size_t nonceLen,
133  const uint8_t *personalizationString, size_t personalizationStringLen)
134 {
135  size_t outLen;
136  DataChunk seedMaterial[3];
137 
138  //Check parameters
139  if(context == NULL || entropyInput == NULL)
141 
142  //The nonce parameter is optional
143  if(nonce == NULL && nonceLen != 0)
145 
146  //The personalization_string parameter is optional
147  if(personalizationString == NULL && personalizationStringLen != 0)
149 
150  //HMAC_DRBG uses multiple occurrences of an approved keyed hash function,
151  //which is based on an approved hash function
152  if(context->hashAlgo == NULL)
153  return ERROR_PRNG_NOT_READY;
154 
155  //The entropy input shall have entropy that is equal to or greater than the
156  //security strength of the instantiation
157  if(entropyInputLen < context->securityStrength)
159 
160  //If an implementation utilizes a nonce in the construction of a seed during
161  //instantiation, the length of the nonce shall be at least half the maximum
162  //security strength supported. Per allowances in SP 800-90A, the length of a
163  //nonce may be less than 1/2 the maximum security strength supported as long
164  //as the entropy input length + the nonce length >= 3/2 security strength
165  if((entropyInputLen + nonceLen) < (3 * context->securityStrength / 2))
167 
168  //Acquire exclusive access to the PRNG state
169  osAcquireMutex(&context->mutex);
170 
171  //Determine the output block length
172  outLen = context->hashAlgo->digestSize;
173 
174  //Let seed_material = entropy_input || nonce || personalization_string
175  seedMaterial[0].buffer = entropyInput;
176  seedMaterial[0].length = entropyInputLen;
177  seedMaterial[1].buffer = nonce;
178  seedMaterial[1].length = nonceLen;
179  seedMaterial[2].buffer = personalizationString;
180  seedMaterial[2].length = personalizationStringLen;
181 
182  //Set Key = 0x00 00...00
183  osMemset(context->k, 0x00, outLen);
184  //V = 0x01 01...01
185  osMemset(context->v, 0x01, outLen);
186 
187  //Compute (Key, V) = HMAC_DRBG_Update(seed_material, Key, V)
188  hmacDrbgUpdate(context, seedMaterial, arraysize(seedMaterial));
189 
190  //Reset reseed_counter
191  context->reseedCounter = 1;
192 
193  //Release exclusive access to the PRNG state
194  osReleaseMutex(&context->mutex);
195 
196  //Successful processing
197  return NO_ERROR;
198 }
199 
200 
201 /**
202  * @brief Reseed the PRNG state
203  * @param[in] context Pointer to the HMAC_DRBG context
204  * @param[in] seed String of bits obtained from the randomness source
205  * @param[in] length Length of the string, in bytes
206  * @return Error code
207  **/
208 
209 error_t hmacDrbgReseed(HmacDrbgContext *context, const uint8_t *seed,
210  size_t length)
211 {
212  //Reseeding process
213  return hmacDrbgReseedEx(context, seed, length, NULL, 0);
214 }
215 
216 
217 /**
218  * @brief Reseed the PRNG state (with additional input)
219  * @param[in] context Pointer to the HMAC_DRBG context
220  * @param[in] entropyInput String of bits obtained from the randomness source
221  * @param[in] entropyInputLen Length of the string, in bytes
222  * @param[in] additionalInput Additional input string received from the
223  * consuming application
224  * @param[in] additionalInputLen Length of the additional input string, in bytes
225  * @return Error code
226  **/
227 
228 error_t hmacDrbgReseedEx(HmacDrbgContext *context, const uint8_t *entropyInput,
229  size_t entropyInputLen, const uint8_t *additionalInput,
230  size_t additionalInputLen)
231 {
232  DataChunk seedMaterial[2];
233 
234  //Check parameters
235  if(context == NULL || entropyInput == NULL)
237 
238  //The additional_input parameter is optional
239  if(additionalInput == NULL && additionalInputLen != 0)
241 
242  //HMAC_DRBG uses multiple occurrences of an approved keyed hash function,
243  //which is based on an approved hash function
244  if(context->hashAlgo == NULL)
245  return ERROR_PRNG_NOT_READY;
246 
247  //Check whether the DRBG has been properly instantiated
248  if(context->reseedCounter == 0)
249  return ERROR_PRNG_NOT_READY;
250 
251  //The entropy input shall have entropy that is equal to or greater than the
252  //security strength of the instantiation
253  if(entropyInputLen < context->securityStrength)
255 
256  //Acquire exclusive access to the PRNG state
257  osAcquireMutex(&context->mutex);
258 
259  //Let seed_material = entropy_input || additional_input
260  seedMaterial[0].buffer = entropyInput;
261  seedMaterial[0].length = entropyInputLen;
262  seedMaterial[1].buffer = additionalInput;
263  seedMaterial[1].length = additionalInputLen;
264 
265  //Compute (Key, V) = HMAC_DRBG_Update(seed_material, Key, V)
266  hmacDrbgUpdate(context, seedMaterial, arraysize(seedMaterial));
267 
268  //Reset reseed_counter
269  context->reseedCounter = 1;
270 
271  //Release exclusive access to the PRNG state
272  osReleaseMutex(&context->mutex);
273 
274  //Successful processing
275  return NO_ERROR;
276 }
277 
278 
279 /**
280  * @brief Generate pseudorandom data
281  * @param[in] context Pointer to the HMAC_DRBG context
282  * @param[out] output Buffer where to store the pseudorandom bytes
283  * @param[in] length Requested number of bytes
284  * @return Error code
285  **/
286 
287 error_t hmacDrbgGenerate(HmacDrbgContext *context, uint8_t *output,
288  size_t length)
289 {
290  //Generation process
291  return hmacDrbgGenerateEx(context, NULL, 0, output, length);
292 }
293 
294 
295 /**
296  * @brief Generate pseudorandom data (with additional input)
297  * @param[in] context Pointer to the HMAC_DRBG context
298  * @param[in] additionalInput Additional input string received from the
299  * consuming application
300  * @param[in] additionalInputLen Length of the additional input string, in bytes
301  * @param[out] output Buffer where to store the pseudorandom bytes
302  * @param[in] outputLen Requested number of bytes
303  * @return Error code
304  **/
305 
307  const uint8_t *additionalInput, size_t additionalInputLen, uint8_t *output,
308  size_t outputLen)
309 {
310  size_t n;
311  size_t outLen;
312  DataChunk providedData[1];
313  HmacContext *hmacContext;
314 
315  //Check parameters
316  if(context == NULL)
318 
319  //The additional_input parameter is optional
320  if(additionalInput == NULL && additionalInputLen != 0)
322 
323  //HMAC_DRBG uses multiple occurrences of an approved keyed hash function,
324  //which is based on an approved hash function
325  if(context->hashAlgo == NULL)
326  return ERROR_PRNG_NOT_READY;
327 
328  //A DRBG shall be instantiated prior to the generation of pseudorandom bits
329  if(context->reseedCounter == 0)
330  return ERROR_PRNG_NOT_READY;
331 
332  //If reseed_counter > reseed_interval, then return an indication that a
333  //reseed is required
335  return ERROR_RESEED_REQUIRED;
336 
337  //Acquire exclusive access to the PRNG state
338  osAcquireMutex(&context->mutex);
339 
340  //Point to the HMAC context
341  hmacContext = &context->hmacContext;
342  //Determine the output block length
343  outLen = context->hashAlgo->digestSize;
344 
345  //The additional input string is received from the consuming application
346  providedData[0].buffer = additionalInput;
347  providedData[0].length = additionalInputLen;
348 
349  //The length of the additional input string may be zero
350  if(additionalInputLen > 0)
351  {
352  //Compute (Key, V) = HMAC_DRBG_Update(additional_input, Key, V)
353  hmacDrbgUpdate(context, providedData, arraysize(providedData));
354  }
355 
356  //Generate the requested number of bytes
357  while(outputLen > 0)
358  {
359  //Number of bytes to generate at a time
360  n = MIN(outputLen, outLen);
361 
362  //Compute V = HMAC(Key, V)
363  hmacInit(hmacContext, context->hashAlgo, context->k, outLen);
364  hmacUpdate(hmacContext, context->v, outLen);
365  hmacFinal(hmacContext, context->v);
366 
367  //Copy data to the output buffer
368  osMemcpy(output, context->v, n);
369 
370  //Next output block
371  output += n;
372  outputLen -= n;
373  }
374 
375  //Compute (Key, V) = HMAC_DRBG_Update(additional_input, Key, V)
376  if(additionalInputLen > 0)
377  {
378  hmacDrbgUpdate(context, providedData, arraysize(providedData));
379  }
380  else
381  {
382  hmacDrbgUpdate(context, NULL, 0);
383  }
384 
385  //Increment reseed_counter
386  context->reseedCounter++;
387 
388  //Release exclusive access to the PRNG state
389  osReleaseMutex(&context->mutex);
390 
391  //Successful processing
392  return NO_ERROR;
393 }
394 
395 
396 /**
397  * @brief Release PRNG context
398  * @param[in] context Pointer to the HMAC_DRBG context
399  **/
400 
402 {
403  //Valid HMAC_DRBG context?
404  if(context != NULL)
405  {
406  //Free previously allocated resources
407  osDeleteMutex(&context->mutex);
408 
409  //Erase the contents of the internal state
410  osMemset(context, 0, sizeof(HmacDrbgContext));
411  }
412 }
413 
414 
415 /**
416  * @brief Update internal state
417  * @param[in] context Pointer to the HMAC_DRBG context
418  * @param[in] providedData The data to be used
419  * @param[in] providedDataLen Number of data chunks representing the data
420  **/
421 
422 void hmacDrbgUpdate(HmacDrbgContext *context, const DataChunk *providedData,
423  uint_t providedDataLen)
424 {
425  uint8_t c;
426  size_t i;
427  size_t outLen;
428  HmacContext *hmacContext;
429 
430  //Point to the HMAC context
431  hmacContext = &context->hmacContext;
432  //Determine the output block length
433  outLen = context->hashAlgo->digestSize;
434 
435  //Constant byte 0x00
436  c = 0;
437 
438  //Compute K = HMAC(K, V || 0x00 || provided_data)
439  hmacInit(hmacContext, context->hashAlgo, context->k, outLen);
440  hmacUpdate(hmacContext, context->v, outLen);
441  hmacUpdate(hmacContext, &c, sizeof(c));
442 
443  for(i = 0; i < providedDataLen; i++)
444  {
445  hmacUpdate(hmacContext, providedData[i].buffer,
446  providedData[i].length);
447  }
448 
449  hmacFinal(hmacContext, context->k);
450 
451  //Compute V = HMAC(K, V)
452  hmacInit(hmacContext, context->hashAlgo, context->k, outLen);
453  hmacUpdate(hmacContext, context->v, outLen);
454  hmacFinal(hmacContext, context->v);
455 
456  //Any data provided?
457  if(providedDataLen > 0)
458  {
459  //Constant byte 0x01
460  c = 1;
461 
462  //Compute K = HMAC(K, V || 0x01 || provided_data)
463  hmacInit(hmacContext, context->hashAlgo, context->k, outLen);
464  hmacUpdate(hmacContext, context->v, outLen);
465  hmacUpdate(hmacContext, &c, sizeof(c));
466 
467  for(i = 0; i < providedDataLen; i++)
468  {
469  hmacUpdate(hmacContext, providedData[i].buffer,
470  providedData[i].length);
471  }
472 
473  hmacFinal(hmacContext, context->k);
474 
475  //Compute V = HMAC(K, V)
476  hmacInit(hmacContext, context->hashAlgo, context->k, outLen);
477  hmacUpdate(hmacContext, context->v, outLen);
478  hmacFinal(hmacContext, context->v);
479  }
480 }
481 
482 #endif
uint8_t k[MAX_HASH_DIGEST_SIZE]
Key.
Definition: hmac_drbg.h:61
HMAC algorithm context.
Definition: hmac.h:59
OsMutex mutex
Mutex preventing simultaneous access to the PRNG state.
Definition: hmac_drbg.h:56
bool_t osCreateMutex(OsMutex *mutex)
Create a mutex object.
#define PrngAlgo
Definition: crypto.h:1008
void hmacDrbgDeinit(HmacDrbgContext *context)
Release PRNG context.
Definition: hmac_drbg.c:401
size_t securityStrength
Security strength.
Definition: hmac_drbg.h:59
size_t digestSize
Definition: crypto.h:1130
const void * buffer
Definition: crypto.h:1053
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
error_t(* PrngAlgoSeed)(void *context, const uint8_t *seed, size_t length)
Definition: crypto.h:1107
error_t(* PrngAlgoReseed)(void *context, const uint8_t *seed, size_t length)
Definition: crypto.h:1110
@ ERROR_PRNG_NOT_READY
Definition: error.h:252
HMAC_DRBG PRNG context.
Definition: hmac_drbg.h:55
HmacContext hmacContext
HMAC context.
Definition: hmac_drbg.h:58
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
error_t hmacDrbgSeedEx(HmacDrbgContext *context, const uint8_t *entropyInput, size_t entropyInputLen, const uint8_t *nonce, size_t nonceLen, const uint8_t *personalizationString, size_t personalizationStringLen)
Seed the PRNG state (with nonce and personalization string)
Definition: hmac_drbg.c:131
error_t
Error codes.
Definition: error.h:43
@ ERROR_RESEED_REQUIRED
Definition: error.h:312
HMAC_DRBG pseudorandom number generator.
error_t hmacDrbgGenerateEx(HmacDrbgContext *context, const uint8_t *additionalInput, size_t additionalInputLen, uint8_t *output, size_t outputLen)
Generate pseudorandom data (with additional input)
Definition: hmac_drbg.c:306
General definitions for cryptographic algorithms.
error_t hmacDrbgInit(HmacDrbgContext *context, const HashAlgo *hashAlgo)
Initialize PRNG context.
Definition: hmac_drbg.c:62
#define HMAC_DRBG_MAX_RESEED_INTERVAL
Definition: hmac_drbg.h:39
uint8_t length
Definition: tcp.h:375
error_t hmacDrbgReseed(HmacDrbgContext *context, const uint8_t *seed, size_t length)
Reseed the PRNG state.
Definition: hmac_drbg.c:209
#define MIN(a, b)
Definition: os_port.h:63
Data chunk descriptor.
Definition: crypto.h:1052
__weak_func void hmacUpdate(HmacContext *context, const void *data, size_t length)
Update the HMAC context with a portion of the message being hashed.
Definition: hmac.c:201
uint64_t reseedCounter
Reseed counter.
Definition: hmac_drbg.h:62
void(* PrngAlgoDeinit)(void *context)
Definition: crypto.h:1116
void osDeleteMutex(OsMutex *mutex)
Delete a mutex object.
uint8_t n
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
__weak_func void hmacFinal(HmacContext *context, uint8_t *digest)
Finish the HMAC calculation.
Definition: hmac.c:218
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
error_t hmacDrbgGenerate(HmacDrbgContext *context, uint8_t *output, size_t length)
Generate pseudorandom data.
Definition: hmac_drbg.c:287
error_t(* PrngAlgoGenerate)(void *context, uint8_t *output, size_t length)
Definition: crypto.h:1113
const PrngAlgo hmacDrbgPrngAlgo
Definition: hmac_drbg.c:43
size_t length
Definition: crypto.h:1054
Common interface for hash algorithms.
Definition: crypto.h:1124
void hmacDrbgUpdate(HmacDrbgContext *context, const DataChunk *providedData, uint_t providedDataLen)
Update internal state.
Definition: hmac_drbg.c:422
uint8_t v[MAX_HASH_DIGEST_SIZE]
Value V.
Definition: hmac_drbg.h:60
const HashAlgo * hashAlgo
Hash function.
Definition: hmac_drbg.h:57
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:138
__weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen)
Initialize HMAC calculation.
Definition: hmac.c:140
error_t(* PrngAlgoInit)(void *context)
Definition: crypto.h:1105
error_t hmacDrbgSeed(HmacDrbgContext *context, const uint8_t *seed, size_t length)
Seed the PRNG state.
Definition: hmac_drbg.c:109
uint8_t nonce[]
Definition: ntp_common.h:239
@ NO_ERROR
Success.
Definition: error.h:44
uint8_t c
Definition: ndp.h:514
Debugging facilities.
#define arraysize(a)
Definition: os_port.h:71
error_t hmacDrbgReseedEx(HmacDrbgContext *context, const uint8_t *entropyInput, size_t entropyInputLen, const uint8_t *additionalInput, size_t additionalInputLen)
Reseed the PRNG state (with additional input)
Definition: hmac_drbg.c:228