1 /*
2  * Copyright (c) 2025, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <nrfx.h>
35 
36 #if NRFX_CHECK(NRFX_CRACEN_ENABLED)
37 
38 #include <hal/nrf_cracen.h>
39 #include <hal/nrf_cracen_rng.h>
40 #include <hal/nrf_cracen_cm.h>
41 #include <helpers/nrf_cracen_cm_dma.h>
42 #include <soc/nrfx_coredep.h>
43 
44 /* TRNG HW chosen configuration options */
45 #define TRNG_CLK_DIV                0
46 #define TRNG_OFF_TIMER_VAL          0
47 #define TRNG_INIT_WAIT_VAL        512
48 #define TRNG_NUMBER_128BIT_BLOCKS   4
49 
50 #define TRNG_CONDITIONING_KEY_SIZE 4 /* Size of the conditioning key: 4 words, 16 bytes */
51 
52 #define AES_ECB_BLK_SZ 16U /* 128 bits */
53 
54 #define CTR_DRBG_MAX_BYTES_PER_REQUEST  (1 << 16)                            /* NIST.SP.800-90Ar1:Table 3 */
55 #define CTR_DRBG_RESEED_INTERVAL        ((uint64_t)1 << 48)                  /* 2^48 as per NIST spec */
56 #define CTR_DRBG_KEY_SIZE               32U                                  /* 256 bits AES Key */
57 #define CTR_DRBG_ENTROPY_SIZE           (CTR_DRBG_KEY_SIZE + AES_ECB_BLK_SZ) /* Seed equals key-len + 16 */
58 
59 /* Return values common between these driver internal functions: */
60 typedef enum {
61     OK                =  0,  /* The function or operation succeeded */
62     ERROR             = -1,  /* Generic error */
63     ERR_TOO_BIG       = -2,  /* Requested size too large */
64     TRNG_RESET_NEEDED = -5,  /* TRNG needs to be reset due to a failure */
65     HW_PROCESSING     = -10, /* Waiting for the hardware to produce data */
66 } cracen_ret_t;
67 
68 /* Internal status of the CTR_DRBG RNG driver */
69 typedef struct {
70     uint8_t          key[CTR_DRBG_KEY_SIZE];
71     uint8_t          value[AES_ECB_BLK_SZ];
72     uint64_t         reseed_counter;
73     nrfx_drv_state_t initialized;
74     bool             trng_conditioning_key_set;
75 } nrfx_cracen_cb_t;
76 
77 static nrfx_cracen_cb_t m_cb;
78 
79 /*
80  * Initialize the TRNG HW and the TRNG driver status.
81  */
trng_init(void)82 static void trng_init(void)
83 {
84     m_cb.trng_conditioning_key_set = false;
85 
86     /* Disable and softreset the RNG */
87     static const nrf_cracen_rng_control_t control_reset = {.soft_reset = true};
88 
89     nrf_cracen_rng_control_set(NRF_CRACENCORE, &control_reset);
90 
91     /* Change from configuration defaults to what we prefer: */
92     nrf_cracen_rng_off_timer_set(NRF_CRACENCORE, TRNG_OFF_TIMER_VAL);
93     nrf_cracen_rng_clk_div_set(NRF_CRACENCORE, TRNG_CLK_DIV);
94     nrf_cracen_rng_init_wait_val_set(NRF_CRACENCORE, TRNG_INIT_WAIT_VAL);
95 
96     /* Configure the control register and enable */
97     static const nrf_cracen_rng_control_t control_enable = {
98             .enable = true,
99             .number_128_blocks = TRNG_NUMBER_128BIT_BLOCKS,
100     };
101 
102     nrf_cracen_rng_control_set(NRF_CRACENCORE, &control_enable);
103 }
104 
105 /*
106  * Set the TRNG HW conditioning key.
107  *
108  * If there is not yet enough data to do so, return HW_PROCESSING otherwise return OK.
109  */
trng_setup_conditioning_key(void)110 static cracen_ret_t trng_setup_conditioning_key(void)
111 {
112     uint32_t level = nrf_cracen_rng_fifo_level_get(NRF_CRACENCORE);
113 
114     if (level < TRNG_CONDITIONING_KEY_SIZE)
115     {
116         return HW_PROCESSING;
117     }
118 
119     for (uint8_t i = 0; i < TRNG_CONDITIONING_KEY_SIZE; i++)
120     {
121         uint32_t key;
122         key = nrf_cracen_rng_fifo_get(NRF_CRACENCORE);
123         nrf_cracen_rng_key_set(NRF_CRACENCORE, i, key);
124     }
125 
126     m_cb.trng_conditioning_key_set = true;
127 
128     return OK;
129 }
130 
131 /*
132  * If the TRNG HW detected the entropy quality was not ok, return TRNG_RESET_NEEDED.
133  * If the HW is still starting or there is not enough data, return HW_PROCESSING.
134  * If the conditioning key is not yet setup, attempt to fill it or return HW_PROCESSING if
135  * we don't have enough data to fill it yet.
136  * If enough data is ready, fill the /p dst buffer with /p size bytes and return OK.
137  */
trng_get(uint8_t * dst,size_t size)138 static cracen_ret_t trng_get(uint8_t * dst, size_t size)
139 {
140     /* Check that startup tests did not fail and we are ready to read data */
141     switch (nrf_cracen_rng_fsm_state_get(NRF_CRACENCORE))
142     {
143         case NRF_CRACEN_RNG_FSM_STATE_ERROR:
144             return TRNG_RESET_NEEDED;
145         case NRF_CRACEN_RNG_FSM_STATE_RESET:
146             return HW_PROCESSING;
147         case NRF_CRACEN_RNG_FSM_STATE_STARTUP:
148         default:
149             break;
150     }
151 
152     /* Program random key for the conditioning function */
153     if (!m_cb.trng_conditioning_key_set)
154     {
155         cracen_ret_t status = trng_setup_conditioning_key();
156 
157         if (status != OK)
158         {
159             return status;
160         }
161     }
162 
163     uint32_t level = nrf_cracen_rng_fifo_level_get(NRF_CRACENCORE);
164 
165     if (size > level * 4) /* FIFO level in 4-byte words */
166     {
167         return HW_PROCESSING;
168     }
169 
170     while (size)
171     {
172         uint32_t data = nrf_cracen_rng_fifo_get(NRF_CRACENCORE);
173 
174         for (int i = 0; i < 4 && size; i++, size--)
175         {
176             *dst = (uint8_t)(data & 0xFF);
177             dst++;
178             data >>= 8;
179         }
180     }
181 
182     return OK;
183 }
184 
185 /*
186  * @brief Function for filling a buffer with entropy from the CRACEN TRNG.
187  *
188  * When this function returns OK, \p size random bytes have been written to \p p_buf.
189  *
190  * Up to 64 bytes can be requested.
191  * If more is requested the function will return ERR_TOO_BIG without copying any data.
192  *
193  * The entropy generated by this function is NIST800-90B and AIS31 compliant, and can be used to
194  * seed FIPS 140-2 compliant pseudo random number generators.
195  *
196  * @note This function is blocking. It will take around a couple of tenths of microseconds to
197  *       complete depending on the amount of bytes requested.
198  *       (~40 microseconds for an nRF54L15 for the maximum 64 bytes)
199  *
200  * @note Note this is a quite power hungry operation.
201  *
202  * @note This function will enable and configure the CRACEN TRNG HW, wait until the entropy has been
203  *       generated, copy it to the destination buffer and disable the HW.
204  *       This function is meant as as internal utility of this driver but may be used by others with
205  *       extra care, specially if some other component is using CRACEN.
206  *
207  * @param[out] p_buf Buffer into which to copy \p size bytes of entropy.
208  * @param[in]  size  Number of bytes to copy.
209  *
210  * @return OK on success, ERR_TOO_BIG if the requested size is too big.
211  */
trng_entropy_get(uint8_t * p_buf,size_t size)212 static cracen_ret_t trng_entropy_get(uint8_t * p_buf, size_t size)
213 {
214     /* Prevent sizes above the FIFO size to guarantee that the hardware will be able to provide the
215      * requested bytes in one go. */
216     if (size > NRF_CRACEN_RNG_FIFO_SIZE)
217     {
218         return ERR_TOO_BIG;
219     }
220 
221     nrf_cracen_module_enable(NRF_CRACEN, NRF_CRACEN_MODULE_RNG_MASK);
222 
223     int ret = TRNG_RESET_NEEDED;
224 
225     while (true)
226     {
227         if (ret == TRNG_RESET_NEEDED)
228         {
229             trng_init();
230         }
231         ret = trng_get(p_buf, size);
232         if (ret == OK)
233         {
234             break;
235         }
236 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
237         nrfx_coredep_delay_us(1);
238 #endif
239     }
240 
241     nrf_cracen_module_disable(NRF_CRACEN, NRF_CRACEN_MODULE_RNG_MASK);
242 
243     return OK;
244 }
245 
246 /*
247  * Check if the CryptoMaster is done.
248  *
249  * returns OK if done, HW_PROCESSING if not yet done, and ERROR on error.
250  */
cm_done_check(void)251 static cracen_ret_t cm_done_check(void)
252 {
253     uint32_t ret;
254     uint32_t busy;
255 
256     ret = nrf_cracen_cm_int_pending_get(NRF_CRACENCORE);
257 
258     if (ret & (NRF_CRACEN_CM_INT_FETCH_ERROR_MASK | NRF_CRACEN_CM_INT_PUSH_ERROR_MASK))
259     {
260         return ERROR;
261     }
262 
263     busy = nrf_cracen_cm_status_get(NRF_CRACENCORE,
264                                     (NRF_CRACEN_CM_STATUS_BUSY_FETCH_MASK |
265                                      NRF_CRACEN_CM_STATUS_BUSY_PUSH_MASK  |
266                                      NRF_CRACEN_CM_STATUS_PUSH_WAITING_MASK));
267 
268     if (busy)
269     {
270         return HW_PROCESSING;
271     }
272 
273     return OK;
274 }
275 
276 /*
277  * Function for encrypting with AES-ECB the input data using the CRACEN CryptoMaster module.
278  *
279  * @note The key, input and output data are in big endian/cryptographic order. That is, input[0]
280  *       corresponds to the highest byte of the 128bit input.
281  *
282  * @note The only failure one can normally expect are bus failures due to incorrect pointers.
283  *
284  * @note This function is meant to be used by the nrfx_random_ctr_drbg driver.
285  *       If using it outside of this driver it must be used with care specially if any other
286  *       component is using CRACEN.
287  *
288  * @note The key size needs to be supported by the CRACEN CryptoMaster AES engine.
289  *
290  * @param[in] p_key    Pointer to the key.
291  * @param[in] key_size Size of the key in bytes (valid sizes 16, 24 or 32 => 128, 192 or 256 bits).
292  * @param[in] p_input  Pointer to the input data (16 bytes/128 bits).
293  * @param[in] p_output Pointer to the output data (16 bytes/128 bits).
294  *
295  * @return OK on success, ERROR on failure.
296  */
cm_aes_ecb(uint8_t * p_key,size_t key_size,uint8_t * p_input,uint8_t * p_output)297 static cracen_ret_t cm_aes_ecb(uint8_t * p_key, size_t key_size, uint8_t * p_input,
298                                uint8_t * p_output)
299 {
300     cracen_ret_t ret;
301 
302     static const uint32_t aes_config_value = NRF_CRACEN_CM_AES_CONFIG(
303             NRF_CRACEN_CM_AES_CONFIG_MODE_ECB,
304             NRF_CRACEN_CM_AES_CONFIG_KEY_SW_PROGRAMMED,
305             false, false, false);
306 
307     struct nrf_cracen_cm_dma_desc in_descs[3];
308 
309 #if defined(__GNUC__)
310 #pragma GCC diagnostic push
311 #pragma GCC diagnostic ignored "-Wcast-qual"
312 #endif /* __GNUC__ */
313     in_descs[0].p_addr = (uint8_t *)&aes_config_value;
314 #if defined(__GNUC__)
315 #pragma GCC diagnostic pop
316 #endif /* __GNUC__ */
317     in_descs[0].length = sizeof(aes_config_value) | NRF_CRACEN_CM_DMA_DESC_LENGTH_REALIGN;
318     in_descs[0].dmatag = NRF_CRACEN_CM_DMA_TAG_AES_CONFIG(NRF_CRACEN_CM_AES_REG_OFFSET_CONFIG);
319     in_descs[0].p_next = &in_descs[1];
320 
321     in_descs[1].p_addr = p_key;
322     in_descs[1].length = key_size | NRF_CRACEN_CM_DMA_DESC_LENGTH_REALIGN;
323     in_descs[1].dmatag = NRF_CRACEN_CM_DMA_TAG_AES_CONFIG(NRF_CRACEN_CM_AES_REG_OFFSET_KEY);
324     in_descs[1].p_next = &in_descs[2];
325 
326     in_descs[2].p_addr = p_input;
327     in_descs[2].length = AES_ECB_BLK_SZ | NRF_CRACEN_CM_DMA_DESC_LENGTH_REALIGN;
328     in_descs[2].dmatag = NRF_CRACEN_CM_DMA_TAG_LAST | NRF_CRACEN_CM_DMA_TAG_ENGINE_AES
329                           | NRF_CRACEN_CM_DMA_TAG_DATATYPE_AES_PAYLOAD;
330     in_descs[2].p_next = NRF_CRACEN_CM_DMA_DESC_STOP;
331 
332     struct nrf_cracen_cm_dma_desc out_desc;
333 
334     out_desc.p_addr = p_output;
335     out_desc.length = AES_ECB_BLK_SZ | NRF_CRACEN_CM_DMA_DESC_LENGTH_REALIGN;
336     out_desc.p_next = NRF_CRACEN_CM_DMA_DESC_STOP;
337     out_desc.dmatag = NRF_CRACEN_CM_DMA_TAG_LAST;
338 
339     nrf_cracen_module_enable(NRF_CRACEN, NRF_CRACEN_MODULE_CRYPTOMASTER_MASK);
340 
341     nrf_cracen_cm_fetch_addr_set(NRF_CRACENCORE, (void *)in_descs);
342     nrf_cracen_cm_push_addr_set(NRF_CRACENCORE, (void *)&out_desc);
343 
344     nrf_cracen_cm_config_indirect_set(NRF_CRACENCORE,(nrf_cracen_cm_config_indirect_mask_t)
345                                                      (NRF_CRACEN_CM_CONFIG_INDIRECT_FETCH_MASK |
346                                                       NRF_CRACEN_CM_CONFIG_INDIRECT_PUSH_MASK));
347 
348     /* Make sure the contents of in_descs and out_desc are updated before starting CryptoMaster. */
349     __DMB();
350 
351     nrf_cracen_cm_start(NRF_CRACENCORE);
352 
353     do {
354         /* The HW is so fast that it is better to "busy wait" here than program an
355          * interrupt. This will normally already succeed in the first try
356          */
357 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
358         nrfx_coredep_delay_us(1);
359 #endif
360         ret = cm_done_check();
361     } while (ret == HW_PROCESSING);
362 
363     nrf_cracen_cm_softreset(NRF_CRACENCORE);
364     nrf_cracen_module_disable(NRF_CRACEN, NRF_CRACEN_MODULE_CRYPTOMASTER_MASK);
365 
366     return ret;
367 }
368 
369 /*
370  * Increment by 1 a number stored in memory in big endian representation.
371  * /p v is a pointer to the first byte storing the number.
372  * /p size is the size of the number.
373  */
be_incr(unsigned char * v,size_t size)374 static inline void be_incr(unsigned char * v, size_t size)
375 {
376     unsigned int add = 1;
377 
378     do {
379         size--;
380         add += v[size];
381         v[size] = add & 0xFF;
382         add >>= 8;
383     } while ((add != 0) && (size > 0));
384 }
385 
386 /*
387  * XOR two arrays of /p size bytes.
388  * /p size must be a multiple of 4.
389  */
xor_array(uint32_t * a,const uint32_t * b,size_t size)390 static inline void xor_array(uint32_t * a, const uint32_t * b, size_t size)
391 {
392     uintptr_t end = (uintptr_t)a + size;
393 
394     for (; (uintptr_t)a < end; a++, b++)
395     {
396         *a = *a ^ *b;
397     }
398 }
399 
400 /*
401  * Implementation of the CTR_DRBG_Update process as described in NIST.SP.800-90Ar1 with ctr_len
402  * equal to blocklen.
403  *
404  * Returns OK on success, ERROR on error.
405  */
ctr_drbg_update(uint8_t * data)406 static cracen_ret_t ctr_drbg_update(uint8_t * data)
407 {
408     int r = 0;
409     uint8_t temp[CTR_DRBG_ENTROPY_SIZE];
410     size_t temp_length = 0;
411 
412     while (temp_length < sizeof(temp))
413     {
414         be_incr(m_cb.value, AES_ECB_BLK_SZ);
415 
416         r = cm_aes_ecb(m_cb.key, sizeof(m_cb.key), m_cb.value, temp + temp_length);
417 
418         if (r != OK)
419         {
420             return ERROR;
421         }
422         temp_length += AES_ECB_BLK_SZ;
423     }
424 
425     if (data)
426     {
427         xor_array((uint32_t *)temp, (uint32_t *)data, sizeof(temp));
428     }
429 
430     memcpy(m_cb.key, temp, sizeof(m_cb.key));
431     memcpy(m_cb.value, temp + sizeof(m_cb.key), sizeof(m_cb.value));
432 
433     return OK;
434 }
435 
436 /*
437  * Re-seed the CTR_DRBG.
438  *
439  * return OK on success, ERROR on error.
440  */
ctr_drbg_reseed(void)441 static cracen_ret_t ctr_drbg_reseed(void)
442 {
443     int r;
444     uint8_t entropy[CTR_DRBG_ENTROPY_SIZE];
445 
446     /* Get the entropy used to seed the DRBG */
447     r = trng_entropy_get(entropy, sizeof(entropy));
448     if (r != OK)
449     {
450         return ERROR;
451     }
452 
453     r = ctr_drbg_update(entropy);
454     if (r != OK)
455     {
456         return ERROR;
457     }
458 
459     m_cb.reseed_counter = 1;
460 
461     return OK;
462 }
463 
nrfx_cracen_ctr_drbg_init(void)464 nrfx_err_t nrfx_cracen_ctr_drbg_init(void)
465 {
466     int r;
467 
468     if (m_cb.initialized == NRFX_DRV_STATE_INITIALIZED)
469     {
470         return NRFX_ERROR_ALREADY;
471     }
472 
473     memset(&m_cb, 0, sizeof(m_cb));
474 
475     r = ctr_drbg_reseed();
476     if (r != OK)
477     {
478         return NRFX_ERROR_INTERNAL;
479     }
480 
481     m_cb.initialized = NRFX_DRV_STATE_INITIALIZED;
482     return NRFX_SUCCESS;
483 }
484 
nrfx_cracen_ctr_drbg_uninit(void)485 void nrfx_cracen_ctr_drbg_uninit(void)
486 {
487     NRFX_ASSERT(m_cb.initialized != NRFX_DRV_STATE_UNINITIALIZED);
488 
489     m_cb.initialized = NRFX_DRV_STATE_UNINITIALIZED;
490 }
491 
nrfx_cracen_ctr_drbg_random_get(uint8_t * p_buf,size_t size)492 nrfx_err_t nrfx_cracen_ctr_drbg_random_get(uint8_t * p_buf, size_t size)
493 {
494     NRFX_ASSERT(m_cb.initialized != NRFX_DRV_STATE_UNINITIALIZED);
495 
496     int r = 0;
497 
498     if (size > 0 && p_buf == NULL)
499     {
500         return NRFX_ERROR_INVALID_PARAM;
501     }
502 
503     if (size > CTR_DRBG_MAX_BYTES_PER_REQUEST )
504     {
505         return NRFX_ERROR_INVALID_PARAM;
506     }
507 
508     if (m_cb.reseed_counter >= CTR_DRBG_RESEED_INTERVAL)
509     {
510         r = ctr_drbg_reseed();
511         if (r != OK)
512         {
513             return NRFX_ERROR_INTERNAL;
514         }
515     }
516 
517     while (size > 0)
518     {
519         uint8_t temp[AES_ECB_BLK_SZ];
520         size_t cur_len = (size < AES_ECB_BLK_SZ) ? size : AES_ECB_BLK_SZ;
521 
522         be_incr(m_cb.value, AES_ECB_BLK_SZ);
523 
524         r = cm_aes_ecb(m_cb.key, sizeof(m_cb.key), m_cb.value, temp);
525         if (r != OK)
526         {
527             return NRFX_ERROR_INTERNAL;
528         }
529 
530         memcpy(p_buf, temp, cur_len);
531 
532         size -= cur_len;
533         p_buf += cur_len;
534     }
535 
536     r = ctr_drbg_update(NULL);
537     if (r != OK)
538     {
539         return NRFX_ERROR_INTERNAL;
540     }
541 
542     m_cb.reseed_counter += 1;
543 
544     return NRFX_SUCCESS;
545 }
546 
547 #endif // NRFX_CHECK(NRFX_CRACEN_ENABLED)
548