| 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<DefaultSecureRng> = RefCell::new(DefaultSecureRng::new()); | |
| } | |
|  | |
| pub trait NewWithSeed { | |
|     type Seed; | |
|     fn new_with_seed(seed: Self::Seed) -> Self; | |
| } | |
|  | |
| pub trait RandomElementInModulus<T, M> { | |
|     /// Sample Random element of type T in range [0, modulus) | |
|     fn random(&mut self, modulus: &M) -> T; | |
| } | |
|  | |
| pub trait RandomGaussianElementInModulus<T, M> { | |
|     /// 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<M> | |
| where | |
|     M: ?Sized, | |
| { | |
|     /// Fill container with random elements of type of its elements | |
|     fn random_fill(&mut self, container: &mut M); | |
| } | |
|  | |
| pub trait RandomFillUniformInModulus<M, P> | |
| 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<M, P> | |
| 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: <ChaCha8Rng as SeedableRng>::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 = <ChaCha8Rng as SeedableRng>::Seed; | |
|     fn new_with_seed(seed: Self::Seed) -> Self { | |
|         DefaultSecureRng::new_seeded(seed) | |
|     } | |
| } | |
|  | |
| impl<T, C> RandomFillUniformInModulus<[T], C> for DefaultSecureRng | |
| where | |
|     T: PrimInt + SampleUniform, | |
|     C: Modulus<Element = T>, | |
| { | |
|     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<T, C> RandomFillGaussianInModulus<[T], C> for DefaultSecureRng | |
| where | |
|     T: PrimInt, | |
|     C: Modulus<Element = T>, | |
| { | |
|     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<T> 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<T> 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<T> RandomElementInModulus<T, T> for DefaultSecureRng | |
| where | |
|     T: Zero + SampleUniform, | |
| { | |
|     fn random(&mut self, modulus: &T) -> T { | |
|         Uniform::new(T::zero(), modulus).sample(&mut self.rng) | |
|     } | |
| } | |
|  | |
| impl<T, M: Modulus<Element = T>> RandomGaussianElementInModulus<T, M> 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<F, R>(func: F) -> R | |
|     where | |
|         F: Fn(&Self) -> R, | |
|     { | |
|         DEFAULT_RNG.with_borrow(|r| func(r)) | |
|     } | |
|  | |
|     fn with_local_mut<F, R>(func: F) -> R | |
|     where | |
|         F: Fn(&mut Self) -> R, | |
|     { | |
|         DEFAULT_RNG.with_borrow_mut(|r| func(r)) | |
|     } | |
|  | |
|     fn with_local_mut_mut<F, R>(func: &mut F) -> R | |
|     where | |
|         F: FnMut(&mut Self) -> R, | |
|     { | |
|         DEFAULT_RNG.with_borrow_mut(|r| func(r)) | |
|     } | |
| }
 |