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