libcoap_rs/prng.rs
1// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright © The libcoap-rs Contributors, all rights reserved.
4 * This file is part of the libcoap-rs project, see the README file for
5 * general information on this project and the NOTICE.md and LICENSE files
6 * for information regarding copyright ownership and terms of use.
7 *
8 * prng.rs - libcoap pseudo-random number generator function bindings.
9 */
10
11//! Module containing methods for accessing or configuring the libcoap PRNG.
12//!
13//! This module provides basic functions to seed the libcoap PRNG and retrieve random bytes from it.
14//!
15//! Additionally, if the `rand` feature is enabled, this module contains integrations with the
16//! [rand] crate that allow using the libcoap PRNG as a [rand::Rng] or setting the libcoap PRNG to
17//! an existing [rand::Rng].
18
19use std::{
20 ffi::{c_uint, c_void},
21 sync::Mutex,
22};
23
24use libcoap_sys::{coap_prng, coap_prng_init};
25
26use crate::{context::ensure_coap_started, error::RngError};
27
28// TODO If we can assert that libcoap's own thread-safety features are enabled at some point, we
29// don't need these mutexes.
30static COAP_RNG_SEED_MUTEX: Mutex<()> = Mutex::new(());
31static COAP_RNG_ACCESS_MUTEX: Mutex<()> = Mutex::new(());
32
33/// Attempts to fill `dest` with random bytes using libcoap's PRNG.
34///
35/// # Errors
36///
37/// Will return an error if libcoap's PRNG has an error or the underlying mutex was poisoned by a
38/// panic in another thread.
39///
40/// # Example
41///
42/// ```
43/// use libcoap_rs::error::RngError;
44/// use libcoap_rs::prng::coap_prng_try_fill;
45///
46/// let mut token = [0u8; 8];
47/// coap_prng_try_fill(&mut token)?;
48///
49///
50/// # Result::<(), RngError>::Ok(())
51/// ```
52pub fn coap_prng_try_fill(dest: &mut [u8]) -> Result<(), RngError> {
53 ensure_coap_started();
54 let _acc_mutex = COAP_RNG_ACCESS_MUTEX.lock()?;
55 // SAFETY: Supplied pointer and length describe the provided slice.
56 match unsafe { coap_prng(dest.as_mut_ptr() as *mut c_void, dest.len()) } {
57 1 => Ok(()),
58 _v => Err(RngError::Unknown),
59 }
60}
61
62/// Seeds the default PRNG of libcoap with the provided seed.
63///
64/// # Errors
65///
66/// May return an error if the mutex for seeding the PRNG is poisoned, i.e. there was some panic
67/// in a previous attempt of seeding the PRNG.
68pub fn seed_coap_prng(seed: c_uint) -> Result<(), RngError> {
69 ensure_coap_started();
70 let guard = COAP_RNG_SEED_MUTEX.lock()?;
71 unsafe {
72 coap_prng_init(seed);
73 }
74 drop(guard);
75 Ok(())
76}
77
78#[cfg(feature = "rand")]
79pub use rand_integration::*;
80
81/// Module containing integration code with the [rand] crate.
82#[cfg(feature = "rand")]
83mod rand_integration {
84 use core::{
85 any::TypeId,
86 ffi::{c_int, c_void},
87 };
88 use std::sync::Mutex;
89
90 use libcoap_sys::coap_set_prng;
91 use rand_core::{CryptoRng, TryRngCore, UnwrapErr, UnwrapMut};
92
93 use crate::{context::ensure_coap_started, error::RngError, prng::coap_prng_try_fill};
94
95 static COAP_RNG_FN_MUTEX: Mutex<Option<Box<dyn ErrorErasingTryRngCore + Send + Sync>>> = Mutex::new(None);
96
97 /// Implementation of the [rand::TryRngCore] trait based on libcoap's PRNG.
98 pub struct CoapRng {}
99
100 impl TryRngCore for CoapRng {
101 type Error = RngError;
102
103 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
104 // This is the same as rand_core::impls::next_u32_via_fill(self), but with
105 // support for returning errors.
106 let mut buf = [0; 4];
107 self.try_fill_bytes(&mut buf)?;
108 Ok(u32::from_le_bytes(buf))
109 }
110
111 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
112 // This is the same as rand_core::impls::next_u64_via_fill(self), but with
113 // support for returning errors.
114 let mut buf = [0; 8];
115 self.try_fill_bytes(&mut buf)?;
116 Ok(u64::from_le_bytes(buf))
117 }
118
119 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RngError> {
120 coap_prng_try_fill(dest)
121 }
122 }
123
124 // For now, we can't implement this, as libcoap falls back to the not cryptographically secure
125 // rand()/srand() if it can't find a cryptographically secure PRNG.
126 // Should be reconsidered either if libcoap removes this fallback or if we can detect whether the
127 // fallback was used.
128 //impl CryptoRng for CoapRng {}
129
130 /// Configures libcoap to use the provided `rng` for pseudo-random number generation instead of its
131 /// default PRNG.
132 ///
133 /// The provided PRNG will be used globally across all contexts.
134 ///
135 /// Important: *DO NOT* provide an instance of [CoapRng] to [set_coap_prng]! This will probably
136 /// lead to a stack overflow, as [CoapRng] would recursively call into itself to generate random
137 /// bytes.
138 /// This function will try to catch attempts to do so, but cannot reliably check for all
139 /// possible cases (e.g., if the [CoapRng] is wrapped in another custom type).
140 ///
141 /// # Errors
142 ///
143 /// May return [RngError::GlobalMutexPoisonError] if the underlying mutex protecting the RNG is
144 /// poisoned, i.e. a thread panicked while holding the lock (which should only happen if the
145 /// previously set RNG panicked).
146 ///
147 /// Will return [RngError::CoapRngAsCustomRng] if the [TryRngCore] implementation provided to
148 /// this function is a known reference to a [CoapRng] instance (which would cause infinite
149 /// recursion).
150 ///
151 /// # Example
152 ///
153 /// ```
154 /// use rand_core::{CryptoRng, RngCore};
155 /// use libcoap_rs::error::RngError;
156 /// use libcoap_rs::prng::{coap_prng_try_fill, set_coap_prng};
157 ///
158 /// pub struct NullRng {}
159 ///
160 /// // This implicitly also adds an implementation for TryRngCore, which is the type that is
161 /// // actually required by set_coap_rng().
162 /// impl RngCore for NullRng {
163 /// fn next_u32(&mut self) -> u32 {
164 /// 0
165 /// }
166 ///
167 /// fn next_u64(&mut self) -> u64 {
168 /// 0
169 /// }
170 ///
171 /// fn fill_bytes(&mut self, dest: &mut [u8]) {
172 /// dest.fill(0);
173 /// }
174 /// }
175 ///
176 /// // Obviously, this is just for demonstration purposes and should not actually be done.
177 /// impl CryptoRng for NullRng {}
178 ///
179 /// set_coap_prng(NullRng{})?;
180 /// let mut token = [1u8; 8];
181 /// coap_prng_try_fill(&mut token)?;
182 ///
183 /// assert_eq!(&token, &[0u8; 8]);
184 ///
185 ///
186 /// # Result::<(), RngError>::Ok(())
187 /// ```
188 pub fn set_coap_prng<RNG: TryRngCore + CryptoRng + Send + Sync + 'static>(rng: RNG) -> Result<(), RngError> {
189 // Check that we aren't adding CoapRng to libcoap (which would cause infinite
190 // recursion/stack overflows).
191 const RECURSIVE_RNG_TYPES: &[TypeId] = &[
192 TypeId::of::<CoapRng>(),
193 TypeId::of::<UnwrapErr<CoapRng>>(),
194 TypeId::of::<UnwrapMut<CoapRng>>(),
195 ];
196 if RECURSIVE_RNG_TYPES.contains(&TypeId::of::<RNG>()) {
197 return Err(RngError::CoapRngAsCustomRng);
198 }
199 ensure_coap_started();
200 let mut guard = COAP_RNG_FN_MUTEX.lock()?;
201 *guard = Some(Box::new(rng));
202 // SAFETY: Pointer is valid and pointed-to function does what libcoap expects.
203 unsafe {
204 coap_set_prng(Some(prng_callback));
205 }
206 drop(guard);
207 Ok(())
208 }
209
210 /// Trait that provides the same functionality as [TryRngCore], but with a fixed error type
211 /// containing no detailed error information.
212 ///
213 /// Used in the type definition for the RNG function container provided to libcoap
214 /// [COAP_RNG_FN_MUTEX], since we can't use [TryRngCore] directly without making the
215 /// type dyn-incompatible.
216 trait ErrorErasingTryRngCore {
217 /// Does the same as [TryRngCore], but converts all errors to [RngError::Unknown].
218 fn try_fill_bytes_erase_error(&mut self, dest: &mut [u8]) -> Result<(), RngError>;
219 }
220
221 impl<T: TryRngCore<Error = E>, E> ErrorErasingTryRngCore for T {
222 fn try_fill_bytes_erase_error(&mut self, dest: &mut [u8]) -> Result<(), RngError> {
223 <T as TryRngCore>::try_fill_bytes(self, dest).map_err(|_e| RngError::Unknown)
224 }
225 }
226
227 /// Callback provided to libcoap for generating random numbers.
228 ///
229 /// # Safety
230 ///
231 /// This function is intended as a [libcoap_sys::coap_rand_func_t], therefore `out` should be valid
232 /// and point to the start of an area of memory that can be filled with `len` bytes.
233 unsafe extern "C" fn prng_callback(out: *mut c_void, len: usize) -> c_int {
234 let out_slice = std::slice::from_raw_parts_mut(out as *mut u8, len);
235 match COAP_RNG_FN_MUTEX.lock() {
236 Ok(mut rng_fn) => rng_fn
237 .as_mut()
238 .expect("rng_callback has been set, but no RNG was set")
239 .try_fill_bytes_erase_error(out_slice)
240 .map_or(0, |_| 1),
241 Err(_e) => 0,
242 }
243 }
244}