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
use std::{
20
    ffi::{c_uint, c_void},
21
    sync::Mutex,
22
};
23

            
24
use libcoap_sys::{coap_prng, coap_prng_init};
25

            
26
use 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.
30
static COAP_RNG_SEED_MUTEX: Mutex<()> = Mutex::new(());
31
static 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
4
/// use libcoap_rs::error::RngError;
44
/// use libcoap_rs::prng::coap_prng_try_fill;
45
///
46
/// let mut token = [0u8; 8];
47
1
/// coap_prng_try_fill(&mut token)?;
48
1
///
49
///
50
/// # Result::<(), RngError>::Ok(())
51
1
/// ```
52
140
pub fn coap_prng_try_fill(dest: &mut [u8]) -> Result<(), RngError> {
53
140
    ensure_coap_started();
54
140
    let _acc_mutex = COAP_RNG_ACCESS_MUTEX.lock()?;
55
    // SAFETY: Supplied pointer and length describe the provided slice.
56
140
    match unsafe { coap_prng(dest.as_mut_ptr() as *mut c_void, dest.len()) } {
57
140
        1 => Ok(()),
58
        _v => Err(RngError::Unknown),
59
    }
60
140
}
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.
68
pub 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")]
79
pub use rand_integration::*;
80

            
81
/// Module containing integration code with the [rand] crate.
82
#[cfg(feature = "rand")]
83
mod 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
4
    /// 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
4
    ///         dest.fill(0);
173
4
    ///     }
174
4
    /// }
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
1
    /// let mut token = [1u8; 8];
181
1
    /// coap_prng_try_fill(&mut token)?;
182
1
    ///
183
    /// assert_eq!(&token, &[0u8; 8]);
184
1
    ///
185
    ///
186
    /// # Result::<(), RngError>::Ok(())
187
1
    /// ```
188
4
    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
1
        if RECURSIVE_RNG_TYPES.contains(&TypeId::of::<RNG>()) {
197
            return Err(RngError::CoapRngAsCustomRng);
198
1
        }
199
1
        ensure_coap_started();
200
1
        let mut guard = COAP_RNG_FN_MUTEX.lock()?;
201
1
        *guard = Some(Box::new(rng));
202
        // SAFETY: Pointer is valid and pointed-to function does what libcoap expects.
203
1
        unsafe {
204
1
            coap_set_prng(Some(prng_callback));
205
1
        }
206
1
        drop(guard);
207
1
        Ok(())
208
1
    }
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
1
        fn try_fill_bytes_erase_error(&mut self, dest: &mut [u8]) -> Result<(), RngError> {
223
1
            <T as TryRngCore>::try_fill_bytes(self, dest).map_err(|_e| RngError::Unknown)
224
1
        }
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
1
    unsafe extern "C" fn prng_callback(out: *mut c_void, len: usize) -> c_int {
234
1
        let out_slice = std::slice::from_raw_parts_mut(out as *mut u8, len);
235
1
        match COAP_RNG_FN_MUTEX.lock() {
236
1
            Ok(mut rng_fn) => rng_fn
237
1
                .as_mut()
238
1
                .expect("rng_callback has been set, but no RNG was set")
239
1
                .try_fill_bytes_erase_error(out_slice)
240
1
                .map_or(0, |_| 1),
241
            Err(_e) => 0,
242
        }
243
1
    }
244
}