1/*! *********************************************************************************
2 * Copyright 2025-2026 NXP
3 *
4 * \file
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 ********************************************************************************** */
8#include "RNG_Interface.h"
9#include "fwk_config.h"
10#include "fwk_platform.h"
11#include "fwk_platform_crypto.h"
12#include "fwk_platform_rng.h"
13#if defined(gPlatformHasNbu_d)
14#include "fwk_platform_ics.h"
15#endif
16#if defined(gRngEnableAutoReseed_d) && (gRngEnableAutoReseed_d > 0)
17#include "fwk_workq.h"
18#endif
19#include "entropy_poll.h"
20#include "mbedtls/entropy.h"
21#include "mbedtls/psa_util.h"
22
23/*! *********************************************************************************
24*************************************************************************************
25* Private macros
26*************************************************************************************
27********************************************************************************** */
28
29#define mPRNG_NoOfBits_c (256U)
30#define mPRNG_NoOfBytes_c (mPRNG_NoOfBits_c / 8U)
31
32typedef struct rng_ctx_t
33{
34 bool_t mRngCtxInitialized;
35 bool_t mNeedReseed;
36 uint32_t mPRNG_Requests;
37} RNG_context_t;
38
39/*! *********************************************************************************
40*************************************************************************************
41* Private memory declarations
42*************************************************************************************
43********************************************************************************** */
44
45static RNG_context_t rng_ctx = {
46 .mRngCtxInitialized = FALSE,
47 .mNeedReseed = FALSE,
48 .mPRNG_Requests = gRngMaxRequests_d,
49};
50
51/*! *********************************************************************************
52*************************************************************************************
53* Public prototypes
54*************************************************************************************
55********************************************************************************** */
56
57/*! *********************************************************************************
58*************************************************************************************
59* Private prototypes
60*************************************************************************************
61********************************************************************************** */
62#if defined(gRngEnableAutoReseed_d) && (gRngEnableAutoReseed_d > 0)
63static void RNG_seed_needed_handler(fwk_work_t *work);
64#endif
65
66/*! *********************************************************************************
67*************************************************************************************
68* Private variables
69*************************************************************************************
70********************************************************************************** */
71#if defined(gRngEnableAutoReseed_d) && (gRngEnableAutoReseed_d > 0)
72static fwk_work_t seed_needed_work = {
73 .handler = RNG_seed_needed_handler,
74};
75#endif
76
77/*! *********************************************************************************
78*************************************************************************************
79* Public functions
80*************************************************************************************
81********************************************************************************** */
82
83/*! *********************************************************************************
84 * \brief Initialize the RNG Software Module
85 * Please call SecLib_Init() before calling this function to make sure
86 * RNG hardware is correctly initialized.
87 *
88 * \return Status of the RNG initialization procedure.
89 *
90 ********************************************************************************** */
91int RNG_Init(void)
92{
93 int result = gRngInternalError_d;
94
95 (void)PLATFORM_InitCrypto();
96
97 do
98 {
99 if (rng_ctx.mRngCtxInitialized == TRUE)
100 {
101 result = gRngSuccess_d;
102 break;
103 }
104
105#if defined(gRngEnableAutoReseed_d) && (gRngEnableAutoReseed_d > 0)
106 /* The workqueue is used to post and schedule seed
107 * trig upon user demand using RNG_NotifyReseedNeeded().
108 */
109 if (WORKQ_InitSysWorkQ() < 0)
110 {
111 break;
112 }
113#endif
114
115#if defined(gPlatformHasNbu_d)
116 /* On Host MCU: register callback so NBU can request a reseed */
117 PLATFORM_RegisterReceivedSeedRequest(&RNG_NotifyReseedNeeded);
118#endif
119
120 /* initialize psa crypto hardware */
121 psa_status_t status = psa_crypto_init();
122 if (status != PSA_SUCCESS)
123 {
124 break;
125 }
126
127 /* set global variable mRngCtxInitialized */
128 rng_ctx.mRngCtxInitialized = TRUE;
129
130 /* Set seed for pseudo random number generation */
131 (void)RNG_SetSeed();
132
133 result = gRngSuccess_d;
134 } while (false);
135
136 return result;
137}
138
139/*! *********************************************************************************
140 * \brief Reinitialize the RNG module post-wakeup.
141 * May do nothing, action is dependent on platform.
142 *
143 * \return gRngSuccess_d if successful, gRngInternalError_d if operation fails.
144 *
145 * Note: Failure only possible for specific platforms.
146 *
147 ********************************************************************************** */
148int RNG_ReInit(void)
149{
150 //call Seclib_Reinit from Seclib_psa.c instead
151 return gRngSuccess_d;
152}
153
154/*! *********************************************************************************
155 * \brief DeInitialize the RNG module.
156 * Resets the RNG context variables. Only used for test purposes.
157 *
158 * \return none
159 *
160 ********************************************************************************** */
161void RNG_DeInit(void)
162{
163 /* Free PSA crypto resources */
164 mbedtls_psa_crypto_free();
165 rng_ctx.mRngCtxInitialized = FALSE;
166 rng_ctx.mNeedReseed = FALSE;
167 rng_ctx.mPRNG_Requests = gRngMaxRequests_d;
168}
169
170/*! *********************************************************************************
171 * \brief Generates a 32-bit statistically random number if the hardware is enable
172 * else a PRNG number will be generated
173 * No random number will be generated if the RNG was not initialized
174 * or an error occurs.
175 *
176 * \param[out] pRandomNo Pointer to location where the value will be stored
177 *
178 ********************************************************************************** */
179int RNG_GetTrueRandomNumber(uint32_t *pRandomNo)
180{
181 int status = gRngInternalError_d;
182 do
183 {
184 /* checks on params */
185 if (pRandomNo == NULL)
186 {
187 status = gRngBadArguments_d;
188 break;
189 }
190
191 if (rng_ctx.mRngCtxInitialized != TRUE)
192 {
193 status = gRngNotInitialized_d;
194 break;
195 }
196 /* use PSA to generate best random possible:
197 * generate TRNG if possible else generate PRNG */
198 if (psa_generate_random((uint8_t *)pRandomNo, sizeof(uint32_t)) == PSA_SUCCESS)
199 {
200 status = gRngSuccess_d;
201 }
202 } while (0 != 0);
203 return status;
204}
205
206/*! *********************************************************************************
207 * \brief Generates a bit pseudo-random number up to 256 bits. The PRNG algorithm used depend
208 * platform's cryptographic hardware and software capabilities.
209 *
210 * \param[out] pOut Pointer to the output buffer (max 32 bytes)
211 * \param[in] outBytes The number of bytes to be copied (1-32)
212 * \param[in] pSeed Ignored - please set to NULL
213 * This parameter is ignored because it is no longer needed.
214 * The PRNG is automatically seeded from the true random source.
215 * The length of the seed if present is 32 bytes.
216 *
217 * \return The number of bytes copied OR
218 * -1 if reseed is needed OR
219 * -3 if the PRNG was not initialized OR
220 * -2 if 0 bytes were requested OR
221 * -1 if an error occurred
222 *
223 ********************************************************************************** */
224int RNG_GetPseudoRandomData(uint8_t *pOut, uint8_t outBytes, uint8_t *pSeed)
225{
226 int ret = gRngInternalError_d;
227 NOT_USED(pSeed);
228 do
229 {
230 /* checks on params */
231 if (pOut == NULL || outBytes == 0u)
232 {
233 ret = gRngBadArguments_d;
234 break;
235 }
236
237 if (rng_ctx.mRngCtxInitialized != TRUE)
238 {
239 ret = gRngNotInitialized_d;
240 break;
241 }
242 else
243 {
244#if (gRngMaxRequests_d > 0)
245 if (rng_ctx.mPRNG_Requests == gRngMaxRequests_d)
246 {
247 if (RNG_NotifyReseedNeeded() < 0)
248 {
249 ret = gRngInternalError_d;
250 break;
251 }
252 }
253 /* Continue in spite of the gRngMaxRequests_d limit reached */
254 rng_ctx.mPRNG_Requests++;
255#endif
256
257 if (outBytes > mPRNG_NoOfBytes_c)
258 {
259 outBytes = mPRNG_NoOfBytes_c;
260 }
261
262 /* use PSA to generate best random possible:
263 * generate TRNG if possible else generate PRNG */
264 if (psa_generate_random((uint8_t *)pOut, outBytes) == PSA_SUCCESS)
265 {
266 ret = (int)outBytes;
267 }
268 }
269 } while (false);
270 return ret;
271}
272
273/*! *********************************************************************************
274 * \brief Generate a seed and send it to the NBU core if applicable.
275 * On platforms with NBU, generates entropy via PSA and forwards it.
276 * On all platforms, clears the reseed flag and resets the request counter.
277 *
278 * \return gRngSuccess_d on success, gRngInternalError_d on failure.
279 *
280 ********************************************************************************** */
281int RNG_SetSeed(void)
282{
283 uint8_t seed[mPRNG_NoOfBytes_c];
284 int status = gRngInternalError_d;
285
286 do
287 {
288 if (rng_ctx.mRngCtxInitialized != TRUE)
289 {
290 status = gRngNotInitialized_d;
291 break;
292 }
293
294 /* Generate seed entropy using PSA */
295 if (psa_generate_random(seed, mPRNG_NoOfBytes_c) != PSA_SUCCESS)
296 {
297 break;
298 }
299
300#if defined(gPlatformHasNbu_d)
301 /* Forward seed to the NBU via inter-core communication */
302 if (PLATFORM_SendRngSeed(seed, mPRNG_NoOfBytes_c) < 0)
303 {
304 break;
305 }
306#endif
307
308 status = gRngSuccess_d;
309
310 rng_ctx.mNeedReseed = FALSE;
311
312 /* Reset to 1 the request to pseudo random number generation when reseeding */
313 rng_ctx.mPRNG_Requests = 1U;
314 } while (false);
315
316 return status;
317}
318
319/*! *********************************************************************************
320 * \brief Notify that a reseed is needed. Sets the reseed flag and optionally
321 * submits to the WorkQueue for deferred processing.
322 *
323 * \return gRngSuccess_d on success.
324 *
325 ********************************************************************************** */
326int RNG_NotifyReseedNeeded(void)
327{
328 int status = gRngSuccess_d;
329 do
330 {
331 if (rng_ctx.mRngCtxInitialized != TRUE)
332 {
333 status = gRngNotInitialized_d;
334 break;
335 }
336
337 rng_ctx.mNeedReseed = TRUE;
338#if defined(gRngEnableAutoReseed_d) && (gRngEnableAutoReseed_d > 0)
339 status = WORKQ_Submit(&seed_needed_work);
340#endif
341 } while (false);
342
343 return status;
344}
345
346/*! *********************************************************************************
347 * \brief Returns whether reseeding is required or not.
348 *
349 * \return TRUE if reseed is needed, FALSE otherwise.
350 *
351 ********************************************************************************** */
352bool_t RNG_IsReseedNeeded(void)
353{
354 return rng_ctx.mNeedReseed;
355}
356
357/*! *********************************************************************************
358 * \brief Initialize seed for the PRNG algorithm with an external seed.
359 * If this function is called again, the PRNG will be reseeded.
360 *
361 * \param[in] external_seed Pointer to 32 byte array used to set seed.
362 *
363 * \return gRngSuccess_d on success
364 * 1 if not applicable on this platform.
365 *
366 ********************************************************************************** */
367int RNG_SetExternalSeed(uint8_t *external_seed)
368{
369 (void)external_seed;
370 return 1; /* External seeding not supported on PSA */
371}
372
373/*! *********************************************************************************
374 * \brief not supported - PSA does not expose a PRNG function pointer
375 *
376 ********************************************************************************** */
377fpRngPrng_t RNG_GetPrngFunc(void)
378{
379 return NULL;
380}
381
382/*! *********************************************************************************
383 * \brief not supported - PSA does not expose a PRNG context
384 *
385 ********************************************************************************** */
386void *RNG_GetPrngContext(void)
387{
388 return NULL;
389}
390
391/*! *********************************************************************************
392*************************************************************************************
393* Private functions
394*************************************************************************************
395********************************************************************************** */
396
397#if defined(gRngEnableAutoReseed_d) && (gRngEnableAutoReseed_d > 0)
398static void RNG_seed_needed_handler(fwk_work_t *work)
399{
400 NOT_USED(work);
401 /* Execute reseed request from WorkQ */
402 if (rng_ctx.mNeedReseed == TRUE)
403 {
404 (void)RNG_SetSeed();
405 }
406}
407#endif
408
409#ifdef MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG
410/** External random generator function, implemented by the platform.
411 *
412 * When the compile-time option #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG is enabled,
413 * this function replaces Mbed TLS's entropy and DRBG modules for all
414 * random generation triggered via PSA crypto interfaces.
415 *
416 * \note This random generator must deliver random numbers with cryptographic
417 * quality and high performance. It must supply unpredictable numbers
418 * with a uniform distribution. The implementation of this function
419 * is responsible for ensuring that the random generator is seeded
420 * with sufficient entropy. If you have a hardware TRNG which is slow
421 * or delivers non-uniform output, declare it as an entropy source
422 * with mbedtls_entropy_add_source() instead of enabling this option.
423 *
424 * \param[in,out] context Pointer to the random generator context.
425 * This is all-bits-zero on the first call
426 * and preserved between successive calls.
427 * \param[out] output Output buffer. On success, this buffer
428 * contains random data with a uniform
429 * distribution.
430 * \param output_size The size of the \p output buffer in bytes.
431 * \param[out] output_length On success, set this value to \p output_size.
432 *
433 * \retval #PSA_SUCCESS
434 * Success. The output buffer contains \p output_size bytes of
435 * cryptographic-quality random data, and \c *output_length is
436 * set to \p output_size.
437 * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY
438 * The random generator requires extra entropy and there is no
439 * way to obtain entropy under current environment conditions.
440 * This error should not happen under normal circumstances since
441 * this function is responsible for obtaining as much entropy as
442 * it needs. However implementations of this function may return
443 * #PSA_ERROR_INSUFFICIENT_ENTROPY if there is no way to obtain
444 * entropy without blocking indefinitely.
445 * \retval #PSA_ERROR_HARDWARE_FAILURE
446 * A failure of the random generator hardware that isn't covered
447 * by #PSA_ERROR_INSUFFICIENT_ENTROPY.
448 */
449psa_status_t mbedtls_psa_external_get_random(mbedtls_psa_external_random_context_t *context,
450 uint8_t *output,
451 size_t output_size,
452 size_t *output_length)
453{
454 size_t gen_length;
455 int status;
456 do
457 {
458 /* call to the platform's RNG module to generate randomness*/
459 status = mbedtls_hardware_poll((void *)context, output, output_size, &gen_length);
460 if (status != PSA_SUCCESS)
461 {
462 break;
463 }
464
465 /* Check for potential wrap-around */
466 if (gen_length > output_size)
467 {
468 output_size = 0;
469 }
470 else
471 {
472 output_size -= gen_length;
473 }
474
475 if (*output_length > SIZE_MAX - gen_length)
476 {
477 *output_length = SIZE_MAX;
478 }
479 else
480 {
481 *output_length += gen_length;
482 }
483
484 } while (output_size > 0U); /* while length superior to 0 */
485
486 return status;
487}
488#endif
489
490/********************************** EOF ***************************************/