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
//! Support for random number generation

use core::fmt::{self, Debug, Formatter};

use aead::generic_array::{typenum::Unsigned, GenericArray};
use chacha20::{
    cipher::{NewStreamCipher, SyncStreamCipher},
    ChaCha20,
};
use rand::{CryptoRng, RngCore, SeedableRng};

#[cfg(all(feature = "alloc", feature = "getrandom"))]
use crate::buffer::SecretBytes;
use crate::error::Error;

/// The expected length of a seed for `fill_random_deterministic`
pub const DETERMINISTIC_SEED_LENGTH: usize = <ChaCha20 as NewStreamCipher>::KeySize::USIZE;
/// Combined trait for CryptoRng and RngCore
pub trait Rng: CryptoRng + RngCore + Debug {}

impl<T: CryptoRng + RngCore + Debug> Rng for T {}

pub trait KeyMaterial {
    fn read_okm(&mut self, buf: &mut [u8]);
}

impl<C: CryptoRng + RngCore> KeyMaterial for C {
    fn read_okm(&mut self, buf: &mut [u8]) {
        self.fill_bytes(buf);
    }
}

#[cfg(feature = "getrandom")]
#[inline]
pub fn default_rng() -> impl CryptoRng + RngCore + Debug {
    rand::rngs::OsRng
}

/// Fill a mutable slice with random data using the
/// system random number generator.
#[cfg(feature = "getrandom")]
#[inline(always)]
pub fn fill_random(value: &mut [u8]) {
    default_rng().fill_bytes(value);
}

/// Written to be compatible with randombytes_deterministic in libsodium,
/// used to generate a deterministic symmetric encryption key
pub fn fill_random_deterministic(seed: &[u8], output: &mut [u8]) -> Result<(), Error> {
    RandomDet::new(seed).fill_bytes(output);
    Ok(())
}

pub struct RandomDet {
    cipher: ChaCha20,
}

impl Debug for RandomDet {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "RandomDet {{}}")
    }
}

impl SeedableRng for RandomDet {
    type Seed = [u8; DETERMINISTIC_SEED_LENGTH];

    #[inline]
    fn from_seed(seed: Self::Seed) -> Self {
        Self {
            cipher: ChaCha20::new(
                GenericArray::from_slice(&seed[..]),
                GenericArray::from_slice(b"LibsodiumDRG"),
            ),
        }
    }
}

impl CryptoRng for RandomDet {}

impl RngCore for RandomDet {
    #[inline]
    fn next_u32(&mut self) -> u32 {
        let mut buf = [0; 4];
        self.cipher.apply_keystream(&mut buf[..]);
        u32::from_le_bytes(buf)
    }

    #[inline]
    fn next_u64(&mut self) -> u64 {
        let mut buf = [0; 8];
        self.cipher.apply_keystream(&mut buf[..]);
        u64::from_le_bytes(buf)
    }

    #[inline]
    fn fill_bytes(&mut self, bytes: &mut [u8]) {
        bytes.iter_mut().for_each(|b| *b = 0u8);
        self.cipher.apply_keystream(bytes);
    }

    #[inline]
    fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), rand::Error> {
        bytes.iter_mut().for_each(|b| *b = 0u8);
        self.cipher.apply_keystream(bytes);
        Ok(())
    }
}

impl RandomDet {
    pub fn new(seed: &[u8]) -> Self {
        let mut sd = [0u8; DETERMINISTIC_SEED_LENGTH];
        let seed_len = seed.len().max(DETERMINISTIC_SEED_LENGTH);
        sd[..seed_len].copy_from_slice(&seed[..seed_len]);
        Self::from_seed(sd)
    }
}

#[cfg(all(feature = "alloc", feature = "getrandom"))]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
/// Create a new `SecretBytes` instance with random data.
#[inline(always)]
pub fn random_secret(len: usize) -> SecretBytes {
    SecretBytes::new_with(len, fill_random)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::buffer::HexRepr;
    use std::string::ToString;

    #[test]
    fn fill_random_det_expected() {
        let seed = b"testseed000000000000000000000001";
        let mut output = [0u8; 32];
        fill_random_deterministic(seed, &mut output).unwrap();
        assert_eq!(
            HexRepr(output).to_string(),
            "b1923a011cd1adbe89552db9862470c29512a8f51d184dfd778bfe7f845390d1"
        );
    }
}