wincwifi 0.3.0

WINC1500 Wifi chip embedded driver
Documentation
use embedded_nal::nb;
#[cfg(feature = "rng")]
use rand_core::impls;
#[cfg(feature = "rng")]
use rand_core::RngCore;

use crate::manager::PRNG_DATA_LENGTH;
use crate::stack::socket_callbacks::Prng;

use super::StackError;
use super::WincClient;
use super::Xfer;

// 5 seconds max, assuming no additional delays
const PRNG_REQUEST_TIMEOUT_MILLISECONDS: u32 = 5_000;

impl<X: Xfer> WincClient<'_, X> {
    /// Generates a deterministic sequence of random bytes using the pseudo-random number generator (PRNG).
    ///
    /// This function is not suitable for cryptographic use or as a source of entropy,
    /// as the output is entirely predictable and reproducible based on internal state.
    ///
    /// # Arguments
    ///
    /// * `data` - A buffer to store the random bytes generated by the Wi-Fi chip.
    ///   The number of random bytes generated depends on the length of this buffer.
    ///
    /// # Returns
    ///
    /// * `()` - If the random bytes are successfully generated.
    /// * `StackError` - If an error occurs during random byte generation.
    pub fn get_random_bytes(&mut self, data: &mut [u8]) -> nb::Result<(), StackError> {
        match &mut self.callbacks.prng {
            None => {
                self.operation_countdown = PRNG_REQUEST_TIMEOUT_MILLISECONDS;
                let to_recv = data.len().min(PRNG_DATA_LENGTH);
                let data = Prng {
                    offset: 0,
                    rcv_buffer: None,
                };
                self.manager
                    .send_prng(&data as *const _ as u32, to_recv as u16)?;
                self.callbacks.prng = Some(Some(data));
            }
            Some(op_prng) => {
                match op_prng {
                    Some(prng) => {
                        if let Some(rcv_buff) = prng.rcv_buffer {
                            let rcvd_len = rcv_buff.len().min((data[prng.offset..]).len());
                            // copy the buffer
                            data[prng.offset..(prng.offset + rcvd_len)]
                                .copy_from_slice(&rcv_buff[..rcvd_len]);
                            // add the offset
                            let offset = prng.offset + rcvd_len;
                            // check if total length is received
                            if offset >= data.len() {
                                self.callbacks.prng = None;
                                return Ok(());
                            } else {
                                // resend the command
                                let new_data = Prng {
                                    offset,
                                    rcv_buffer: None,
                                };
                                let to_recv = (data[offset..]).len().min(PRNG_DATA_LENGTH);
                                self.manager
                                    .send_prng(&data as *const _ as u32, to_recv as u16)?;
                                self.callbacks.prng = Some(Some(new_data));
                            }
                        } else {
                            self.delay_us(self.poll_loop_delay_us);
                            self.operation_countdown -= 1;
                            if self.operation_countdown == 0 {
                                self.callbacks.prng = None;
                                return Err(nb::Error::Other(StackError::GeneralTimeout));
                            }
                        }
                    }
                    _ => {
                        return Err(nb::Error::Other(StackError::Unexpected));
                    }
                }
            }
        }
        self.dispatch_events()?;
        Err(nb::Error::WouldBlock)
    }
}

#[cfg(feature = "rng")]
impl<X: Xfer> RngCore for WincClient<'_, X> {
    /// Generates and returns a deterministic pseudo-random `u32` value.
    ///
    /// # Returns
    ///
    /// * `u32` - A random value if generation succeeds, or `0xFFFFFFFF` if it fails.
    fn next_u32(&mut self) -> u32 {
        impls::next_u32_via_fill(self)
    }

    /// Generates and returns a deterministic pseudo-random `u64` value.
    ///
    /// # Returns
    ///
    /// * `u64` - A random value if generation succeeds, or `0xFFFFFFFFFFFFFFFF` if it fails.
    fn next_u64(&mut self) -> u64 {
        impls::next_u64_via_fill(self)
    }

    /// Fills the provided byte slice with a deterministic sequence of pseudo-random bytes.
    ///
    /// This function is not suitable for cryptographic use or as a source of entropy,
    /// as the output is entirely predictable and reproducible based on internal state.
    ///
    /// # Arguments
    ///
    /// * `dst` - A mutable buffer to store the random bytes generated by the Wi-Fi chip.
    ///   The number of random bytes generated depends on the length of this buffer.
    ///   If random byte generation fails, the buffer will be filled with `0xFF`.
    fn fill_bytes(&mut self, dst: &mut [u8]) {
        let result = nb::block!(self.get_random_bytes(dst));

        if result.is_err() {
            dst.fill(0xffu8);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::client::{test_shared::*, SocketCallbacks};
    use crate::manager::EventListener;
    #[cfg(feature = "rng")]
    use rand_core::RngCore;

    #[test]
    fn test_prng_timeout() {
        let mut client = make_test_client();
        let mut random_bytes = [0x0u8; 32];

        let result = nb::block!(client.get_random_bytes(&mut random_bytes));
        assert_eq!(result, Err(StackError::GeneralTimeout));
    }

    #[test]
    fn test_prng_success() {
        let mut client = make_test_client();
        const TEST_LEN: usize = 16;
        let test_data: [u8; TEST_LEN] = [0x01u8; TEST_LEN];
        let mut random_bytes = [0x0u8; TEST_LEN];

        let mut my_debug = |callbacks: &mut SocketCallbacks| {
            callbacks.on_prng(&test_data);
        };
        client.debug_callback = Some(&mut my_debug);

        let result = nb::block!(client.get_random_bytes(&mut random_bytes));
        assert_eq!(result, Ok(()));
        assert_eq!(random_bytes, test_data);
    }

    #[test]
    fn test_prng_large_buffer() {
        let mut client = make_test_client();
        let test_data: [u8; PRNG_DATA_LENGTH] = [0x01u8; PRNG_DATA_LENGTH];
        const IN_BUFF_LEN: usize = 1100;
        let mut input_buffer = [0x0u8; IN_BUFF_LEN];

        let mut my_debug = |callbacks: &mut SocketCallbacks| {
            callbacks.on_prng(&test_data);
        };
        client.debug_callback = Some(&mut my_debug);

        let result = nb::block!(client.get_random_bytes(&mut input_buffer));
        assert_eq!(result, Ok(()));

        let mut offset = 0;
        while offset < IN_BUFF_LEN {
            let remaining = IN_BUFF_LEN - offset;
            let step = test_data.len().min(remaining);

            assert_eq!(&input_buffer[offset..offset + step], &test_data[..step]);

            offset += step;
        }
    }

    #[cfg(feature = "rng")]
    #[test]
    fn test_rng_fill_bytes_success() {
        let mut client = make_test_client();
        let test_data: [u8; 32] = [0x01u8; 32];
        let mut random_bytes = [0x0u8; 32];

        let mut my_debug = |callbacks: &mut SocketCallbacks| {
            callbacks.on_prng(&test_data);
        };
        client.debug_callback = Some(&mut my_debug);

        client.fill_bytes(&mut random_bytes);

        assert!(random_bytes.iter().all(|&b| b != 0));
    }

    #[cfg(feature = "rng")]
    #[test]
    fn test_rng_fill_bytes_failure() {
        let mut client = make_test_client();
        let mut random_bytes = [0xffu8; 32];

        client.fill_bytes(&mut random_bytes);

        assert!(random_bytes.iter().all(|&b| b == 0xff));
    }

    #[cfg(feature = "rng")]
    #[test]
    fn test_rng_next_u32_success() {
        let mut client = make_test_client();
        let test_data: [u8; 32] = [0x01u8; 32];

        let mut my_debug = |callbacks: &mut SocketCallbacks| {
            callbacks.on_prng(&test_data);
        };
        client.debug_callback = Some(&mut my_debug);

        let random_u32 = client.next_u32();

        assert!(random_u32 == 0x01010101);
    }

    #[cfg(feature = "rng")]
    #[test]
    fn test_rng_next_u32_failure() {
        let mut client = make_test_client();

        let random_u32 = client.next_u32();

        assert!(random_u32 == 0xffffffff);
    }

    #[cfg(feature = "rng")]
    #[test]
    fn test_rng_next_u64_success() {
        let mut client = make_test_client();
        let test_data: [u8; 32] = [0x01u8; 32];

        let mut my_debug = |callbacks: &mut SocketCallbacks| {
            callbacks.on_prng(&test_data);
        };
        client.debug_callback = Some(&mut my_debug);

        let random_u64 = client.next_u64();

        assert!(random_u64 == 0x0101010101010101);
    }

    #[cfg(feature = "rng")]
    #[test]
    fn test_rng_next_u64_failure() {
        let mut client = make_test_client();

        let random_u64 = client.next_u64();

        assert!(random_u64 == 0xffffffffffffffff);
    }
}