1 /*
2  * Copyright (c) 2021-2024, The TrustedFirmware-M Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "cc3xx_rng.h"
9 
10 #include "cc3xx_error.h"
11 #include "cc3xx_dev.h"
12 
13 #include <assert.h>
14 #include <stdint.h>
15 #include <stddef.h>
16 #include <string.h>
17 
18 #include "fatal_error.h"
19 
20 #ifdef CC3XX_CONFIG_RNG_EXTERNAL_TRNG
21 #include "cc3xx_rng_external_trng.h"
22 #endif /* CC3XX_CONFIG_RNG_EXTERNAL_TRNG */
23 
rng_init(void)24 static void rng_init(void)
25 {
26     /* Enable clock */
27     P_CC3XX->rng.rng_clk_enable = 0x1U;
28 
29     /* reset trng */
30     P_CC3XX->rng.rng_sw_reset = 0x1U;
31 
32     /* Apparently there's no way to tell that the reset has finished, so just do
33      * these things repeatedly until they succeed (and hence the reset has
34      * finished). Works because the reset value of SAMPLE_CNT1 is 0xFFFF.
35      */
36     do {
37         /* Enable clock */
38         P_CC3XX->rng.rng_clk_enable = 0x1U;
39 
40         /* Set subsampling ratio */
41         P_CC3XX->rng.sample_cnt1 = CC3XX_CONFIG_RNG_SUBSAMPLING_RATE;
42 
43     } while (P_CC3XX->rng.sample_cnt1 != CC3XX_CONFIG_RNG_SUBSAMPLING_RATE);
44 
45     /* Temporarily disable the random source */
46     P_CC3XX->rng.rnd_source_enable = 0x0U;
47 
48     /* Clear the interrupts */
49     P_CC3XX->rng.rng_icr = 0x3FU;
50 
51     /* Mask all interrupts except EHR_VALID */
52     P_CC3XX->rng.rng_imr = 0x3EU;
53 
54     /* Select the oscillator ring (And set SOP_SEL to 0x1 as is mandatory) */
55     P_CC3XX->rng.trng_config = CC3XX_CONFIG_RNG_RING_OSCILLATOR_ID | (0x1U << 2);
56 
57     /* Set debug control register to no bypasses */
58     P_CC3XX->rng.trng_debug_control = 0x0U;
59 
60     /* Enable the random source */
61     P_CC3XX->rng.rnd_source_enable = 0x1U;
62 }
63 
rng_finish(void)64 static void rng_finish(void)
65 {
66     /* Disable the random source */
67     P_CC3XX->rng.rnd_source_enable = 0x0U;
68 
69     /* Disable clock */
70     P_CC3XX->rng.rng_clk_enable = 0x0U;
71 }
72 
fill_entropy_buf(uint32_t * buf)73 static cc3xx_err_t fill_entropy_buf(uint32_t *buf) {
74     uint32_t attempt_count = 0;
75     uint32_t idx;
76 
77     /* Wait until the RNG has finished. Any status other than 0x1 indicates
78      * that either the RNG hasn't finished or a statistical test has been
79      * failed.
80      */
81     do {
82         if (P_CC3XX->rng.rng_isr & 0xEU) {
83             /* At least one test has failed - the buffer contents aren't
84              * random.
85              */
86 
87             /* Reset EHR registers */
88             P_CC3XX->rng.rst_bits_counter = 0x1U;
89 
90             /* Clear the interrupt bits to restart generator */
91             P_CC3XX->rng.rng_icr = 0x3FU;
92 
93             attempt_count++;
94         }
95     } while ((! (P_CC3XX->rng.rng_isr & 0x1U))
96              && attempt_count < CC3XX_CONFIG_RNG_MAX_ATTEMPTS);
97 
98     if (attempt_count == CC3XX_CONFIG_RNG_MAX_ATTEMPTS) {
99         rng_finish();
100         FATAL_ERR(CC3XX_ERR_RNG_TOO_MANY_ATTEMPTS);
101         return CC3XX_ERR_RNG_TOO_MANY_ATTEMPTS;
102     }
103 
104     /* Reset EHR register */
105     P_CC3XX->rng.rst_bits_counter = 0x1U;
106 
107     /* Make sure the interrupt is cleared before the generator is
108      * restarted, to avoid a race condition with the hardware
109      */
110     P_CC3XX->rng.rng_icr = 0xFFFFFFFF;
111 
112     /* Reading the EHR_DATA restarts the generator */
113     for (idx = 0; idx < 6; idx++) {
114         buf[idx] = P_CC3XX->rng.ehr_data[idx];
115     }
116 
117     return CC3XX_ERR_SUCCESS;
118 }
119 
120 #ifdef CC3XX_CONFIG_RNG_ENABLE
121 #ifndef CC3XX_CONFIG_RNG_EXTERNAL_TRNG
cc3xx_lowlevel_rng_get_random(uint8_t * buf,size_t length)122 cc3xx_err_t cc3xx_lowlevel_rng_get_random(uint8_t *buf, size_t length)
123 {
124     static uint32_t entropy_buf[6];
125     static size_t entropy_buf_used_idx = sizeof(entropy_buf);
126     size_t copy_size;
127     cc3xx_err_t err;
128 
129     rng_init();
130 
131     while(length > 0) {
132         copy_size = sizeof(entropy_buf) - entropy_buf_used_idx < length ?
133                     sizeof(entropy_buf) - entropy_buf_used_idx : length;
134 
135         /* Fill from entropy buffer if we still have some */
136         memcpy(buf, ((uint8_t *)entropy_buf) + entropy_buf_used_idx, copy_size);
137         length -= copy_size;
138         buf += copy_size;
139         entropy_buf_used_idx += copy_size;
140 
141         if (length == 0) {
142             err = CC3XX_ERR_SUCCESS;
143             goto out;
144         }
145 
146         err = fill_entropy_buf(entropy_buf);
147         if (err != CC3XX_ERR_SUCCESS) {
148             goto out;
149         }
150         entropy_buf_used_idx = 0;
151     }
152 
153     err = CC3XX_ERR_SUCCESS;
154 out:
155     rng_finish();
156 
157     return err;
158 }
159 
160 /* As per NIST SP800-90A A.5.1 */
cc3xx_lowlevel_rng_get_random_uint(uint32_t bound,uint32_t * uint)161 cc3xx_err_t cc3xx_lowlevel_rng_get_random_uint(uint32_t bound, uint32_t *uint)
162 {
163     uint32_t value;
164     uint32_t attempts = 0;
165     cc3xx_err_t err;
166     uint32_t mask;
167 
168     /* Zero is not a sane bound */
169     assert(bound != 0);
170 
171     /* There are two cases that we need to handle differently, the one where we
172      * have a single bit set, and the one where we have multiple. First check
173      * which we have.
174      */
175     if ((bound & (bound - 1)) == 0) {
176         /* If a single bit is set, we can get the mask by subtracting one */
177         mask = bound - 1;
178     } else {
179         /* Else, we shift the all-one word right until it matches the offset of
180          * the leading one-bit in the bound.
181          */
182         mask = UINT32_MAX >> __builtin_clz(bound);
183     }
184 
185     do {
186         /* Only pull as much entropy as we need, as RNG reseeding is slow */
187         err = cc3xx_lowlevel_rng_get_random((uint8_t *)&value,
188                                             (32 - __builtin_clz(bound) + 7) / 8);
189         if (err != CC3XX_ERR_SUCCESS) {
190             return err;
191         }
192 
193         value &= mask;
194 
195         attempts += 1;
196         if (attempts >= CC3XX_CONFIG_RNG_MAX_ATTEMPTS) {
197             FATAL_ERR(CC3XX_ERR_RNG_TOO_MANY_ATTEMPTS);
198             return CC3XX_ERR_RNG_TOO_MANY_ATTEMPTS;
199         }
200     } while (value >= bound);
201 
202     *uint = value;
203 
204     return CC3XX_ERR_SUCCESS;
205 }
206 #else
cc3xx_lowlevel_rng_get_random(uint8_t * buf,size_t length)207 cc3xx_err_t cc3xx_lowlevel_rng_get_random(uint8_t *buf, size_t length)
208 {
209     /* The function rng_get_random() is external to the driver and must
210      * be provided by the integration. This option must be selected in
211      * the cc3xx_config.h file when building CC3XX driver code
212      */
213     return rng_get_random(buf, length);
214 }
215 #endif /* !CC3XX_CONFIG_RNG_EXTERNAL_TRNG */
216 #endif /* CC3XX_CONFIG_RNG_ENABLE */
217