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