1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * prng.rs - libcoap pseudo-random number generator functions.
4
 * This file is part of the libcoap-rs crate, see the README and LICENSE files for
5
 * more information and terms of use.
6
 * Copyright © 2024 The NAMIB Project Developers, all rights reserved.
7
 * See the README as well as the LICENSE file for more information.
8
 */
9

            
10
//! Module containing methods for accessing or configuring the libcoap PRNG.
11
//!
12
//! This module provides basic functions to seed the libcoap PRNG and retrieve random bytes from it.
13
//!
14
//! Additionally, if the `rand` feature is enabled, this module contains integrations with the
15
//! [rand] crate that allow using the libcoap PRNG as a [rand::Rng] or setting the libcoap PRNG to
16
//! an existing [rand::Rng].
17

            
18
use std::ffi::{c_uint, c_void};
19
#[cfg(feature = "rand")]
20
use std::ffi::c_int;
21
use std::sync::Mutex;
22

            
23
#[cfg(feature = "rand")]
24
use libc::size_t;
25
#[cfg(feature = "rand")]
26
use rand::{CryptoRng, RngCore};
27

            
28
use libcoap_sys::{coap_prng, coap_prng_init};
29
#[cfg(feature = "rand")]
30
use libcoap_sys::coap_set_prng;
31

            
32
use crate::context::ensure_coap_started;
33
use crate::error::RngError;
34

            
35
// TODO If we can assert that libcoap's own thread-safety features are enabled at some point, we
36
//      don't need these mutexes.
37
static COAP_RNG_SEED_MUTEX: Mutex<()> = Mutex::new(());
38
#[cfg(feature = "rand")]
39
static COAP_RNG_FN_MUTEX: Mutex<Option<Box<dyn RngCore + Send + Sync>>> = Mutex::new(None);
40
static COAP_RNG_ACCESS_MUTEX: Mutex<()> = Mutex::new(());
41

            
42
/// Attempts to fill `dest` with random bytes using libcoap's PRNG.
43
///
44
/// # Errors
45
///
46
/// Will return an error if libcoap's PRNG has an error or the underlying mutex was poisoned by a
47
/// panic in another thread.
48
///
49
/// # Example
50
///
51
/// ```
52
4
/// use libcoap_rs::error::RngError;
53
/// use libcoap_rs::prng::coap_prng_try_fill;
54
///
55
/// let mut token = [0u8; 8];
56
1
/// coap_prng_try_fill(&mut token)?;
57
1
///
58
///
59
/// # Result::<(), RngError>::Ok(())
60
1
/// ```
61
140
pub fn coap_prng_try_fill(dest: &mut [u8]) -> Result<(), RngError> {
62
140
    ensure_coap_started();
63
140
    let _acc_mutex = COAP_RNG_ACCESS_MUTEX.lock()?;
64
    // SAFETY: Supplied pointer and length describe the provided slice.
65
140
    match unsafe { coap_prng(dest.as_mut_ptr() as *mut c_void, dest.len()) } {
66
140
        1 => Ok(()),
67
        _v => Err(RngError::Unknown),
68
    }
69
140
}
70
/// Implementation of the [rand::RngCore] trait based on libcoap's PRNG.
71
///
72
/// Important: *DO NOT* provide an instance of [CoapRng] to [set_coap_prng]! This will probably lead
73
/// to a stack overflow, as [CoapRng] would recursively call into itself to generate random bytes.
74
#[cfg(feature = "rand")]
75
pub struct CoapRng {}
76

            
77
#[cfg(feature = "rand")]
78
impl RngCore for CoapRng {
79
    fn next_u32(&mut self) -> u32 {
80
        rand_core::impls::next_u32_via_fill(self)
81
    }
82

            
83
    fn next_u64(&mut self) -> u64 {
84
        rand_core::impls::next_u64_via_fill(self)
85
    }
86

            
87
    fn fill_bytes(&mut self, dest: &mut [u8]) {
88
        self.try_fill_bytes(dest)
89
            .expect("error while generating bytes from libcoap RNG")
90
    }
91

            
92
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
93
        coap_prng_try_fill(dest).map_err(|e| rand::Error::new(e))
94
    }
95
}
96

            
97
// For now, we can't implement this, as libcoap falls back to the not cryptographically secure
98
// rand()/srand() if it can't find a cryptographically secure PRNG.
99
// Should be reconsidered either if libcoap removes this fallback or if we can detect whether the
100
// fallback was used.
101
//impl CryptoRng for CoapRng {}
102

            
103
/// Seeds the default PRNG of libcoap with the provided seed.
104
///
105
/// # Errors
106
///
107
/// May return an error if the mutex for seeding the PRNG is poisoned, i.e. there was some panic
108
/// in a previous attempt of seeding the PRNG.
109
pub fn seed_coap_prng(seed: c_uint) -> Result<(), RngError> {
110
    ensure_coap_started();
111
    let guard = COAP_RNG_SEED_MUTEX.lock()?;
112
    unsafe {
113
        coap_prng_init(seed);
114
    }
115
    drop(guard);
116
    Ok(())
117
}
118

            
119
/// Configures libcoap to use the provided `rng` for pseudo-random number generation instead of its
120
/// default PRNG.
121
///
122
/// The provided PRNG will be used globally across all contexts.
123
///
124
/// # Errors
125
///
126
/// May return an error if the underlying mutex protecting the RNG is poisoned, i.e. a thread
127
/// panicked while holding the lock (which should only happen if the previously set RNG panicked).
128
///
129
/// # Example
130
///
131
/// ```
132
4
/// use rand_core::{CryptoRng, Error, RngCore};
133
/// use libcoap_rs::error::RngError;
134
/// use libcoap_rs::prng::{coap_prng_try_fill, set_coap_prng};
135
///
136
/// pub struct NullRng {}
137
///
138
/// impl RngCore for NullRng {
139
///     fn next_u32(&mut self) -> u32 {
140
///         0
141
///     }
142
///
143
///     fn next_u64(&mut self) -> u64 {
144
///         0
145
///     }
146
///
147
///     fn fill_bytes(&mut self, dest: &mut [u8]) {
148
///         dest.fill(0);
149
///     }
150
///
151
///     fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
152
4
///         dest.fill(0);
153
4
///         Ok(())
154
4
///     }
155
4
/// }
156
///
157
/// // Obviously, this is just for demonstration purposes and should not actually be done.
158
/// impl CryptoRng for NullRng {}
159
///
160
/// set_coap_prng(NullRng{})?;
161
1
/// let mut token = [1u8; 8];
162
1
/// coap_prng_try_fill(&mut token)?;
163
1
///
164
/// assert_eq!(&token, &[0u8; 8]);
165
1
///
166
///
167
/// # Result::<(), RngError>::Ok(())
168
1
/// ```
169
4
#[cfg(feature = "rand")]
170
1
pub fn set_coap_prng<RNG: RngCore + CryptoRng + Send + Sync + 'static>(rng: RNG) -> Result<(), RngError> {
171
1
    ensure_coap_started();
172
1
    let mut guard = COAP_RNG_FN_MUTEX.lock()?;
173
1
    *guard = Some(Box::new(rng));
174
1
    // SAFETY: Pointer is valid and pointed to function does what libcoap expects.
175
1
    unsafe {
176
1
        coap_set_prng(Some(prng_callback));
177
1
    }
178
1
    drop(guard);
179
1
    Ok(())
180
1
}
181

            
182
/// Callback provided to libcoap for generating random numbers.
183
///
184
/// # Safety
185
///
186
/// This function is intended as a [libcoap_sys::coap_rand_func_t], therefore `out` should be valid
187
/// and point to the start of an area of memory that can be filled with `len` bytes.
188
#[cfg(feature = "rand")]
189
35
unsafe extern "C" fn prng_callback(out: *mut c_void, len: size_t) -> c_int {
190
35
    let out_slice = std::slice::from_raw_parts_mut(out as *mut u8, len);
191
35
    match COAP_RNG_FN_MUTEX.lock() {
192
35
        Ok(mut rng_fn) => rng_fn
193
35
            .as_mut()
194
35
            .expect("rng_callback has been set, but no RNG was set")
195
35
            .try_fill_bytes(out_slice)
196
35
            .map_or(0, |_| 1),
197
        Err(_e) => 0,
198
    }
199
35
}