use std::cell::RefCell; use itertools::izip; use num_traits::{PrimInt, Zero}; use rand::{distributions::Uniform, Rng, RngCore, SeedableRng}; use rand_chacha::ChaCha8Rng; use rand_distr::{uniform::SampleUniform, Distribution}; use crate::{backend::Modulus, utils::WithLocal}; thread_local! { pub(crate) static DEFAULT_RNG: RefCell = RefCell::new(DefaultSecureRng::new()); } pub trait NewWithSeed { type Seed; fn new_with_seed(seed: Self::Seed) -> Self; } pub trait RandomElementInModulus { /// Sample Random element of type T in range [0, modulus) fn random(&mut self, modulus: &M) -> T; } pub trait RandomGaussianElementInModulus { /// Sample Random gaussian element from \mu = 0.0 and \sigma = 3.19. Sampled /// element is converted to signed representation in modulus. fn random(&mut self, modulus: &M) -> T; } pub trait RandomFill where M: ?Sized, { /// Fill container with random elements of type of its elements fn random_fill(&mut self, container: &mut M); } pub trait RandomFillUniformInModulus where M: ?Sized, { /// Fill container with random elements in range [0, modulus) fn random_fill(&mut self, modulus: &P, container: &mut M); } pub trait RandomFillGaussianInModulus where M: ?Sized, { /// Fill container with gaussian elements sampled from normal distribution /// with \mu = 0.0 and \sigma = 3.19. Elements are converted to signed /// represented in the modulus. fn random_fill(&mut self, modulus: &P, container: &mut M); } pub struct DefaultSecureRng { rng: ChaCha8Rng, } impl DefaultSecureRng { pub fn new_seeded(seed: ::Seed) -> DefaultSecureRng { let rng = ChaCha8Rng::from_seed(seed); DefaultSecureRng { rng } } pub fn new() -> DefaultSecureRng { let rng = ChaCha8Rng::from_entropy(); DefaultSecureRng { rng } } pub fn fill_bytes(&mut self, a: &mut [u8; 32]) { self.rng.fill_bytes(a); } } impl NewWithSeed for DefaultSecureRng { type Seed = ::Seed; fn new_with_seed(seed: Self::Seed) -> Self { DefaultSecureRng::new_seeded(seed) } } impl RandomFillUniformInModulus<[T], C> for DefaultSecureRng where T: PrimInt + SampleUniform, C: Modulus, { fn random_fill(&mut self, modulus: &C, container: &mut [T]) { izip!( (&mut self.rng).sample_iter(Uniform::new_inclusive( T::zero(), modulus.largest_unsigned_value() )), container.iter_mut() ) .for_each(|(from, to)| { *to = from; }); } } impl RandomFillGaussianInModulus<[T], C> for DefaultSecureRng where T: PrimInt, C: Modulus, { fn random_fill(&mut self, modulus: &C, container: &mut [T]) { izip!( rand_distr::Normal::new(0.0, 3.19f64) .unwrap() .sample_iter(&mut self.rng), container.iter_mut() ) .for_each(|(from, to)| { *to = modulus.map_element_from_f64(from); }); } } impl RandomFill<[T]> for DefaultSecureRng where T: PrimInt + SampleUniform, { fn random_fill(&mut self, container: &mut [T]) { izip!( (&mut self.rng).sample_iter(Uniform::new_inclusive(T::zero(), T::max_value())), container.iter_mut() ) .for_each(|(from, to)| { *to = from; }); } } impl RandomFill<[T; 32]> for DefaultSecureRng where T: PrimInt + SampleUniform, { fn random_fill(&mut self, container: &mut [T; 32]) { izip!( (&mut self.rng).sample_iter(Uniform::new_inclusive(T::zero(), T::max_value())), container.iter_mut() ) .for_each(|(from, to)| { *to = from; }); } } impl RandomElementInModulus for DefaultSecureRng where T: Zero + SampleUniform, { fn random(&mut self, modulus: &T) -> T { Uniform::new(T::zero(), modulus).sample(&mut self.rng) } } impl> RandomGaussianElementInModulus for DefaultSecureRng { fn random(&mut self, modulus: &M) -> T { modulus.map_element_from_f64( rand_distr::Normal::new(0.0, 3.19f64) .unwrap() .sample(&mut self.rng), ) } } impl WithLocal for DefaultSecureRng { fn with_local(func: F) -> R where F: Fn(&Self) -> R, { DEFAULT_RNG.with_borrow(|r| func(r)) } fn with_local_mut(func: F) -> R where F: Fn(&mut Self) -> R, { DEFAULT_RNG.with_borrow_mut(|r| func(r)) } fn with_local_mut_mut(func: &mut F) -> R where F: FnMut(&mut Self) -> R, { DEFAULT_RNG.with_borrow_mut(|r| func(r)) } }