MCUXpresso SDK Documentation

middleware/wireless/framework/services/SecLib_RNG/RNG_psa.c

middleware/wireless/framework/services/SecLib_RNG/RNG_psa.c#

  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 ***************************************/