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