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}