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,
    }
}