Skip to main content

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}