xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/controller/src/ble_ll_rand.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *  http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <stdint.h>
21 #include <assert.h>
22 #include <string.h>
23 #include "syscfg/syscfg.h"
24 #include "os/os.h"
25 #include "nimble/ble.h"
26 #include "nimble/nimble_opt.h"
27 #include "controller/ble_hw.h"
28 #include "controller/ble_ll.h"
29 #if MYNEWT_VAL(TRNG)
30 #include "trng/trng.h"
31 #endif
32 
33 #if MYNEWT_VAL(TRNG)
34 static struct trng_dev *g_trng;
35 #else
36 /* This is a simple circular buffer for holding N samples of random data */
37 struct ble_ll_rnum_data
38 {
39     uint8_t *rnd_in;
40     uint8_t *rnd_out;
41     volatile uint8_t rnd_size;
42 };
43 
44 struct ble_ll_rnum_data g_ble_ll_rnum_data;
45 uint8_t g_ble_ll_rnum_buf[MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)];
46 
47 #define IS_RNUM_BUF_END(x)  \
48     (x == &g_ble_ll_rnum_buf[MYNEWT_VAL(BLE_LL_RNG_BUFSIZE) - 1])
49 
50 void
ble_ll_rand_sample(uint8_t rnum)51 ble_ll_rand_sample(uint8_t rnum)
52 {
53     os_sr_t sr;
54 
55     OS_ENTER_CRITICAL(sr);
56     if (g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)) {
57         ++g_ble_ll_rnum_data.rnd_size;
58         g_ble_ll_rnum_data.rnd_in[0] = rnum;
59         if (IS_RNUM_BUF_END(g_ble_ll_rnum_data.rnd_in)) {
60             g_ble_ll_rnum_data.rnd_in = g_ble_ll_rnum_buf;
61         } else {
62             ++g_ble_ll_rnum_data.rnd_in;
63         }
64     } else {
65         /* Stop generating random numbers as we are full */
66         ble_hw_rng_stop();
67     }
68     OS_EXIT_CRITICAL(sr);
69 }
70 #endif
71 
72 /* Get 'len' bytes of random data */
73 int
ble_ll_rand_data_get(uint8_t * buf,uint8_t len)74 ble_ll_rand_data_get(uint8_t *buf, uint8_t len)
75 {
76 #if MYNEWT_VAL(TRNG)
77     size_t num;
78 
79     while (len) {
80         num = trng_read(g_trng, buf, len);
81         buf += num;
82         len -= num;
83     }
84 #else
85     uint8_t rnums;
86     os_sr_t sr;
87 
88     while (len != 0) {
89         OS_ENTER_CRITICAL(sr);
90         rnums = g_ble_ll_rnum_data.rnd_size;
91         if (rnums > len) {
92             rnums = len;
93         }
94         len -= rnums;
95         g_ble_ll_rnum_data.rnd_size -= rnums;
96         while (rnums) {
97             buf[0] = g_ble_ll_rnum_data.rnd_out[0];
98             if (IS_RNUM_BUF_END(g_ble_ll_rnum_data.rnd_out)) {
99                 g_ble_ll_rnum_data.rnd_out = g_ble_ll_rnum_buf;
100             } else {
101                 ++g_ble_ll_rnum_data.rnd_out;
102             }
103             ++buf;
104             --rnums;
105         }
106         OS_EXIT_CRITICAL(sr);
107 
108         /* Make sure rng is started! */
109         ble_hw_rng_start();
110 
111         /* Wait till bytes are in buffer. */
112         if (len) {
113             while ((g_ble_ll_rnum_data.rnd_size < len) &&
114                    (g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE))) {
115                 /* Spin here */
116             }
117         }
118     }
119 #endif
120     return BLE_ERR_SUCCESS;
121 }
122 
123 /**
124  * Called to obtain a "prand" as defined in core V4.2 Vol 6 Part B 1.3.2.2
125  *
126  * @param prand
127  */
128 void
ble_ll_rand_prand_get(uint8_t * prand)129 ble_ll_rand_prand_get(uint8_t *prand)
130 {
131     uint16_t sum;
132 
133     while (1) {
134         /* Get 24 bits of random data */
135         ble_ll_rand_data_get(prand, 3);
136 
137         /* Prand cannot be all zeros or 1's. */
138         sum = prand[0] + prand[1] + prand[2];
139         if ((sum != 0) && (sum != (3 * 0xff))) {
140             break;
141         }
142     }
143 
144     /* Upper two bits must be 01 */
145     prand[2] &= ~0xc0;
146     prand[2] |= 0x40;
147 }
148 
149 /**
150  * Start the generation of random numbers
151  *
152  * @return int
153  */
154 int
ble_ll_rand_start(void)155 ble_ll_rand_start(void)
156 {
157 #if MYNEWT_VAL(TRNG)
158     /* Nothing to do - this is handled by driver */
159 #else
160     /* Start the generation of numbers if we are not full */
161     if (g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)) {
162         ble_hw_rng_start();
163     }
164 #endif
165     return 0;
166 }
167 
168 /**
169  * Initialize LL random number generation. Should be called only once on
170  * initialization.
171  *
172  * @return int
173  */
174 int
ble_ll_rand_init(void)175 ble_ll_rand_init(void)
176 {
177 #if MYNEWT_VAL(TRNG)
178     g_trng = (struct trng_dev *) os_dev_open("trng", OS_TIMEOUT_NEVER, NULL);
179 #else
180     g_ble_ll_rnum_data.rnd_in = g_ble_ll_rnum_buf;
181     g_ble_ll_rnum_data.rnd_out = g_ble_ll_rnum_buf;
182     ble_hw_rng_init(ble_ll_rand_sample, 1);
183 #endif
184     return 0;
185 }
186