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
19#[cfg(feature = "rand")]
20use std::ffi::c_int;
21use std::{
22    ffi::{c_uint, c_void},
23    sync::Mutex,
24};
25
26#[cfg(feature = "rand")]
27use libcoap_sys::coap_set_prng;
28use libcoap_sys::{coap_prng, coap_prng_init};
29#[cfg(feature = "rand")]
30use rand::{CryptoRng, RngCore};
31
32use crate::{context::ensure_coap_started, error::RngError};
33
34// TODO If we can assert that libcoap's own thread-safety features are enabled at some point, we
35//      don't need these mutexes.
36static COAP_RNG_SEED_MUTEX: Mutex<()> = Mutex::new(());
37#[cfg(feature = "rand")]
38static COAP_RNG_FN_MUTEX: Mutex<Option<Box<dyn RngCore + Send + Sync>>> = Mutex::new(None);
39static COAP_RNG_ACCESS_MUTEX: Mutex<()> = Mutex::new(());
40
41/// Attempts to fill `dest` with random bytes using libcoap's PRNG.
42///
43/// # Errors
44///
45/// Will return an error if libcoap's PRNG has an error or the underlying mutex was poisoned by a
46/// panic in another thread.
47///
48/// # Example
49///
50/// ```
51/// use libcoap_rs::error::RngError;
52/// use libcoap_rs::prng::coap_prng_try_fill;
53///
54/// let mut token = [0u8; 8];
55/// coap_prng_try_fill(&mut token)?;
56///
57///
58/// # Result::<(), RngError>::Ok(())
59/// ```
60pub fn coap_prng_try_fill(dest: &mut [u8]) -> Result<(), RngError> {
61    ensure_coap_started();
62    let _acc_mutex = COAP_RNG_ACCESS_MUTEX.lock()?;
63    // SAFETY: Supplied pointer and length describe the provided slice.
64    match unsafe { coap_prng(dest.as_mut_ptr() as *mut c_void, dest.len()) } {
65        1 => Ok(()),
66        _v => Err(RngError::Unknown),
67    }
68}
69/// Implementation of the [rand::RngCore] trait based on libcoap's PRNG.
70///
71/// Important: *DO NOT* provide an instance of [CoapRng] to [set_coap_prng]! This will probably lead
72/// to a stack overflow, as [CoapRng] would recursively call into itself to generate random bytes.
73#[cfg(feature = "rand")]
74pub struct CoapRng {}
75
76#[cfg(feature = "rand")]
77impl RngCore for CoapRng {
78    fn next_u32(&mut self) -> u32 {
79        rand_core::impls::next_u32_via_fill(self)
80    }
81
82    fn next_u64(&mut self) -> u64 {
83        rand_core::impls::next_u64_via_fill(self)
84    }
85
86    fn fill_bytes(&mut self, dest: &mut [u8]) {
87        self.try_fill_bytes(dest)
88            .expect("error while generating bytes from libcoap RNG")
89    }
90
91    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
92        coap_prng_try_fill(dest).map_err(|e| rand::Error::new(e))
93    }
94}
95
96// For now, we can't implement this, as libcoap falls back to the not cryptographically secure
97// rand()/srand() if it can't find a cryptographically secure PRNG.
98// Should be reconsidered either if libcoap removes this fallback or if we can detect whether the
99// fallback was used.
100//impl CryptoRng for CoapRng {}
101
102/// Seeds the default PRNG of libcoap with the provided seed.
103///
104/// # Errors
105///
106/// May return an error if the mutex for seeding the PRNG is poisoned, i.e. there was some panic
107/// in a previous attempt of seeding the PRNG.
108pub fn seed_coap_prng(seed: c_uint) -> Result<(), RngError> {
109    ensure_coap_started();
110    let guard = COAP_RNG_SEED_MUTEX.lock()?;
111    unsafe {
112        coap_prng_init(seed);
113    }
114    drop(guard);
115    Ok(())
116}
117
118/// Configures libcoap to use the provided `rng` for pseudo-random number generation instead of its
119/// default PRNG.
120///
121/// The provided PRNG will be used globally across all contexts.
122///
123/// # Errors
124///
125/// May return an error if the underlying mutex protecting the RNG is poisoned, i.e. a thread
126/// panicked while holding the lock (which should only happen if the previously set RNG panicked).
127///
128/// # Example
129///
130/// ```
131/// use rand_core::{CryptoRng, Error, RngCore};
132/// use libcoap_rs::error::RngError;
133/// use libcoap_rs::prng::{coap_prng_try_fill, set_coap_prng};
134///
135/// pub struct NullRng {}
136///
137/// impl RngCore for NullRng {
138///     fn next_u32(&mut self) -> u32 {
139///         0
140///     }
141///
142///     fn next_u64(&mut self) -> u64 {
143///         0
144///     }
145///
146///     fn fill_bytes(&mut self, dest: &mut [u8]) {
147///         dest.fill(0);
148///     }
149///
150///     fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
151///         dest.fill(0);
152///         Ok(())
153///     }
154/// }
155///
156/// // Obviously, this is just for demonstration purposes and should not actually be done.
157/// impl CryptoRng for NullRng {}
158///
159/// set_coap_prng(NullRng{})?;
160/// let mut token = [1u8; 8];
161/// coap_prng_try_fill(&mut token)?;
162///
163/// assert_eq!(&token, &[0u8; 8]);
164///
165///
166/// # Result::<(), RngError>::Ok(())
167/// ```
168#[cfg(feature = "rand")]
169pub fn set_coap_prng<RNG: RngCore + CryptoRng + Send + Sync + 'static>(rng: RNG) -> Result<(), RngError> {
170    ensure_coap_started();
171    let mut guard = COAP_RNG_FN_MUTEX.lock()?;
172    *guard = Some(Box::new(rng));
173    // SAFETY: Pointer is valid and pointed to function does what libcoap expects.
174    unsafe {
175        coap_set_prng(Some(prng_callback));
176    }
177    drop(guard);
178    Ok(())
179}
180
181/// Callback provided to libcoap for generating random numbers.
182///
183/// # Safety
184///
185/// This function is intended as a [libcoap_sys::coap_rand_func_t], therefore `out` should be valid
186/// and point to the start of an area of memory that can be filled with `len` bytes.
187#[cfg(feature = "rand")]
188unsafe extern "C" fn prng_callback(out: *mut c_void, len: usize) -> c_int {
189    let out_slice = std::slice::from_raw_parts_mut(out as *mut u8, len);
190    match COAP_RNG_FN_MUTEX.lock() {
191        Ok(mut rng_fn) => rng_fn
192            .as_mut()
193            .expect("rng_callback has been set, but no RNG was set")
194            .try_fill_bytes(out_slice)
195            .map_or(0, |_| 1),
196        Err(_e) => 0,
197    }
198}