diff --git a/src/backend.rs b/src/backend.rs index 3352263..5f0fa09 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,11 +1,14 @@ use std::marker::PhantomData; use itertools::izip; -use num_traits::{WrappingAdd, WrappingMul, WrappingSub}; +use num_traits::{PrimInt, Signed, ToPrimitive, WrappingAdd, WrappingMul, WrappingSub, Zero}; pub trait Modulus { type Element; - fn is_native() -> bool; + /// Modulus value if it fits in Element + fn q(&self) -> Option; + /// Is modulus native? + fn is_native(&self) -> bool; /// -1 in signed representaiton fn neg_one(&self) -> Self::Element; /// Largest unsigned value that fits in the modulus. That is, q - 1. @@ -13,15 +16,70 @@ pub trait Modulus { /// Smallest unsigned value that fits in the modulus /// Always assmed to be 0. fn smallest_unsigned_value(&self) -> Self::Element; - /// Max +value in signed representation - fn signed_max(&self) -> Self::Element; - /// Min -value in signed representation - fn signed_min(&self) -> Self::Element; + /// Convert unsigned value in signed represetation to i64 + fn to_i64(&self, v: &Self::Element) -> i64; + /// Convert f64 to signed represented in modulus + fn from_f64(&self, v: f64) -> Self::Element; + /// Convert i64 to signed represented in modulus + fn from_i64(&self, v: i64) -> Self::Element; +} + +impl Modulus for u64 { + type Element = u64; + fn is_native(&self) -> bool { + // q of size u64 can never be a naitve modulus + false + } + fn largest_unsigned_value(&self) -> Self::Element { + self - 1 + } + fn neg_one(&self) -> Self::Element { + self - 1 + } + fn smallest_unsigned_value(&self) -> Self::Element { + 0 + } + fn to_i64(&self, v: &Self::Element) -> i64 { + assert!(v < self); + + if *v > (self >> 1) { + ToPrimitive::to_i64(&(self - v)).unwrap() + } else { + ToPrimitive::to_i64(v).unwrap() + } + } + fn from_f64(&self, v: f64) -> Self::Element { + //FIXME (Jay): Before I check whether v is smaller than 0 with `let is_neg = + // o.is_sign_negative() && o != 0.0; I'm ocnfused why didn't I simply check < + // 0.0? + let v = v.round(); + if v < 0.0 { + self - v.to_u64().unwrap() + } else { + v.to_u64().unwrap() + } + } + fn from_i64(&self, v: i64) -> Self::Element { + if v < 0 { + self - v.to_u64().unwrap() + } else { + v.to_u64().unwrap() + } + } + fn q(&self) -> Option { + Some(*self) + } } pub trait ModInit { + type M; + fn new(modulus: Self::M) -> Self; +} + +pub trait GetModulus { type Element; - fn new(q: Self::Element) -> Self; + type M: Modulus; + fn modulus(&self) -> &Self::M; } pub trait VectorOps { @@ -44,7 +102,7 @@ pub trait VectorOps { c: &Self::Element, ); - fn modulus(&self) -> Self::Element; + // fn modulus(&self) -> Self::Element; } pub trait ArithmeticOps { @@ -55,20 +113,28 @@ pub trait ArithmeticOps { fn sub(&self, a: &Self::Element, b: &Self::Element) -> Self::Element; fn neg(&self, a: &Self::Element) -> Self::Element; - fn modulus(&self) -> Self::Element; + // fn modulus(&self) -> Self::Element; } -pub struct ModularOpsU64 { +pub struct ModularOpsU64 { q: u64, logq: usize, barrett_mu: u128, barrett_alpha: usize, + modulus: T, } -impl ModInit for ModularOpsU64 { - type Element = u64; - fn new(q: u64) -> ModularOpsU64 { - let logq = 64 - q.leading_zeros(); +impl ModInit for ModularOpsU64 +where + T: Modulus, +{ + type M = T; + fn new(modulus: Self::M) -> ModularOpsU64 { + assert!(!modulus.is_native()); + + // largest unsigned value modulus fits is modulus-1 + let q = modulus.largest_unsigned_value() + 1; + let logq = 64 - (q + 1u64).leading_zeros(); // barrett calculation let mu = (1u128 << (logq * 2 + 3)) / (q as u128); @@ -79,11 +145,12 @@ impl ModInit for ModularOpsU64 { logq: logq as usize, barrett_alpha: alpha as usize, barrett_mu: mu, + modulus, } } } -impl ModularOpsU64 { +impl ModularOpsU64 { fn add_mod_fast(&self, a: u64, b: u64) -> u64 { debug_assert!(a < self.q); debug_assert!(b < self.q); @@ -136,7 +203,7 @@ impl ModularOpsU64 { } } -impl ArithmeticOps for ModularOpsU64 { +impl ArithmeticOps for ModularOpsU64 { type Element = u64; fn add(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { @@ -155,12 +222,12 @@ impl ArithmeticOps for ModularOpsU64 { self.q - *a } - fn modulus(&self) -> Self::Element { - self.q - } + // fn modulus(&self) -> Self::Element { + // self.q + // } } -impl VectorOps for ModularOpsU64 { +impl VectorOps for ModularOpsU64 { type Element = u64; fn elwise_add_mut(&self, a: &mut [Self::Element], b: &[Self::Element]) { @@ -220,31 +287,131 @@ impl VectorOps for ModularOpsU64 { }); } - fn modulus(&self) -> Self::Element { - self.q + // fn modulus(&self) -> Self::Element { + // self.q + // } +} + +impl GetModulus for ModularOpsU64 +where + T: Modulus, +{ + type Element = T::Element; + type M = T; + fn modulus(&self) -> &Self::M { + &self.modulus } } pub struct WordSizeModulus { - _phantom: PhantomData, + modulus: T, } -impl ModInit for WordSizeModulus { - type Element = T; - fn new(q: M) -> Self { +impl ModInit for WordSizeModulus +where + T: Modulus, +{ + type M = T; + fn new(modulus: T) -> Self { + assert!(modulus.is_native()); // For now assume ModulusOpsU64 is only used for u64 - Self { - _phantom: PhantomData, - } + Self { modulus: modulus } + } +} + +impl ArithmeticOps for WordSizeModulus +where + T: Modulus, + T::Element: WrappingAdd + WrappingSub + WrappingMul + Zero, +{ + type Element = T::Element; + fn add(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + T::Element::wrapping_add(a, b) + } + + fn mul(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + T::Element::wrapping_mul(a, b) } + + fn neg(&self, a: &Self::Element) -> Self::Element { + T::Element::wrapping_sub(&T::Element::zero(), a) + } + + fn sub(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + T::Element::wrapping_sub(a, b) + } + + // fn modulus(&self) -> &T { + // &self.modulus + // } } -// impl ArithmeticOps for -// WordSizeModulus { fn add(&self, a: &Self::Element, b: &Self::Element) -// -> Self::Element { T::wrapping_add(*a, *b) -// } +impl VectorOps for WordSizeModulus +where + T: Modulus, + T::Element: WrappingAdd + WrappingSub + WrappingMul + Zero, +{ + type Element = T::Element; + + fn elwise_add_mut(&self, a: &mut [Self::Element], b: &[Self::Element]) { + izip!(a.iter_mut(), b.iter()).for_each(|(ai, bi)| { + *ai = T::Element::wrapping_add(ai, bi); + }); + } + + fn elwise_sub_mut(&self, a: &mut [Self::Element], b: &[Self::Element]) { + izip!(a.iter_mut(), b.iter()).for_each(|(ai, bi)| { + *ai = T::Element::wrapping_sub(ai, bi); + }); + } + + fn elwise_mul_mut(&self, a: &mut [Self::Element], b: &[Self::Element]) { + izip!(a.iter_mut(), b.iter()).for_each(|(ai, bi)| { + *ai = T::Element::wrapping_mul(ai, bi); + }); + } + + fn elwise_neg_mut(&self, a: &mut [Self::Element]) { + a.iter_mut() + .for_each(|ai| *ai = T::Element::wrapping_sub(&T::Element::zero(), ai)); + } + + fn elwise_scalar_mul(&self, out: &mut [Self::Element], a: &[Self::Element], b: &Self::Element) { + izip!(out.iter_mut(), a.iter()).for_each(|(oi, ai)| { + *oi = T::Element::wrapping_mul(ai, b); + }); + } + + fn elwise_mul(&self, out: &mut [Self::Element], a: &[Self::Element], b: &[Self::Element]) { + izip!(out.iter_mut(), a.iter(), b.iter()).for_each(|(oi, ai, bi)| { + *oi = T::Element::wrapping_mul(ai, bi); + }); + } + + fn elwise_scalar_mul_mut(&self, a: &mut [Self::Element], b: &Self::Element) { + a.iter_mut().for_each(|ai| { + *ai = T::Element::wrapping_mul(ai, b); + }); + } + + fn elwise_fma_mut(&self, a: &mut [Self::Element], b: &[Self::Element], c: &[Self::Element]) { + izip!(a.iter_mut(), b.iter(), c.iter()).for_each(|(ai, bi, ci)| { + *ai = T::Element::wrapping_add(ai, &T::Element::wrapping_mul(bi, ci)); + }); + } -// fn modulus(&self) -> Self::Element { + fn elwise_fma_scalar_mut( + &self, + a: &mut [Self::Element], + b: &[Self::Element], + c: &Self::Element, + ) { + izip!(a.iter_mut(), b.iter()).for_each(|(ai, bi)| { + *ai = T::Element::wrapping_add(ai, &T::Element::wrapping_mul(bi, c)); + }); + } -// } -// } + // fn modulus(&self) -> &T { + // &self.modulus + // } +} diff --git a/src/bool/evaluator.rs b/src/bool/evaluator.rs index e667e91..bf506ab 100644 --- a/src/bool/evaluator.rs +++ b/src/bool/evaluator.rs @@ -9,13 +9,16 @@ use itertools::{izip, partition, Itertools}; use num_traits::{FromPrimitive, Num, One, PrimInt, ToPrimitive, WrappingSub, Zero}; use crate::{ - backend::{ArithmeticOps, ModInit, ModularOpsU64, VectorOps}, + backend::{ArithmeticOps, GetModulus, ModInit, ModularOpsU64, Modulus, VectorOps}, bool::parameters::{MP_BOOL_PARAMS, SP_BOOL_PARAMS}, decomposer::{Decomposer, DefaultDecomposer, NumInfo, RlweDecomposer}, lwe::{decrypt_lwe, encrypt_lwe, lwe_key_switch, lwe_ksk_keygen, measure_noise_lwe, LweSecret}, multi_party::public_key_share, ntt::{self, Ntt, NttBackendU64, NttInit}, - random::{DefaultSecureRng, NewWithSeed, RandomGaussianDist, RandomUniformDist}, + random::{ + DefaultSecureRng, NewWithSeed, RandomFillGaussianInModulus, RandomFillUniformInModulus, + RandomGaussianElementInModulus, + }, rgsw::{ decrypt_rlwe, galois_auto, galois_key_gen, generate_auto_map, public_key_encrypt_rgsw, rgsw_by_rgsw_inplace, rlwe_by_rgsw, secret_key_encrypt_rgsw, IsTrivial, RgswCiphertext, @@ -23,22 +26,29 @@ use crate::{ }, utils::{ fill_random_ternary_secret_with_hamming_weight, generate_prime, mod_exponent, - TryConvertFrom, WithLocal, + TryConvertFrom1, WithLocal, }, Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret, }; -use super::parameters::BoolParameters; +use super::parameters::{BoolParameters, CiphertextModulus}; thread_local! { - static BOOL_EVALUATOR: RefCell>, NttBackendU64, ModularOpsU64>> = RefCell::new(BoolEvaluator::new(MP_BOOL_PARAMS)); + static BOOL_EVALUATOR: RefCell>, NttBackendU64, ModularOpsU64>, ModularOpsU64>>> = RefCell::new(BoolEvaluator::new(MP_BOOL_PARAMS)); } pub fn set_parameter_set(parameter: &BoolParameters) { BoolEvaluator::with_local_mut(|e| *e = BoolEvaluator::new(parameter.clone())) } -impl WithLocal for BoolEvaluator>, NttBackendU64, ModularOpsU64> { +impl WithLocal + for BoolEvaluator< + Vec>, + NttBackendU64, + ModularOpsU64>, + ModularOpsU64>, + > +{ fn with_local(func: F) -> R where F: Fn(&Self) -> R, @@ -92,6 +102,37 @@ where // pub(crate) static CLIENT_KEY: RefCell = // RefCell::new(ClientKey::random()); } +trait BoolEncoding { + type Element; + fn true_el(&self) -> Self::Element; + fn false_el(&self) -> Self::Element; + fn qby4(&self) -> Self::Element; + fn decode(&self, m: Self::Element) -> bool; +} + +impl BoolEncoding for CiphertextModulus { + type Element = T; + fn false_el(&self) -> Self::Element { + todo!() + } + fn qby4(&self) -> Self::Element { + todo!() + } + fn true_el(&self) -> Self::Element { + todo!() + } + fn decode(&self, m: Self::Element) -> bool { + // let m = (((encoded_m + self.pbs_info.rlweq_by8).to_f64().unwrap() * 4f64) + // / self.pbs_info.parameters.rlwe_q().0.to_f64().unwrap()) + // .round() as usize + // % 4usize; + // panic!("Incorrect bool decryption. Got m={m} but expected m to be + // 0 or 1") + + todo!() + } +} + trait PbsKey { type M: Matrix; @@ -105,11 +146,18 @@ trait PbsKey { trait PbsInfo { type Element; - type ModOp: VectorOps + ArithmeticOps; + type Modulus: Modulus; type NttOp: Ntt; type D: Decomposer; - fn rlwe_q(&self) -> Self::Element; - fn lwe_q(&self) -> Self::Element; + + // Although both types have same bounds, they can be different types. For ex, + // type RlweModOp may only support native modulus, where LweModOp may only + // support prime modulus, etc. + type RlweModOp: VectorOps + ArithmeticOps; + type LweModOp: VectorOps + ArithmeticOps; + + fn rlwe_q(&self) -> &Self::Modulus; + fn lwe_q(&self) -> &Self::Modulus; fn br_q(&self) -> usize; fn rlwe_n(&self) -> usize; fn lwe_n(&self) -> usize; @@ -123,8 +171,8 @@ trait PbsInfo { fn auto_decomposer(&self) -> &Self::D; /// Modulus operators - fn modop_lweq(&self) -> &Self::ModOp; - fn modop_rlweq(&self) -> &Self::ModOp; + fn modop_lweq(&self) -> &Self::LweModOp; + fn modop_rlweq(&self) -> &Self::RlweModOp; /// Ntt operators fn nttop_rlweq(&self) -> &Self::NttOp; @@ -188,8 +236,8 @@ struct PublicKey { impl< M: MatrixMut + MatrixEntity, - Rng: NewWithSeed + RandomUniformDist<[M::MatElement], Parameters = M::MatElement>, - ModOp: VectorOps + ModInit, + Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus>, + ModOp: VectorOps + ModInit>, > From< &[CommonReferenceSeededCollectivePublicKeyShare< @@ -218,10 +266,14 @@ where // sample A let seed = value[0].cr_seed; let mut main_rng = Rng::new_with_seed(seed); - RandomUniformDist::random_fill(&mut main_rng, ¶meters.rlwe_q().0, key.get_row_mut(0)); + RandomFillUniformInModulus::random_fill( + &mut main_rng, + parameters.rlwe_q(), + key.get_row_mut(0), + ); // Sum all Bs - let rlweq_modop = ModOp::new(parameters.rlwe_q().0); + let rlweq_modop = ModOp::new(parameters.rlwe_q().clone()); value.iter().for_each(|share_i| { assert!(share_i.cr_seed == seed); assert!(&share_i.parameters == parameters); @@ -256,14 +308,14 @@ fn aggregate_multi_party_server_key_shares< M: MatrixMut + MatrixEntity, S: Copy + PartialEq, D: RlweDecomposer, - ModOp: VectorOps + ModInit, - NttOp: Ntt + NttInit, + ModOp: VectorOps + ModInit>, + NttOp: Ntt + NttInit>, >( shares: &[CommonReferenceSeededMultiPartyServerKeyShare, S>], ) -> SeededMultiPartyServerKey> where ::R: RowMut + RowEntity, - M::MatElement: Copy + PartialEq + Zero, + M::MatElement: PrimInt + PartialEq + Zero, M: Clone, { assert!(shares.len() > 0); @@ -272,8 +324,8 @@ where let rlwe_n = parameters.rlwe_n().0; let g = parameters.g() as isize; - let rlwe_q = parameters.rlwe_q().0; - let lwe_q = parameters.lwe_q().0; + let rlwe_q = parameters.rlwe_q(); + let lwe_q = parameters.lwe_q(); // sanity checks shares.iter().skip(1).for_each(|s| { @@ -281,7 +333,7 @@ where assert!(s.cr_seed == cr_seed); }); - let rlweq_modop = ModOp::new(rlwe_q); + let rlweq_modop = ModOp::new(*rlwe_q); let rlweq_nttop = NttOp::new(rlwe_q, rlwe_n); // auto keys @@ -316,7 +368,8 @@ where rlwe_n, ); - let mut tmp_rgsw = RgswCiphertext::::empty(rlwe_n, &rgsw_by_rgsw_decomposer, rlwe_q).data; + let mut tmp_rgsw = + RgswCiphertext::::empty(rlwe_n, &rgsw_by_rgsw_decomposer, rlwe_q.clone()).data; let rgsw_cts = (0..lwe_n) .into_iter() .map(|index| { @@ -349,7 +402,7 @@ where // LWE ksks let mut lwe_ksk = M::R::zeros(rlwe_n * parameters.lwe_decomposition_count().0); - let lweq_modop = ModOp::new(lwe_q); + let lweq_modop = ModOp::new(*lwe_q); shares.iter().for_each(|si| { assert!(si.lwe_ksk.as_ref().len() == rlwe_n * parameters.lwe_decomposition_count().0); lweq_modop.elwise_add_mut(lwe_ksk.as_mut(), si.lwe_ksk.as_ref()) @@ -429,8 +482,8 @@ struct ServerKeyEvaluationDomain { impl< M: MatrixMut + MatrixEntity, - R: RandomUniformDist<[M::MatElement], Parameters = M::MatElement> + NewWithSeed, - N: NttInit + Ntt, + R: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus> + NewWithSeed, + N: NttInit> + Ntt, > From<&SeededServerKey, R::Seed>> for ServerKeyEvaluationDomain where @@ -444,8 +497,8 @@ where let g = parameters.g() as isize; let ring_size = value.parameters.rlwe_n().0; let lwe_n = value.parameters.lwe_n().0; - let rlwe_q = value.parameters.rlwe_q().0; - let lwq_q = value.parameters.lwe_q().0; + let rlwe_q = value.parameters.rlwe_q(); + let lwq_q = value.parameters.lwe_q(); let nttop = N::new(rlwe_q, ring_size); @@ -460,7 +513,7 @@ where // sample RLWE'_A(-s(X^k)) data.iter_rows_mut().take(auto_decomp_count).for_each(|ri| { - RandomUniformDist::random_fill(&mut main_prng, &rlwe_q, ri.as_mut()) + RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut()) }); // copy over RLWE'B_(-s(X^k)) @@ -502,7 +555,11 @@ where .skip(rlrg_a_decomp.0 * 2) .take(rlrg_b_decomp.0) .for_each(|ri| { - RandomUniformDist::random_fill(&mut main_prng, &rlwe_q, ri.as_mut()) + RandomFillUniformInModulus::random_fill( + &mut main_prng, + &rlwe_q, + ri.as_mut(), + ) }); // copy over RLWE'_B(m) @@ -528,7 +585,11 @@ where let mut data = M::zeros(d * ring_size, lwe_n + 1); izip!(data.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| { - RandomUniformDist::random_fill(&mut main_prng, &lwq_q, &mut lwe_i.as_mut()[1..]); + RandomFillUniformInModulus::random_fill( + &mut main_prng, + &lwq_q, + &mut lwe_i.as_mut()[1..], + ); lwe_i.as_mut()[0] = *bi; }); @@ -547,13 +608,13 @@ where impl< M: MatrixMut + MatrixEntity, Rng: NewWithSeed, - N: NttInit + Ntt, + N: NttInit> + Ntt, > From<&SeededMultiPartyServerKey>> for ServerKeyEvaluationDomain where ::R: RowMut, Rng::Seed: Copy, - Rng: RandomUniformDist<[M::MatElement], Parameters = M::MatElement>, + Rng: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus>, M::MatElement: Copy, { fn from( @@ -562,8 +623,8 @@ where let g = value.parameters.g() as isize; let rlwe_n = value.parameters.rlwe_n().0; let lwe_n = value.parameters.lwe_n().0; - let rlwe_q = value.parameters.rlwe_q().0; - let lwe_q = value.parameters.lwe_q().0; + let rlwe_q = value.parameters.rlwe_q(); + let lwe_q = value.parameters.lwe_q(); let mut main_prng = Rng::new_with_seed(value.cr_seed); @@ -577,7 +638,7 @@ where // sample a key.iter_rows_mut().take(auto_d_count).for_each(|ri| { - RandomUniformDist::random_fill(&mut main_prng, &rlwe_q, ri.as_mut()) + RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut()) }); let key_part_b = value.auto_keys.get(&i).unwrap(); @@ -677,7 +738,11 @@ where let d_lwe = value.parameters.lwe_decomposition_count().0; let mut lwe_ksk = M::zeros(rlwe_n * d_lwe, lwe_n + 1); izip!(lwe_ksk.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| { - RandomUniformDist::random_fill(&mut main_prng, &lwe_q, &mut lwe_i.as_mut()[1..]); + RandomFillUniformInModulus::random_fill( + &mut main_prng, + &lwe_q, + &mut lwe_i.as_mut()[1..], + ); lwe_i.as_mut()[0] = *bi; }); @@ -707,7 +772,7 @@ impl PbsKey for ServerKeyEvaluationDomain { } } -struct BoolPbsInfo { +struct BoolPbsInfo { auto_decomposer: DefaultDecomposer, rlwe_rgsw_decomposer: ( DefaultDecomposer, @@ -716,25 +781,27 @@ struct BoolPbsInfo { lwe_decomposer: DefaultDecomposer, g_k_dlog_map: Vec, rlwe_nttop: Ntt, - rlwe_modop: ModOp, - lwe_modop: ModOp, + rlwe_modop: RlweModOp, + lwe_modop: LweModOp, embedding_factor: usize, nand_test_vec: M::R, - rlweq_by8: M::MatElement, rlwe_qby4: M::MatElement, rlwe_auto_maps: Vec<(Vec, Vec)>, parameters: BoolParameters, } -impl PbsInfo for BoolPbsInfo +impl PbsInfo for BoolPbsInfo where M::MatElement: PrimInt + WrappingSub + NumInfo + Debug, - ModOp: ArithmeticOps + VectorOps, + RlweModOp: ArithmeticOps + VectorOps, + LweModOp: ArithmeticOps + VectorOps, NttOp: Ntt, { + type Modulus = CiphertextModulus; type Element = M::MatElement; type D = DefaultDecomposer; - type ModOp = ModOp; + type RlweModOp = RlweModOp; + type LweModOp = LweModOp; type NttOp = NttOp; fn rlwe_auto_map(&self, k: isize) -> &(Vec, Vec) { let g = self.parameters.g() as isize; @@ -747,7 +814,7 @@ where } } fn br_q(&self) -> usize { - self.parameters.br_q().0.to_usize().unwrap() + *self.parameters.br_q() } fn lwe_decomposer(&self) -> &Self::D { &self.lwe_decomposer @@ -770,19 +837,19 @@ where fn lwe_n(&self) -> usize { self.parameters.lwe_n().0 } - fn lwe_q(&self) -> Self::Element { - self.parameters.lwe_q().0 + fn lwe_q(&self) -> &Self::Modulus { + self.parameters.lwe_q() } fn rlwe_n(&self) -> usize { self.parameters.rlwe_n().0 } - fn rlwe_q(&self) -> Self::Element { - self.parameters.rlwe_q().0 + fn rlwe_q(&self) -> &Self::Modulus { + self.parameters.rlwe_q() } - fn modop_lweq(&self) -> &Self::ModOp { + fn modop_lweq(&self) -> &Self::LweModOp { &self.lwe_modop } - fn modop_rlweq(&self) -> &Self::ModOp { + fn modop_rlweq(&self) -> &Self::RlweModOp { &self.rlwe_modop } fn nttop_rlweq(&self) -> &Self::NttOp { @@ -790,37 +857,46 @@ where } } -struct BoolEvaluator +struct BoolEvaluator where M: Matrix, { - pbs_info: BoolPbsInfo, + pbs_info: BoolPbsInfo, scratch_memory: ScratchMemory, _phantom: PhantomData, } -impl BoolEvaluator +impl BoolEvaluator {} + +impl BoolEvaluator where - NttOp: NttInit + Ntt, - ModOp: ModInit - + ArithmeticOps - + VectorOps, - M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub, M: MatrixEntity + MatrixMut, - M::R: TryConvertFrom<[i32], Parameters = M::MatElement> + RowEntity + Debug, - M: TryConvertFrom<[i32], Parameters = M::MatElement>, + M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub, + NttOp: Ntt, + RlweModOp: ArithmeticOps + + VectorOps + + GetModulus>, + LweModOp: ArithmeticOps + + VectorOps + + GetModulus>, + M::R: TryConvertFrom1<[i32], CiphertextModulus> + RowEntity + Debug, ::R: RowMut, - DefaultSecureRng: RandomGaussianDist<[M::MatElement], Parameters = M::MatElement> - + RandomGaussianDist - + RandomUniformDist<[M::MatElement], Parameters = M::MatElement> + DefaultSecureRng: RandomFillGaussianInModulus<[M::MatElement], CiphertextModulus> + + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus> + + RandomGaussianElementInModulus> + NewWithSeed, { - fn new(parameters: BoolParameters) -> Self { + fn new(parameters: BoolParameters) -> Self + where + RlweModOp: ModInit>, + LweModOp: ModInit>, + NttOp: NttInit>, + { //TODO(Jay): Run sanity checks for modulus values in parameters // generatr dlog map s.t. g^{k} % q = a, for all a \in Z*_{q} let g = parameters.g(); - let q = parameters.br_q().0.to_usize().unwrap(); + let q = *parameters.br_q(); let mut g_k_dlog_map = vec![0usize; q]; for i in 0..q / 2 { let v = mod_exponent(g as u64, i as u64, q as u64) as usize; @@ -832,21 +908,19 @@ where let embedding_factor = (2 * parameters.rlwe_n().0) / q; - let rlwe_nttop = NttOp::new(parameters.rlwe_q().0, parameters.rlwe_n().0); - let rlwe_modop = ModInit::new(parameters.rlwe_q().0); - let lwe_modop = ModInit::new(parameters.lwe_q().0); + let rlwe_nttop = NttOp::new(parameters.rlwe_q(), parameters.rlwe_n().0); + let rlwe_modop = RlweModOp::new(*parameters.rlwe_q()); + let lwe_modop = LweModOp::new(*parameters.lwe_q()); // set test vectors - let rlwe_q = parameters.rlwe_q().0; - let q = parameters.br_q().0.to_usize().unwrap(); + let q = *parameters.br_q(); let qby2 = q >> 1; let qby8 = q >> 3; let mut nand_test_vec = M::R::zeros(qby2); // Q/8 (Q: rlwe_q) - let rlwe_qby8 = M::MatElement::from_f64((rlwe_q.to_f64().unwrap() / 8.0).round()).unwrap(); - let true_m_el = rlwe_qby8; + let true_m_el = parameters.rlwe_q().true_el(); // -Q/8 - let false_m_el = rlwe_q - rlwe_qby8; + let false_m_el = parameters.rlwe_q().false_el(); for i in 0..qby2 { if i < (3 * qby8) { nand_test_vec.as_mut()[i] = true_m_el; @@ -866,7 +940,7 @@ where .for_each(|(v, to_index, to_sign)| { if !to_sign { // negate - nand_test_vec_autog.as_mut()[*to_index] = parameters.rlwe_q().0 - *v; + nand_test_vec_autog.as_mut()[*to_index] = rlwe_modop.neg(v); } else { nand_test_vec_autog.as_mut()[*to_index] = *v; } @@ -880,7 +954,7 @@ where rlwe_auto_maps.push(generate_auto_map(ring_size, i)) } - let rlwe_qby4 = M::MatElement::from_f64((rlwe_q.to_f64().unwrap() / 4.0).round()).unwrap(); + let rlwe_qby4 = parameters.rlwe_q().qby4(); let scratch_memory = ScratchMemory::new(¶meters); @@ -894,8 +968,7 @@ where rlwe_modop, rlwe_nttop, nand_test_vec: nand_test_vec_autog, - rlweq_by8: rlwe_qby8, - rlwe_qby4: rlwe_qby4, + rlwe_qby4, rlwe_auto_maps, parameters: parameters, }; @@ -954,7 +1027,7 @@ where // generate rgsw ciphertexts RGSW(si) where si is i^th LWE secret element let ring_size = self.pbs_info.parameters.rlwe_n().0; - let rlwe_q = self.pbs_info.parameters.rlwe_q().0; + let rlwe_q = self.pbs_info.parameters.rlwe_q(); let (rlrg_d_a, rlrg_d_b) = ( self.pbs_info.rlwe_rgsw_decomposer.0.decomposition_count(), self.pbs_info.rlwe_rgsw_decomposer.1.decomposition_count(), @@ -971,7 +1044,7 @@ where // dbg!(si); if si < 0 { // X^{-i} = X^{2N - i} = -X^{N-i} - m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q - M::MatElement::one(); + m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one(); } else { // X^{i} m.as_mut()[si.abs() as usize] = M::MatElement::one(); @@ -1033,11 +1106,11 @@ where let g = self.pbs_info.parameters.g() as isize; let ring_size = self.pbs_info.parameters.rlwe_n().0; - let rlwe_q = self.pbs_info.parameters.rlwe_q().0; - let lwe_q = self.pbs_info.parameters.lwe_q().0; + let rlwe_q = self.pbs_info.parameters.rlwe_q(); + let lwe_q = self.pbs_info.parameters.lwe_q(); - let rlweq_modop = ModOp::new(rlwe_q); - let rlweq_nttop = NttOp::new(rlwe_q, ring_size); + let rlweq_modop = &self.pbs_info.rlwe_modop; + let rlweq_nttop = &self.pbs_info.rlwe_nttop; // sanity check assert!(sk_rlwe.values().len() == ring_size); @@ -1056,8 +1129,8 @@ where sk_rlwe.values(), i, &auto_gadget, - &rlweq_modop, - &rlweq_nttop, + rlweq_modop, + rlweq_nttop, &mut main_prng, rng, ); @@ -1089,7 +1162,7 @@ where if si < 0 { // X^{-si} = X^{2N-si} = -X^{N-si}, assuming abs(si) < N // (which it is given si is secret element) - m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q - M::MatElement::one(); + m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one(); } else { m.as_mut()[si as usize] = M::MatElement::one(); } @@ -1103,8 +1176,8 @@ where collective_pk, &rgrg_gadget_a, &rgrg_gadget_b, - &rlweq_modop, - &rlweq_nttop, + rlweq_modop, + rlweq_nttop, rng, ); @@ -1115,14 +1188,14 @@ where // LWE ksk let mut lwe_ksk = M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size); - let lwe_modop = ModOp::new(lwe_q); + let lwe_modop = &self.pbs_info.lwe_modop; let d_lwe_gadget_vec = self.pbs_info.lwe_decomposer.gadget_vector(); lwe_ksk_keygen( sk_rlwe.values(), sk_lwe.values(), &mut lwe_ksk, &d_lwe_gadget_vec, - &lwe_modop, + lwe_modop, &mut main_prng, rng, ); @@ -1148,17 +1221,14 @@ where > { DefaultSecureRng::with_local_mut(|rng| { let mut share_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0); - let modop = ModOp::new(self.pbs_info.parameters.rlwe_q().0); - let nttop = NttOp::new( - self.pbs_info.parameters.rlwe_q().0, - self.pbs_info.parameters.rlwe_n().0, - ); + let modop = &self.pbs_info.rlwe_modop; + let nttop = &self.pbs_info.rlwe_nttop; let mut main_prng = DefaultSecureRng::new_seeded(cr_seed); public_key_share( &mut share_out, client_key.sk_rlwe.values(), - &modop, - &nttop, + modop, + nttop, &mut main_prng, rng, ); @@ -1180,7 +1250,7 @@ where let modop = &self.pbs_info.rlwe_modop; let mut neg_s = M::R::try_convert_from( client_key.sk_rlwe.values(), - &self.pbs_info.parameters.rlwe_q().0, + &self.pbs_info.parameters.rlwe_q(), ); modop.elwise_neg_mut(neg_s.as_mut()); @@ -1190,8 +1260,8 @@ where }); let e = DefaultSecureRng::with_local_mut(|rng| { - let mut e = M::MatElement::zero(); - RandomGaussianDist::random_fill(rng, &self.pbs_info.parameters.rlwe_q().0, &mut e); + let mut e = + RandomGaussianElementInModulus::random(rng, self.pbs_info.parameters.rlwe_q()); e }); let share = modop.add(&neg_sa, &e); @@ -1211,19 +1281,7 @@ where .for_each(|share_i| sum_a = modop.add(&sum_a, &share_i.share)); let encoded_m = modop.add(&lwe_ct.as_ref()[0], &sum_a); - - let m = (((encoded_m + self.pbs_info.rlweq_by8).to_f64().unwrap() * 4f64) - / self.pbs_info.parameters.rlwe_q().0.to_f64().unwrap()) - .round() as usize - % 4usize; - - if m == 0 { - return false; - } else if m == 1 { - return true; - } else { - panic!("Bool decryption failure. Expected m to be either 1 or 0, but m={m} "); - } + self.pbs_info.parameters.rlwe_q().decode(encoded_m) } /// First encrypt as RLWE(m) with m as constant polynomial and extract it as @@ -1238,7 +1296,7 @@ where let ring_size = self.pbs_info.parameters.rlwe_n().0; let mut u = vec![0i32; ring_size]; fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng); - let mut u = M::R::try_convert_from(&u, &self.pbs_info.parameters.rlwe_q().0); + let mut u = M::R::try_convert_from(&u, &self.pbs_info.parameters.rlwe_q()); nttop.forward(u.as_mut()); let mut ua = M::R::zeros(ring_size); @@ -1259,9 +1317,9 @@ where let mut rlwe = M::zeros(2, ring_size); // sample error rlwe.iter_rows_mut().for_each(|ri| { - RandomGaussianDist::random_fill( + RandomFillGaussianInModulus::random_fill( rng, - &self.pbs_info.parameters.rlwe_q().0, + &self.pbs_info.parameters.rlwe_q(), ri.as_mut(), ); }); @@ -1271,12 +1329,13 @@ where // b*u + e1 modop.elwise_add_mut(rlwe.get_row_mut(1), ub.as_ref()); + //FIXME(Jay): Figure out a way to get Q/8 form modulus let m = if m { // Q/8 - self.pbs_info.rlweq_by8 + self.pbs_info.rlwe_q().true_el() } else { // -Q/8 - self.pbs_info.parameters.rlwe_q().0 - self.pbs_info.rlweq_by8 + self.pbs_info.rlwe_q().false_el() }; // b*u + e1 + m, where m is constant polynomial @@ -1292,12 +1351,13 @@ where /// TODO(Jay): Fetch client key from thread local pub fn sk_encrypt(&self, m: bool, client_key: &ClientKey) -> M::R { + //FIXME(Jay): Figure out a way to get Q/8 form modulus let m = if m { // Q/8 - self.pbs_info.rlweq_by8 + self.pbs_info.rlwe_q().true_el() } else { // -Q/8 - self.pbs_info.parameters.rlwe_q().0 - self.pbs_info.rlweq_by8 + self.pbs_info.rlwe_q().false_el() }; DefaultSecureRng::with_local_mut(|rng| { @@ -1319,23 +1379,7 @@ where client_key.sk_rlwe.values(), &self.pbs_info.rlwe_modop, ); - let m = { - // m + q/8 => {0,q/4 1} - (((m + self.pbs_info.rlweq_by8).to_f64().unwrap() * 4.0) - / self.pbs_info.parameters.rlwe_q().0.to_f64().unwrap()) - .round() - .to_usize() - .unwrap() - % 4 - }; - - if m == 0 { - false - } else if m == 1 { - true - } else { - panic!("Incorrect bool decryption. Got m={m} but expected m to be 0 or 1") - } + self.pbs_info.rlwe_q().decode(m) } // TODO(Jay): scratch spaces must be thread local. Don't pass them as arguments @@ -1515,19 +1559,19 @@ fn pbs< let rlwe_q = pbs_info.rlwe_q(); let lwe_q = pbs_info.lwe_q(); let br_q = pbs_info.br_q(); - let rlwe_qf64 = rlwe_q.to_f64().unwrap(); - let lwe_qf64 = lwe_q.to_f64().unwrap(); + let rlwe_qf64 = rlwe_q.q().unwrap().to_f64().unwrap(); + let lwe_qf64 = lwe_q.q().unwrap().to_f64().unwrap(); let br_qf64 = br_q.to_f64().unwrap(); let rlwe_n = pbs_info.rlwe_n(); - PBSTracer::with_local_mut(|t| { - let out = lwe_in - .as_ref() - .iter() - .map(|v| v.to_u64().unwrap()) - .collect_vec(); - t.ct_rlwe_q_mod = out; - }); + // PBSTracer::with_local_mut(|t| { + // let out = lwe_in + // .as_ref() + // .iter() + // .map(|v| v.to_u64().unwrap()) + // .collect_vec(); + // t.ct_rlwe_q_mod = out; + // }); // moddown Q -> Q_ks lwe_in.as_mut().iter_mut().for_each(|v| { @@ -1535,14 +1579,14 @@ fn pbs< M::MatElement::from_f64(((v.to_f64().unwrap() * lwe_qf64) / rlwe_qf64).round()).unwrap() }); - PBSTracer::with_local_mut(|t| { - let out = lwe_in - .as_ref() - .iter() - .map(|v| v.to_u64().unwrap()) - .collect_vec(); - t.ct_lwe_q_mod = out; - }); + // PBSTracer::with_local_mut(|t| { + // let out = lwe_in + // .as_ref() + // .iter() + // .map(|v| v.to_u64().unwrap()) + // .collect_vec(); + // t.ct_lwe_q_mod = out; + // }); // key switch RLWE secret to LWE secret scratch_lwe_vec.as_mut().fill(M::MatElement::zero()); @@ -1554,14 +1598,14 @@ fn pbs< pbs_info.lwe_decomposer(), ); - PBSTracer::with_local_mut(|t| { - let out = scratch_lwe_vec - .as_ref() - .iter() - .map(|v| v.to_u64().unwrap()) - .collect_vec(); - t.ct_lwe_q_mod_after_ksk = out; - }); + // PBSTracer::with_local_mut(|t| { + // let out = scratch_lwe_vec + // .as_ref() + // .iter() + // .map(|v| v.to_u64().unwrap()) + // .collect_vec(); + // t.ct_lwe_q_mod_after_ksk = out; + // }); // odd mowdown Q_ks -> q let g_k_dlog_map = pbs_info.g_k_dlog_map(); @@ -1577,14 +1621,14 @@ fn pbs< g_k_si[k].push(index); }); - PBSTracer::with_local_mut(|t| { - let out = scratch_lwe_vec - .as_ref() - .iter() - .map(|v| mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64) as u64) - .collect_vec(); - t.ct_br_q_mod = out; - }); + // PBSTracer::with_local_mut(|t| { + // let out = scratch_lwe_vec + // .as_ref() + // .iter() + // .map(|v| mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64) as + // u64) .collect_vec(); + // t.ct_br_q_mod = out; + // }); // handle b and set trivial test RLWE let g = pbs_info.g() as usize; @@ -1750,1059 +1794,65 @@ fn monomial_mul>( }); } -thread_local! { - static PBS_TRACER: RefCell>>> = RefCell::new(PBSTracer::default()); -} - -#[derive(Default)] -struct PBSTracer -where - M: Matrix + Default, -{ - pub(crate) ct_rlwe_q_mod: M::R, - pub(crate) ct_lwe_q_mod: M::R, - pub(crate) ct_lwe_q_mod_after_ksk: M::R, - pub(crate) ct_br_q_mod: Vec, -} - -impl PBSTracer>> { - fn trace(&self, parameters: &BoolParameters, sk_lwe: &[i32], sk_rlwe: &[i32]) { - assert!(parameters.rlwe_n().0 == sk_rlwe.len()); - assert!(parameters.lwe_n().0 == sk_lwe.len()); - - let modop_rlweq = ModularOpsU64::new(parameters.rlwe_q().0); - // noise after mod down Q -> Q_ks - let m_back0 = decrypt_lwe(&self.ct_rlwe_q_mod, sk_rlwe, &modop_rlweq); - - let modop_lweq = ModularOpsU64::new(parameters.lwe_q().0); - // noise after mod down Q -> Q_ks - let m_back1 = decrypt_lwe(&self.ct_lwe_q_mod, sk_rlwe, &modop_lweq); - // noise after key switch from RLWE -> LWE - let m_back2 = decrypt_lwe(&self.ct_lwe_q_mod_after_ksk, sk_lwe, &modop_lweq); - - // noise after mod down odd from Q_ks -> q - let modop_br_q = ModularOpsU64::new(parameters.br_q().0); - let m_back3 = decrypt_lwe(&self.ct_br_q_mod, sk_lwe, &modop_br_q); - - println!( - " - M initial mod Q: {m_back0}, - M after mod down Q -> Q_ks: {m_back1}, - M after key switch from RLWE -> LWE: {m_back2}, - M after mod dwon Q_ks -> q: {m_back3} - " - ); - } -} - -impl WithLocal for PBSTracer>> { - fn with_local(func: F) -> R - where - F: Fn(&Self) -> R, - { - PBS_TRACER.with_borrow(|t| func(t)) - } - - fn with_local_mut(func: F) -> R - where - F: Fn(&mut Self) -> R, - { - PBS_TRACER.with_borrow_mut(|t| func(t)) - } -} - -#[cfg(test)] -mod tests { - use std::iter::Sum; - - use rand::{thread_rng, Rng}; - use rand_distr::Uniform; - - use crate::{ - backend::ModularOpsU64, - bool, - ntt::NttBackendU64, - random::DEFAULT_RNG, - rgsw::{ - self, measure_noise, public_key_encrypt_rlwe, secret_key_encrypt_rlwe, - tests::{_measure_noise_rgsw, _sk_encrypt_rlwe}, - RgswCiphertext, RgswCiphertextEvaluationDomain, SeededRgswCiphertext, - SeededRlweCiphertext, - }, - utils::{negacyclic_mul, Stats}, - }; - - use super::*; - - #[test] - fn bool_encrypt_decrypt_works() { - let bool_evaluator = - BoolEvaluator::>, NttBackendU64, ModularOpsU64>::new(SP_BOOL_PARAMS); - let client_key = bool_evaluator.client_key(); - - let mut m = true; - for _ in 0..1000 { - let lwe_ct = bool_evaluator.sk_encrypt(m, &client_key); - let m_back = bool_evaluator.sk_decrypt(&lwe_ct, &client_key); - assert_eq!(m, m_back); - m = !m; - } - } - - #[test] - fn bool_nand() { - DefaultSecureRng::with_local_mut(|r| { - let rng = DefaultSecureRng::new_seeded([19u8; 32]); - *r = rng; - }); - - let mut bool_evaluator = - BoolEvaluator::>, NttBackendU64, ModularOpsU64>::new(SP_BOOL_PARAMS); - - // println!("{:?}", bool_evaluator.nand_test_vec); - let client_key = bool_evaluator.client_key(); - let seeded_server_key = bool_evaluator.server_key(&client_key); - let server_key_eval_domain = - ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from( - &seeded_server_key, - ); - - let mut m0 = false; - let mut m1 = true; - let mut ct0 = bool_evaluator.sk_encrypt(m0, &client_key); - let mut ct1 = bool_evaluator.sk_encrypt(m1, &client_key); - - for _ in 0..1000 { - let ct_back = bool_evaluator.nand(&ct0, &ct1, &server_key_eval_domain); - - let m_out = !(m0 && m1); - - // Trace and measure PBS noise - { - let noise0 = { - let ideal = if m0 { - bool_evaluator.pbs_info.rlweq_by8 - } else { - bool_evaluator.pbs_info.rlwe_q() - bool_evaluator.pbs_info.rlweq_by8 - }; - let n = measure_noise_lwe( - &ct0, - client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - &ideal, - ); - let v = decrypt_lwe( - &ct0, - client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - ); - (n, v) - }; - let noise1 = { - let ideal = if m1 { - bool_evaluator.pbs_info.rlweq_by8 - } else { - bool_evaluator.pbs_info.rlwe_q() - bool_evaluator.pbs_info.rlweq_by8 - }; - let n = measure_noise_lwe( - &ct1, - client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - &ideal, - ); - let v = decrypt_lwe( - &ct1, - client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - ); - (n, v) - }; - - // Trace PBS - PBSTracer::with_local(|t| { - t.trace( - &SP_BOOL_PARAMS, - &client_key.sk_lwe.values(), - client_key.sk_rlwe.values(), - ) - }); - - // Calculate noise in ciphertext post PBS - let noise_out = { - let ideal = if m_out { - bool_evaluator.pbs_info.rlweq_by8 - } else { - bool_evaluator.pbs_info.rlwe_q() - bool_evaluator.pbs_info.rlweq_by8 - }; - let n = measure_noise_lwe( - &ct_back, - client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - &ideal, - ); - let v = decrypt_lwe( - &ct_back, - client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - ); - (n, v) - }; - dbg!(m0, m1, m_out); - println!( - "ct0 (noise, message): {:?} \n ct1 (noise, message): {:?} \n PBS (noise, message): {:?}", noise0, noise1, noise_out - ); - } - let m_back = bool_evaluator.sk_decrypt(&ct_back, &client_key); - assert!(m_out == m_back, "Expected {m_out}, got {m_back}"); - println!("----------"); - - m1 = m0; - m0 = m_out; - - ct1 = ct0; - ct0 = ct_back; - } - } - - #[test] - fn multi_party_encryption_decryption() { - let bool_evaluator = - BoolEvaluator::>, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); - - let no_of_parties = 500; - let parties = (0..no_of_parties) - .map(|_| bool_evaluator.client_key()) - .collect_vec(); - - let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()]; - parties.iter().for_each(|k| { - izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| { - *ideal_i = *ideal_i + s_i; - }); - }); - - println!("{:?}", &ideal_rlwe_sk); - - let mut m = true; - for i in 0..100 { - let pk_cr_seed = [0u8; 32]; - - let public_key_share = parties - .iter() - .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k)) - .collect_vec(); - - let collective_pk = PublicKey::>, DefaultSecureRng, ModularOpsU64>::from( - public_key_share.as_slice(), - ); - let lwe_ct = bool_evaluator.pk_encrypt(&collective_pk.key, m); - - let decryption_shares = parties - .iter() - .map(|k| bool_evaluator.multi_party_decryption_share(&lwe_ct, k)) - .collect_vec(); - - let m_back = bool_evaluator.multi_party_decrypt(&decryption_shares, &lwe_ct); - - { - let ideal_m = if m { - bool_evaluator.pbs_info.rlweq_by8 - } else { - bool_evaluator.pbs_info.rlwe_q() - bool_evaluator.pbs_info.rlweq_by8 - }; - let noise = measure_noise_lwe( - &lwe_ct, - &ideal_rlwe_sk, - &bool_evaluator.pbs_info.rlwe_modop, - &ideal_m, - ); - println!("Noise: {noise}"); - } - - assert_eq!(m_back, m); - m = !m; - } - } - - fn _collecitve_public_key_gen(rlwe_q: u64, parties_rlwe_sk: &[RlweSecret]) -> Vec> { - let ring_size = parties_rlwe_sk[0].values.len(); - assert!(ring_size.is_power_of_two()); - let mut rng = DefaultSecureRng::new(); - let nttop = NttBackendU64::new(rlwe_q, ring_size); - let modop = ModularOpsU64::new(rlwe_q); - - // Generate Pk shares - let pk_seed = [0u8; 32]; - let pk_shares = parties_rlwe_sk.iter().map(|sk| { - let mut p_rng = DefaultSecureRng::new_seeded(pk_seed); - let mut share_out = vec![0u64; ring_size]; - public_key_share( - &mut share_out, - sk.values(), - &modop, - &nttop, - &mut p_rng, - &mut rng, - ); - share_out - }); - - let mut pk_part_b = vec![0u64; ring_size]; - pk_shares.for_each(|share| modop.elwise_add_mut(&mut pk_part_b, &share)); - let mut pk_part_a = vec![0u64; ring_size]; - let mut p_rng = DefaultSecureRng::new_seeded(pk_seed); - RandomUniformDist::random_fill(&mut p_rng, &rlwe_q, pk_part_a.as_mut_slice()); - - vec![pk_part_a, pk_part_b] - } - - fn _multi_party_all_keygen( - bool_evaluator: &BoolEvaluator>, NttBackendU64, ModularOpsU64>, - no_of_parties: usize, - ) -> ( - Vec, - PublicKey>, DefaultSecureRng, ModularOpsU64>, - Vec< - CommonReferenceSeededMultiPartyServerKeyShare< - Vec>, - BoolParameters, - [u8; 32], - >, - >, - SeededMultiPartyServerKey>, [u8; 32], BoolParameters>, - ServerKeyEvaluationDomain>, DefaultSecureRng, NttBackendU64>, - ClientKey, - ) { - let parties = (0..no_of_parties) - .map(|_| bool_evaluator.client_key()) - .collect_vec(); - - let mut rng = DefaultSecureRng::new(); - - // Collective public key - let mut pk_cr_seed = [0u8; 32]; - rng.fill_bytes(&mut pk_cr_seed); - let public_key_share = parties - .iter() - .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k)) - .collect_vec(); - let collective_pk = PublicKey::>, DefaultSecureRng, ModularOpsU64>::from( - public_key_share.as_slice(), - ); - - // Server key - let mut pbs_cr_seed = [1u8; 32]; - rng.fill_bytes(&mut pbs_cr_seed); - let server_key_shares = parties - .iter() - .map(|k| { - bool_evaluator.multi_party_server_key_share(pbs_cr_seed, &collective_pk.key, k) - }) - .collect_vec(); - let seeded_server_key = aggregate_multi_party_server_key_shares::< - _, - _, - (DefaultDecomposer, DefaultDecomposer), - ModularOpsU64, - NttBackendU64, - >(&server_key_shares); - let server_key_eval = ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from( - &seeded_server_key, - ); - - // construct ideal rlwe sk for meauring noise - let ideal_client_key = { - let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()]; - parties.iter().for_each(|k| { - izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| { - *ideal_i = *ideal_i + s_i; - }); - }); - let mut ideal_lwe_sk = vec![0i32; bool_evaluator.pbs_info.lwe_n()]; - parties.iter().for_each(|k| { - izip!(ideal_lwe_sk.iter_mut(), k.sk_lwe.values()).for_each(|(ideal_i, s_i)| { - *ideal_i = *ideal_i + s_i; - }); - }); - - ClientKey { - sk_lwe: LweSecret { - values: ideal_lwe_sk, - }, - sk_rlwe: RlweSecret { - values: ideal_rlwe_sk, - }, - } - }; - - ( - parties, - collective_pk, - server_key_shares, - seeded_server_key, - server_key_eval, - ideal_client_key, - ) - } - - #[test] - fn multi_party_nand() { - let mut bool_evaluator = - BoolEvaluator::>, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); - - let (parties, collective_pk, _, _, server_key_eval, ideal_client_key) = - _multi_party_all_keygen(&bool_evaluator, 2); - - let mut m0 = true; - let mut m1 = false; - - let mut lwe0 = bool_evaluator.pk_encrypt(&collective_pk.key, m0); - let mut lwe1 = bool_evaluator.pk_encrypt(&collective_pk.key, m1); - - for _ in 0..2000 { - let lwe_out = bool_evaluator.nand(&lwe0, &lwe1, &server_key_eval); - - let m_expected = !(m0 & m1); - - // measure noise - { - let noise0 = { - let ideal = if m0 { - bool_evaluator.pbs_info.rlweq_by8 - } else { - bool_evaluator.pbs_info.rlwe_q() - bool_evaluator.pbs_info.rlweq_by8 - }; - let n = measure_noise_lwe( - &lwe0, - ideal_client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - &ideal, - ); - let v = decrypt_lwe( - &lwe0, - ideal_client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - ); - (n, v) - }; - let noise1 = { - let ideal = if m1 { - bool_evaluator.pbs_info.rlweq_by8 - } else { - bool_evaluator.pbs_info.rlwe_q() - bool_evaluator.pbs_info.rlweq_by8 - }; - let n = measure_noise_lwe( - &lwe1, - ideal_client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - &ideal, - ); - let v = decrypt_lwe( - &lwe1, - ideal_client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - ); - (n, v) - }; - - // Trace PBS - PBSTracer::with_local(|t| { - t.trace( - &MP_BOOL_PARAMS, - &ideal_client_key.sk_lwe.values(), - &ideal_client_key.sk_rlwe.values(), - ) - }); - - let noise_out = { - let ideal_m = if m_expected { - bool_evaluator.pbs_info.rlweq_by8 - } else { - bool_evaluator.pbs_info.parameters.rlwe_q().0 - - bool_evaluator.pbs_info.rlweq_by8 - }; - let n = measure_noise_lwe( - &lwe_out, - ideal_client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - &ideal_m, - ); - let v = decrypt_lwe( - &lwe_out, - ideal_client_key.sk_rlwe.values(), - &bool_evaluator.pbs_info.rlwe_modop, - ); - (n, v) - }; - dbg!(m0, m1, m_expected); - println!( - "ct0 (noise, message): {:?} \n ct1 (noise, message): {:?} \n PBS (noise, message): {:?}", noise0, noise1, noise_out - ); - } - - // multi-party decrypt - let decryption_shares = parties - .iter() - .map(|k| bool_evaluator.multi_party_decryption_share(&lwe_out, k)) - .collect_vec(); - let m_back = bool_evaluator.multi_party_decrypt(&decryption_shares, &lwe_out); - - // let m_back = bool_evaluator.sk_decrypt(&lwe_out, &ideal_client_key); - - assert!(m_expected == m_back, "Expected {m_expected}, got {m_back}"); - m1 = m0; - m0 = m_expected; - - lwe1 = lwe0; - lwe0 = lwe_out; - } - } - - #[test] - fn tester() { - // pub(super) const TEST_MP_BOOL_PARAMS: BoolParameters = - // BoolParameters:: { rlwe_q: 1152921504606830593, - // rlwe_logq: 60, - // lwe_q: 1 << 20, - // lwe_logq: 20, - // br_q: 1 << 11, - // rlwe_n: 1 << 11, - // lwe_n: 500, - // d_rgsw: 4, - // logb_rgsw: 12, - // d_lwe: 5, - // logb_lwe: 4, - // g: 5, - // w: 1, - // }; - - let bool_evaluator = - BoolEvaluator::>, NttBackendU64, ModularOpsU64>::new(MP_BOOL_PARAMS); - - // let (_, collective_pk, _, _, server_key_eval, ideal_client_key) = - // _multi_party_all_keygen(&bool_evaluator, 20); - let no_of_parties = 2; - let lwe_q = bool_evaluator.pbs_info.parameters.lwe_q().0; - let rlwe_q = bool_evaluator.pbs_info.parameters.rlwe_q().0; - let lwe_n = bool_evaluator.pbs_info.parameters.lwe_n().0; - let rlwe_n = bool_evaluator.pbs_info.parameters.rlwe_n().0; - let lwe_modop = &bool_evaluator.pbs_info.lwe_modop; - let rlwe_nttop = &bool_evaluator.pbs_info.rlwe_nttop; - let rlwe_modop = &bool_evaluator.pbs_info.rlwe_modop; - - let rlwe_rgsw_decomposer = &bool_evaluator.pbs_info.rlwe_rgsw_decomposer; - let rlwe_rgsw_gadget_a = rlwe_rgsw_decomposer.0.gadget_vector(); - let rlwe_rgsw_gadget_b = rlwe_rgsw_decomposer.1.gadget_vector(); - - // let rgsw_rgsw_decomposer = &bool_evaluator - // .pbs_info - // .parameters - // .rgsw_rgsw_decomposer::>(); - // let rgsw_rgsw_gagdet_a = rgsw_rgsw_decomposer.a().gadget_vector(); - // let rgsw_rgsw_gagdet_b = rgsw_rgsw_decomposer.b().gadget_vector(); - - let parties = (0..no_of_parties) - .map(|_| bool_evaluator.client_key()) - .collect_vec(); - - let ideal_client_key = { - let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()]; - parties.iter().for_each(|k| { - izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| { - *ideal_i = *ideal_i + s_i; - }); - }); - let mut ideal_lwe_sk = vec![0i32; bool_evaluator.pbs_info.lwe_n()]; - parties.iter().for_each(|k| { - izip!(ideal_lwe_sk.iter_mut(), k.sk_lwe.values()).for_each(|(ideal_i, s_i)| { - *ideal_i = *ideal_i + s_i; - }); - }); - - ClientKey { - sk_lwe: LweSecret { - values: ideal_lwe_sk, - }, - sk_rlwe: RlweSecret { - values: ideal_rlwe_sk, - }, - } - }; - - // check noise in freshly encrypted RLWE ciphertext (ie var_fresh) - if false { - let mut rng = DefaultSecureRng::new(); - let mut check = Stats { samples: vec![] }; - for _ in 0..10 { - // generate a new collective public key - let mut pk_cr_seed = [0u8; 32]; - rng.fill_bytes(&mut pk_cr_seed); - let public_key_share = parties - .iter() - .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k)) - .collect_vec(); - let collective_pk = - PublicKey::>, DefaultSecureRng, ModularOpsU64>::from( - public_key_share.as_slice(), - ); - - let mut m = vec![0u64; rlwe_n]; - RandomUniformDist::random_fill(&mut rng, &rlwe_q, m.as_mut_slice()); - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - public_key_encrypt_rlwe( - &mut rlwe_ct, - &collective_pk.key, - &m, - rlwe_modop, - rlwe_nttop, - &mut rng, - ); - - let mut m_back = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_ct, - ideal_client_key.sk_rlwe.values(), - &mut m_back, - rlwe_nttop, - rlwe_modop, - ); - - rlwe_modop.elwise_sub_mut(m_back.as_mut_slice(), m.as_slice()); - - check.add_more(Vec::::try_convert_from(&m_back, &rlwe_q).as_slice()); - } - - println!("Public key Std: {}", check.std_dev().abs().log2()); - } - - if true { - // Generate server key shares - let mut rng = DefaultSecureRng::new(); - let mut pk_cr_seed = [0u8; 32]; - rng.fill_bytes(&mut pk_cr_seed); - let public_key_share = parties - .iter() - .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k)) - .collect_vec(); - let collective_pk = PublicKey::>, DefaultSecureRng, ModularOpsU64>::from( - public_key_share.as_slice(), - ); - let pbs_cr_seed = [0u8; 32]; - rng.fill_bytes(&mut pk_cr_seed); - let server_key_shares = parties - .iter() - .map(|k| { - bool_evaluator.multi_party_server_key_share(pbs_cr_seed, &collective_pk.key, k) - }) - .collect_vec(); - - let seeded_server_key = aggregate_multi_party_server_key_shares::< - _, - _, - (DefaultDecomposer, DefaultDecomposer), - ModularOpsU64, - NttBackendU64, - >(&server_key_shares); - - // Check noise in RGSW ciphertexts of ideal LWE secret elements - if true { - let mut check = Stats { samples: vec![] }; - izip!( - ideal_client_key.sk_lwe.values.iter(), - seeded_server_key.rgsw_cts.iter() - ) - .for_each(|(s_i, rgsw_ct_i)| { - // X^{s[i]} - let mut m_si = vec![0u64; rlwe_n]; - let s_i = *s_i * (bool_evaluator.pbs_info.embedding_factor as i32); - if s_i < 0 { - m_si[rlwe_n - (s_i.abs() as usize)] = rlwe_q - 1; - } else { - m_si[s_i as usize] = 1; - } - - // RLWE(-sm) - let mut neg_s_eval = - Vec::::try_convert_from(ideal_client_key.sk_rlwe.values(), &rlwe_q); - rlwe_modop.elwise_neg_mut(&mut neg_s_eval); - rlwe_nttop.forward(&mut neg_s_eval); - for j in 0..rlwe_rgsw_decomposer.a().decomposition_count() { - // -s[X]*X^{s_lwe[i]}*B_j - let mut m_ideal = m_si.clone(); - rlwe_nttop.forward(m_ideal.as_mut_slice()); - rlwe_modop.elwise_mul_mut(m_ideal.as_mut_slice(), neg_s_eval.as_slice()); - rlwe_nttop.backward(m_ideal.as_mut_slice()); - rlwe_modop - .elwise_scalar_mul_mut(m_ideal.as_mut_slice(), &rlwe_rgsw_gadget_a[j]); - - // RLWE(-s*X^{s_lwe[i]}*B_j) - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - rlwe_ct[0].copy_from_slice(&rgsw_ct_i[j]); - rlwe_ct[1].copy_from_slice( - &rgsw_ct_i[j + rlwe_rgsw_decomposer.a().decomposition_count()], - ); - - let mut m_back = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_ct, - ideal_client_key.sk_rlwe.values(), - &mut m_back, - rlwe_nttop, - rlwe_modop, - ); - - // diff - rlwe_modop.elwise_sub_mut(&mut m_back, &m_ideal); - check.add_more(&Vec::::try_convert_from(&m_back, &rlwe_q)); - } - - // RLWE'(m) - for j in 0..rlwe_rgsw_decomposer.b().decomposition_count() { - // X^{s_lwe[i]}*B_j - let mut m_ideal = m_si.clone(); - rlwe_modop - .elwise_scalar_mul_mut(m_ideal.as_mut_slice(), &rlwe_rgsw_gadget_b[j]); - - // RLWE(X^{s_lwe[i]}*B_j) - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - rlwe_ct[0].copy_from_slice( - &rgsw_ct_i[j + (2 * rlwe_rgsw_decomposer.a().decomposition_count())], - ); - rlwe_ct[1].copy_from_slice( - &rgsw_ct_i[j - + (2 * rlwe_rgsw_decomposer.a().decomposition_count() - + rlwe_rgsw_decomposer.b().decomposition_count())], - ); - - let mut m_back = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_ct, - ideal_client_key.sk_rlwe.values(), - &mut m_back, - rlwe_nttop, - rlwe_modop, - ); - - // diff - rlwe_modop.elwise_sub_mut(&mut m_back, &m_ideal); - check.add_more(&Vec::::try_convert_from(&m_back, &rlwe_q)); - } - }); - println!( - "RGSW Std: {} {} ;; max={}", - check.mean(), - check.std_dev().abs().log2(), - check.samples.iter().max().unwrap() - ); - } - - // check noise in RLWE x RGSW(X^{s_i}) where RGSW is accunulated RGSW ciphertext - if false { - let mut check = Stats { samples: vec![] }; - // server key in Evaluation domain - let server_key_eval_domain = - ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from( - &seeded_server_key, - ); - izip!( - ideal_client_key.sk_lwe.values(), - seeded_server_key.rgsw_cts.iter() - ) - .for_each(|(s_i, rgsw_ct_i)| { - let mut rgsw_ct_i = rgsw_ct_i.clone(); - rgsw_ct_i - .iter_mut() - .for_each(|ri| rlwe_nttop.forward(ri.as_mut())); - - let mut m = vec![0u64; rlwe_n]; - RandomUniformDist::random_fill(&mut rng, &rlwe_q, m.as_mut_slice()); - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - public_key_encrypt_rlwe( - &mut rlwe_ct, - &collective_pk.key, - &m, - rlwe_modop, - rlwe_nttop, - &mut rng, - ); - - // RLWE(m*X^{s[i]}) = RLWE(m) x RGSW(X^{s[i]}) - let mut rlwe_after = RlweCiphertext::<_, DefaultSecureRng>::new_trivial(vec![ - vec![0u64; rlwe_n], - m.clone(), - ]); - // let mut rlwe_after = - // RlweCiphertext::<_, DefaultSecureRng>::from_raw(rlwe_ct.clone(), false); - let mut scratch = vec![ - vec![0u64; rlwe_n]; - std::cmp::max( - rlwe_rgsw_decomposer.0.decomposition_count(), - rlwe_rgsw_decomposer.1.decomposition_count() - ) + 2 - ]; - rlwe_by_rgsw( - &mut rlwe_after, - &rgsw_ct_i, - &mut scratch, - rlwe_rgsw_decomposer, - rlwe_nttop, - rlwe_modop, - ); - - // m1 = X^{s[i]} - let mut m1 = vec![0u64; rlwe_n]; - let s_i = *s_i * (bool_evaluator.pbs_info.embedding_factor as i32); - if s_i < 0 { - m1[rlwe_n - (s_i.abs() as usize)] = rlwe_q - 1; - } else { - m1[s_i as usize] = 1; - } - - // (m+e) * m1 - let mut m_plus_e_times_m1 = m.clone(); - // decrypt_rlwe( - // &rlwe_ct, - // ideal_client_key.sk_rlwe.values(), - // &mut m_plus_e_times_m1, - // rlwe_nttop, - // rlwe_modop, - // ); - rlwe_nttop.forward(m_plus_e_times_m1.as_mut_slice()); - rlwe_nttop.forward(m1.as_mut_slice()); - rlwe_modop.elwise_mul_mut(m_plus_e_times_m1.as_mut_slice(), m1.as_slice()); - rlwe_nttop.backward(m_plus_e_times_m1.as_mut_slice()); - - // Resulting RLWE ciphertext will equal: (m0m1 + em1) + e_{rlsw x rgsw}. - // Hence, resulting rlwe ciphertext will have error em1 + e_{rlwe x rgsw}. - // Here we're only concerned with e_{rlwe x rgsw}, that is noise caused due to - // RLWExRGSW. Also note, in practice m1 is a monomial, for ex, X^{s_{i}}, for - // some i and var(em1) = var(e). - let mut m_plus_e_times_m1_more_e = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_after, - ideal_client_key.sk_rlwe.values(), - &mut m_plus_e_times_m1_more_e, - rlwe_nttop, - rlwe_modop, - ); - - // diff - rlwe_modop.elwise_sub_mut( - m_plus_e_times_m1_more_e.as_mut_slice(), - m_plus_e_times_m1.as_slice(), - ); +// thread_local! { +// static PBS_TRACER: RefCell>>> = +// RefCell::new(PBSTracer::default()); } + +// #[derive(Default)] +// struct PBSTracer +// where +// M: Matrix + Default, +// { +// pub(crate) ct_rlwe_q_mod: M::R, +// pub(crate) ct_lwe_q_mod: M::R, +// pub(crate) ct_lwe_q_mod_after_ksk: M::R, +// pub(crate) ct_br_q_mod: Vec, +// } - let noise = measure_noise( - &rlwe_after, - &m_plus_e_times_m1, - rlwe_nttop, - rlwe_modop, - ideal_client_key.sk_rlwe.values(), - ); - print!("NOISE: {}", noise); +// impl PBSTracer>> { +// fn trace(&self, parameters: &BoolParameters, sk_lwe: &[i32], +// sk_rlwe: &[i32]) { assert!(parameters.rlwe_n().0 == sk_rlwe.len()); +// assert!(parameters.lwe_n().0 == sk_lwe.len()); + +// let modop_rlweq = ModularOpsU64::new(*parameters.rlwe_q()); +// // noise after mod down Q -> Q_ks +// let m_back0 = decrypt_lwe(&self.ct_rlwe_q_mod, sk_rlwe, +// &modop_rlweq); + +// let modop_lweq = ModularOpsU64::new(parameters.lwe_q().0); +// // noise after mod down Q -> Q_ks +// let m_back1 = decrypt_lwe(&self.ct_lwe_q_mod, sk_rlwe, &modop_lweq); +// // noise after key switch from RLWE -> LWE +// let m_back2 = decrypt_lwe(&self.ct_lwe_q_mod_after_ksk, sk_lwe, +// &modop_lweq); + +// // noise after mod down odd from Q_ks -> q +// let modop_br_q = ModularOpsU64::new(parameters.br_q().0); +// let m_back3 = decrypt_lwe(&self.ct_br_q_mod, sk_lwe, &modop_br_q); + +// println!( +// " +// M initial mod Q: {m_back0}, +// M after mod down Q -> Q_ks: {m_back1}, +// M after key switch from RLWE -> LWE: {m_back2}, +// M after mod dwon Q_ks -> q: {m_back3} +// " +// ); +// } +// } - check.add_more(&Vec::::try_convert_from( - &m_plus_e_times_m1_more_e, - &rlwe_q, - )); - }); - println!( - "RLWE x RGSW, where RGSW has noise var_brk, std: {} {}", - check.std_dev(), - check.std_dev().abs().log2() - ) - } - } +// impl WithLocal for PBSTracer>> { +// fn with_local(func: F) -> R +// where +// F: Fn(&Self) -> R, +// { +// PBS_TRACER.with_borrow(|t| func(t)) +// } - // Check noise in fresh RGSW ciphertexts, ie X^{s_j[i]}, must equal noise in - // fresh RLWE ciphertext - if true {} - // test LWE ksk from RLWE -> LWE - // if false { - // let logp = 2; - // let mut rng = DefaultSecureRng::new(); - - // let m = 1; - // let encoded_m = m << (lwe_logq - logp); - - // // Encrypt - // let mut lwe_ct = vec![0u64; rlwe_n + 1]; - // encrypt_lwe( - // &mut lwe_ct, - // &encoded_m, - // ideal_client_key.sk_rlwe.values(), - // lwe_modop, - // &mut rng, - // ); - - // // key switch - // let lwe_decomposer = &bool_evaluator.decomposer_lwe; - // let mut lwe_out = vec![0u64; lwe_n + 1]; - // lwe_key_switch( - // &mut lwe_out, - // &lwe_ct, - // &server_key_eval.lwe_ksk, - // lwe_modop, - // lwe_decomposer, - // ); - - // let encoded_m_back = decrypt_lwe(&lwe_out, - // ideal_client_key.sk_lwe.values(), lwe_modop); let m_back - // = ((encoded_m_back as f64 * (1 << logp) as f64) / - // (lwe_q as f64)).round() as u64; dbg!(m_back, m); - - // let noise = measure_noise_lwe( - // &lwe_out, - // ideal_client_key.sk_lwe.values(), - // lwe_modop, - // &encoded_m, - // ); - - // println!("Noise: {noise}"); - // } - - // Measure noise in RGSW ciphertexts of ideal LWE secrets - // if true { - // let gadget_vec = gadget_vector( - // bool_evaluator.parameters.rlwe_logq, - // bool_evaluator.parameters.logb_rgsw, - // bool_evaluator.parameters.d_rgsw, - // ); - - // for i in 0..20 { - // // measure noise in RGSW(s[i]) - // let si = - // ideal_client_key.sk_lwe.values[i] * - // (bool_evaluator.embedding_factor as i32); let mut - // si_poly = vec![0u64; rlwe_n]; if si < 0 { - // si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1; - // } else { - // si_poly[(si.abs() as usize)] = 1; - // } - - // let mut rgsw_si = server_key_eval.rgsw_cts[i].clone(); - // rgsw_si - // .iter_mut() - // .for_each(|ri| rlwe_nttop.backward(ri.as_mut())); - - // println!("####### Noise in RGSW(X^s_{i}) #######"); - // _measure_noise_rgsw( - // &rgsw_si, - // &si_poly, - // ideal_client_key.sk_rlwe.values(), - // &gadget_vec, - // rlwe_q, - // ); - // println!("####### ##################### #######"); - // } - // } - - // // measure noise grwoth in RLWExRGSW - // if true { - // let mut rng = DefaultSecureRng::new(); - // let mut carry_m = vec![0u64; rlwe_n]; - // RandomUniformDist::random_fill(&mut rng, &rlwe_q, - // carry_m.as_mut_slice()); - - // // RGSW(carrym) - // let trivial_rlwect = vec![vec![0u64; rlwe_n], carry_m.clone()]; - // let mut rlwe_ct = RlweCiphertext::<_, - // DefaultSecureRng>::from_raw(trivial_rlwect, true); - - // let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; - // d_rgsw + 2]; let mul_mod = - // |v0: &u64, v1: &u64| (((*v0 as u128 * *v1 as u128) % (rlwe_q as u128)) as u64); - - // for i in 0..bool_evaluator.parameters.lwe_n { - // rlwe_by_rgsw( - // &mut rlwe_ct, - // server_key_eval.rgsw_ct_lwe_si(i), - // &mut scratch_matrix_dplus2_ring, - // rlwe_decomposer, - // rlwe_nttop, - // rlwe_modop, - // ); - - // // carry_m[X] * s_i[X] - // let si = - // ideal_client_key.sk_lwe.values[i] * - // (bool_evaluator.embedding_factor as i32); let mut - // si_poly = vec![0u64; rlwe_n]; if si < 0 { - // si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1; - // } else { - // si_poly[(si.abs() as usize)] = 1; - // } - // carry_m = negacyclic_mul(&carry_m, &si_poly, mul_mod, - // rlwe_q); - - // let noise = measure_noise( - // &rlwe_ct, - // &carry_m, - // rlwe_nttop, - // rlwe_modop, - // ideal_client_key.sk_rlwe.values(), - // ); - // println!("Noise RLWE(carry_m) accumulating {i}^th secret - // monomial: {noise}"); } - // } - - // // Check galois keys - // if false { - // let g = bool_evaluator.g() as isize; - // let mut rng = DefaultSecureRng::new(); - // let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; - // d_rgsw + 2]; for i in [g, -g] { - // let mut m = vec![0u64; rlwe_n]; - // RandomUniformDist::random_fill(&mut rng, &rlwe_q, - // m.as_mut_slice()); let mut rlwe_ct = { - // let mut data = vec![vec![0u64; rlwe_n]; 2]; - // public_key_encrypt_rlwe( - // &mut data, - // &collective_pk.key, - // &m, - // rlwe_modop, - // rlwe_nttop, - // &mut rng, - // ); - // RlweCiphertext::<_, DefaultSecureRng>::from_raw(data, - // false) }; - - // let auto_key = server_key_eval.galois_key_for_auto(i); - // let (auto_map_index, auto_map_sign) = - // generate_auto_map(rlwe_n, i); galois_auto( - // &mut rlwe_ct, - // auto_key, - // &mut scratch_matrix_dplus2_ring, - // &auto_map_index, - // &auto_map_sign, - // rlwe_modop, - // rlwe_nttop, - // rlwe_decomposer, - // ); - - // // send m(X) -> m(X^i) - // let mut m_k = vec![0u64; rlwe_n]; - // izip!(m.iter(), auto_map_index.iter(), - // auto_map_sign.iter()).for_each( |(mi, to_index, to_sign)| - // { if !to_sign { - // m_k[*to_index] = rlwe_q - *mi; - // } else { - // m_k[*to_index] = *mi; - // } - // }, - // ); - - // // measure noise - // let noise = measure_noise( - // &rlwe_ct, - // &m_k, - // rlwe_nttop, - // rlwe_modop, - // ideal_client_key.sk_rlwe.values(), - // ); - - // println!("Noise after auto k={i}: {noise}"); - // } - // } - } -} +// fn with_local_mut(func: F) -> R +// where +// F: Fn(&mut Self) -> R, +// { +// PBS_TRACER.with_borrow_mut(|t| func(t)) +// } +// } diff --git a/src/bool/parameters.rs b/src/bool/parameters.rs index 4e59f89..d7f6e83 100644 --- a/src/bool/parameters.rs +++ b/src/bool/parameters.rs @@ -1,10 +1,12 @@ -use crate::decomposer::Decomposer; +use num_traits::{ConstZero, PrimInt, Zero}; + +use crate::{backend::Modulus, decomposer::Decomposer}; #[derive(Clone, PartialEq)] pub(super) struct BoolParameters { rlwe_q: CiphertextModulus, lwe_q: CiphertextModulus, - br_q: CiphertextModulus, + br_q: usize, rlwe_n: PolynomialSize, lwe_n: LweDimension, lwe_decomposer_base: DecompostionLogBase, @@ -30,7 +32,7 @@ impl BoolParameters { &self.lwe_q } - pub(crate) fn br_q(&self) -> &CiphertextModulus { + pub(crate) fn br_q(&self) -> &usize { &self.br_q } @@ -164,12 +166,72 @@ pub(crate) struct LweDimension(pub(crate) usize); #[derive(Clone, Copy, PartialEq)] pub(crate) struct PolynomialSize(pub(crate) usize); #[derive(Clone, Copy, PartialEq)] -pub(crate) struct CiphertextModulus(T); + +/// T eqauls modulus when modulus is non-native. Otherwise T equals 0. bool is +/// true when modulus is native, false otherwise. +pub(crate) struct CiphertextModulus(T, bool); + +impl CiphertextModulus { + const fn new_native() -> Self { + // T::zero is stored only for convenience. It has no use when modulus + // is native. That is, either u128,u64,u32,u16 + Self(T::ZERO, true) + } + + const fn new_non_native(q: T) -> Self { + Self(q, false) + } +} + +impl Modulus for CiphertextModulus +where + T: PrimInt, +{ + type Element = T; + fn is_native(&self) -> bool { + false + } + fn largest_unsigned_value(&self) -> Self::Element { + if self.1 { + T::max_value() + } else { + self.0 - T::one() + } + } + fn neg_one(&self) -> Self::Element { + if self.1 { + T::max_value() + } else { + self.0 - T::one() + } + } + // fn signed_max(&self) -> Self::Element {} + // fn signed_min(&self) -> Self::Element {} + fn smallest_unsigned_value(&self) -> Self::Element { + T::zero() + } + + fn to_i64(&self, v: &Self::Element) -> i64 { + todo!() + } + + fn from_f64(&self, v: f64) -> Self::Element { + todo!() + } + + fn from_i64(&self, v: i64) -> Self::Element { + todo!() + } + + fn q(&self) -> Option { + todo!() + } +} pub(super) const SP_BOOL_PARAMS: BoolParameters = BoolParameters:: { - rlwe_q: CiphertextModulus(268369921u64), - lwe_q: CiphertextModulus(1 << 16), - br_q: CiphertextModulus(1 << 10), + rlwe_q: CiphertextModulus::new_non_native(268369921u64), + lwe_q: CiphertextModulus::new_non_native(1 << 16), + br_q: 1 << 10, rlwe_n: PolynomialSize(1 << 10), lwe_n: LweDimension(493), lwe_decomposer_base: DecompostionLogBase(4), @@ -185,9 +247,9 @@ pub(super) const SP_BOOL_PARAMS: BoolParameters = BoolParameters:: { }; pub(super) const MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { - rlwe_q: CiphertextModulus(1152921504606830593), - lwe_q: CiphertextModulus(1 << 20), - br_q: CiphertextModulus(1 << 11), + rlwe_q: CiphertextModulus::new_non_native(1152921504606830593), + lwe_q: CiphertextModulus::new_non_native(1 << 20), + br_q: 1 << 10, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(500), lwe_decomposer_base: DecompostionLogBase(4), diff --git a/src/lib.rs b/src/lib.rs index 69f5dde..3d15566 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,7 @@ use itertools::{izip, Itertools}; use num::UnsignedInteger; use num_traits::{abs, Zero}; use rand::CryptoRng; -use random::{RandomGaussianDist, RandomUniformDist}; -use utils::TryConvertFrom; +use utils::TryConvertFrom1; mod backend; mod bool; diff --git a/src/lwe.rs b/src/lwe.rs index 27df3a4..000235f 100644 --- a/src/lwe.rs +++ b/src/lwe.rs @@ -9,12 +9,13 @@ use itertools::{izip, Itertools}; use num_traits::{abs, PrimInt, ToPrimitive, Zero}; use crate::{ - backend::{ArithmeticOps, VectorOps}, + backend::{ArithmeticOps, GetModulus, Modulus, VectorOps}, decomposer::Decomposer, - lwe, - num::UnsignedInteger, - random::{DefaultSecureRng, NewWithSeed, RandomGaussianDist, RandomUniformDist, DEFAULT_RNG}, - utils::{fill_random_ternary_secret_with_hamming_weight, TryConvertFrom, WithLocal}, + random::{ + DefaultSecureRng, NewWithSeed, RandomFillGaussianInModulus, RandomGaussianElementInModulus, + RandomFillUniformInModulus, DEFAULT_RNG, + }, + utils::{fill_random_ternary_secret_with_hamming_weight, TryConvertFrom1, WithLocal}, Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret, }; @@ -53,7 +54,7 @@ struct LweKeySwitchingKey { impl< M: MatrixMut + MatrixEntity, - R: NewWithSeed + RandomUniformDist<[M::MatElement], Parameters = M::MatElement>, + R: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], M::MatElement>, > From<&SeededLweKeySwitchingKey> for LweKeySwitchingKey where M::R: RowMut, @@ -64,7 +65,7 @@ where let mut p_rng = R::new_with_seed(value.seed.clone()); let mut data = M::zeros(value.data.as_ref().len(), value.to_lwe_n + 1); izip!(value.data.as_ref().iter(), data.iter_rows_mut()).for_each(|(bi, lwe_i)| { - RandomUniformDist::random_fill(&mut p_rng, &value.modulus, &mut lwe_i.as_mut()[1..]); + RandomFillUniformInModulus::random_fill(&mut p_rng, &value.modulus, &mut lwe_i.as_mut()[1..]); lwe_i.as_mut()[0] = *bi; }); LweKeySwitchingKey { @@ -132,9 +133,11 @@ pub(crate) fn lwe_key_switch< pub fn lwe_ksk_keygen< Ro: Row + RowMut + RowEntity, S, - Op: VectorOps + ArithmeticOps, - R: RandomGaussianDist, - PR: RandomUniformDist<[Ro::Element], Parameters = Ro::Element>, + Op: VectorOps + + ArithmeticOps + + GetModulus, + R: RandomGaussianElementInModulus, + PR: RandomFillUniformInModulus<[Ro::Element], Op::M>, >( from_lwe_sk: &[S], to_lwe_sk: &[S], @@ -144,17 +147,17 @@ pub fn lwe_ksk_keygen< p_rng: &mut PR, rng: &mut R, ) where - Ro: TryConvertFrom<[S], Parameters = Ro::Element>, + Ro: TryConvertFrom1<[S], Op::M>, Ro::Element: Zero + Debug, { assert!(ksk_out.as_ref().len() == (from_lwe_sk.len() * gadget.len())); let d = gadget.len(); - let modulus = VectorOps::modulus(operator); - let mut neg_sk_in_m = Ro::try_convert_from(from_lwe_sk, &modulus); + let modulus = operator.modulus(); + let mut neg_sk_in_m = Ro::try_convert_from(from_lwe_sk, modulus); operator.elwise_neg_mut(neg_sk_in_m.as_mut()); - let sk_out_m = Ro::try_convert_from(to_lwe_sk, &modulus); + let sk_out_m = Ro::try_convert_from(to_lwe_sk, modulus); let mut scratch = Ro::zeros(to_lwe_sk.len()); @@ -162,7 +165,7 @@ pub fn lwe_ksk_keygen< |(neg_sk_in_si, d_lwes_partb)| { izip!(gadget.iter(), d_lwes_partb.into_iter()).for_each(|(f, lwe_b)| { // sample `a` - RandomUniformDist::random_fill(p_rng, &modulus, scratch.as_mut()); + RandomFillUniformInModulus::random_fill(p_rng, &modulus, scratch.as_mut()); // a * z let mut az = Ro::Element::zero(); @@ -173,8 +176,7 @@ pub fn lwe_ksk_keygen< // a*z + (-s_i)*\beta^j + e let mut b = operator.add(&az, &operator.mul(f, neg_sk_in_si)); - let mut e = Ro::Element::zero(); - RandomGaussianDist::random_fill(rng, &modulus, &mut e); + let e = RandomGaussianElementInModulus::random(rng, &modulus); b = operator.add(&b, &e); *lwe_b = b; @@ -186,10 +188,9 @@ pub fn lwe_ksk_keygen< /// Encrypts encoded message m as LWE ciphertext pub fn encrypt_lwe< Ro: Row + RowMut, - R: RandomGaussianDist - + RandomUniformDist<[Ro::Element], Parameters = Ro::Element>, + Op: ArithmeticOps + GetModulus, + R: RandomGaussianElementInModulus + RandomFillUniformInModulus<[Ro::Element], Op::M>, S, - Op: ArithmeticOps, >( lwe_out: &mut Ro, m: &Ro::Element, @@ -197,14 +198,14 @@ pub fn encrypt_lwe< operator: &Op, rng: &mut R, ) where - Ro: TryConvertFrom<[S], Parameters = Ro::Element>, + Ro: TryConvertFrom1<[S], Op::M>, Ro::Element: Zero, { - let s = Ro::try_convert_from(s, &operator.modulus()); + let s = Ro::try_convert_from(s, operator.modulus()); assert!(s.as_ref().len() == (lwe_out.as_ref().len() - 1)); // a*s - RandomUniformDist::random_fill(rng, &operator.modulus(), &mut lwe_out.as_mut()[1..]); + RandomFillUniformInModulus::random_fill(rng, operator.modulus(), &mut lwe_out.as_mut()[1..]); let mut sa = Ro::Element::zero(); izip!(lwe_out.as_mut().iter().skip(1), s.as_ref()).for_each(|(ai, si)| { let tmp = operator.mul(ai, si); @@ -212,22 +213,25 @@ pub fn encrypt_lwe< }); // b = a*s + e + m - let mut e = Ro::Element::zero(); - RandomGaussianDist::random_fill(rng, &operator.modulus(), &mut e); + let e = RandomGaussianElementInModulus::random(rng, operator.modulus()); let b = operator.add(&operator.add(&sa, &e), m); lwe_out.as_mut()[0] = b; } -pub fn decrypt_lwe, S>( +pub fn decrypt_lwe< + Ro: Row, + Op: ArithmeticOps + GetModulus, + S, +>( lwe_ct: &Ro, s: &[S], operator: &Op, ) -> Ro::Element where - Ro: TryConvertFrom<[S], Parameters = Ro::Element>, + Ro: TryConvertFrom1<[S], Op::M>, Ro::Element: Zero, { - let s = Ro::try_convert_from(s, &operator.modulus()); + let s = Ro::try_convert_from(s, operator.modulus()); let mut sa = Ro::Element::zero(); izip!(lwe_ct.as_ref().iter().skip(1), s.as_ref()).for_each(|(ai, si)| { @@ -244,14 +248,18 @@ where /// - ct: Input LWE ciphertext /// - s: corresponding secret /// - ideal_m: Ideal `encoded` message -pub(crate) fn measure_noise_lwe, S>( +pub(crate) fn measure_noise_lwe< + Ro: Row, + Op: ArithmeticOps + GetModulus, + S, +>( ct: &Ro, s: &[S], operator: &Op, ideal_m: &Ro::Element, ) -> f64 where - Ro: TryConvertFrom<[S], Parameters = Ro::Element>, + Ro: TryConvertFrom1<[S], Op::M>, Ro::Element: Zero + ToPrimitive + PrimInt + Display, { assert!(s.len() == ct.as_ref().len() - 1,); @@ -265,10 +273,7 @@ where let mut diff = operator.sub(&m, ideal_m); let q = operator.modulus(); - if diff > (q >> 1) { - diff = q - diff; - } - return diff.to_f64().unwrap().log2(); + return q.to_i64(&diff).to_f64().unwrap().abs().log2(); } #[cfg(test)] diff --git a/src/multi_party.rs b/src/multi_party.rs index cda5bf8..86fbf60 100644 --- a/src/multi_party.rs +++ b/src/multi_party.rs @@ -1,18 +1,18 @@ use crate::{ - backend::VectorOps, + backend::{GetModulus, VectorOps}, ntt::Ntt, - random::{NewWithSeed, RandomGaussianDist, RandomUniformDist}, - utils::TryConvertFrom, + random::{NewWithSeed, RandomFillGaussianInModulus, RandomFillUniformInModulus}, + utils::TryConvertFrom1, Matrix, Row, RowEntity, RowMut, }; pub(crate) fn public_key_share< R: Row + RowMut + RowEntity, S, - ModOp: VectorOps, + ModOp: VectorOps + GetModulus, NttOp: Ntt, - Rng: RandomGaussianDist<[R::Element], Parameters = R::Element>, - PRng: RandomUniformDist<[R::Element], Parameters = R::Element>, + Rng: RandomFillGaussianInModulus<[R::Element], ModOp::M>, + PRng: RandomFillUniformInModulus<[R::Element], ModOp::M>, >( share_out: &mut R, s_i: &[S], @@ -21,7 +21,7 @@ pub(crate) fn public_key_share< p_rng: &mut PRng, rng: &mut Rng, ) where - R: TryConvertFrom<[S], Parameters = R::Element>, + R: TryConvertFrom1<[S], ModOp::M>, { let ring_size = share_out.as_ref().len(); assert!(s_i.len() == ring_size); @@ -31,7 +31,7 @@ pub(crate) fn public_key_share< // sample a let mut a = { let mut a = R::zeros(ring_size); - RandomUniformDist::random_fill(p_rng, &q, a.as_mut()); + RandomFillUniformInModulus::random_fill(p_rng, &q, a.as_mut()); a }; @@ -42,6 +42,6 @@ pub(crate) fn public_key_share< modop.elwise_mul_mut(s.as_mut(), a.as_ref()); nttop.backward(s.as_mut()); - RandomGaussianDist::random_fill(rng, &q, share_out.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, share_out.as_mut()); modop.elwise_add_mut(share_out.as_mut(), s.as_ref()); // s*e + e } diff --git a/src/noise.rs b/src/noise.rs index 6a0b9ed..af09641 100644 --- a/src/noise.rs +++ b/src/noise.rs @@ -2,20 +2,21 @@ mod tests { use rand::{thread_rng, Rng}; - use crate::{ - backend::{ArithmeticOps, ModInit, ModularOpsU64}, - decomposer::{Decomposer, DefaultDecomposer}, - ntt::{Ntt, NttBackendU64, NttInit}, - random::{DefaultSecureRng, RandomGaussianDist, RandomUniformDist}, - rgsw::{ - less1_rlwe_by_rgsw, measure_noise, rgsw_by_rgsw_inplace, rlwe_by_rgsw, - secret_key_encrypt_rgsw, secret_key_encrypt_rlwe, RgswCiphertext, - RgswCiphertextEvaluationDomain, RlweCiphertext, RlweSecret, SeededRgswCiphertext, - SeededRlweCiphertext, - }, - utils::{generate_prime, negacyclic_mul}, - Matrix, Row, Secret, - }; + // use crate::{ + // backend::{ArithmeticOps, ModInit, ModularOpsU64}, + // decomposer::{Decomposer, DefaultDecomposer}, + // ntt::{Ntt, NttBackendU64, NttInit}, + // random::{DefaultSecureRng, RandomGaussianDist, RandomUniformDist}, + // rgsw::{ + // less1_rlwe_by_rgsw, measure_noise, rgsw_by_rgsw_inplace, + // rlwe_by_rgsw, secret_key_encrypt_rgsw, + // secret_key_encrypt_rlwe, RgswCiphertext, + // RgswCiphertextEvaluationDomain, RlweCiphertext, RlweSecret, + // SeededRgswCiphertext, SeededRlweCiphertext, + // }, + // utils::{generate_prime, negacyclic_mul}, + // Matrix, Row, Secret, + // }; // // Test B part with limbd -1 when variance of m is 1 // #[test] diff --git a/src/ntt.rs b/src/ntt.rs index 54f8b85..b6b9649 100644 --- a/src/ntt.rs +++ b/src/ntt.rs @@ -3,15 +3,14 @@ use rand::{thread_rng, Rng, RngCore, SeedableRng}; use rand_chacha::ChaCha8Rng; use crate::{ - backend::{ArithmeticOps, ModInit, ModularOpsU64}, + backend::{ArithmeticOps, ModInit, ModularOpsU64, Modulus}, utils::{mod_exponent, mod_inverse, shoup_representation_fq}, }; -pub trait NttInit { - type Element; +pub trait NttInit { /// Ntt istance must be compatible across different instances with same `q` /// and `n` - fn new(q: Self::Element, n: usize) -> Self; + fn new(q: &M, n: usize) -> Self; } pub trait Ntt { @@ -204,9 +203,8 @@ pub struct NttBackendU64 { psi_inv_powers_bo_shoup: Box<[u64]>, } -impl NttInit for NttBackendU64 { - type Element = u64; - fn new(q: u64, n: usize) -> Self { +impl NttBackendU64 { + fn _new(q: u64, n: usize) -> Self { // \psi = 2n^{th} primitive root of unity in F_q let mut rng = ChaCha8Rng::from_seed([0u8; 32]); let psi = find_primitive_root(q, (n * 2) as u64, &mut rng) @@ -270,6 +268,14 @@ impl NttInit for NttBackendU64 { } } +impl> NttInit for NttBackendU64 { + fn new(q: &M, n: usize) -> Self { + // This NTT does not support native modulus + assert!(!q.is_native()); + NttBackendU64::_new(q.q().unwrap(), n) + } +} + impl NttBackendU64 { fn reduce_from_lazy(&self, a: &mut [u64]) { let q = self.q; @@ -356,7 +362,7 @@ mod tests { #[test] fn native_ntt_backend_works() { // TODO(Jay): Improve tests. Add tests for different primes and ring size. - let ntt_backend = NttBackendU64::new(Q_60_BITS, N); + let ntt_backend = NttBackendU64::_new(Q_60_BITS, N); for _ in 0..K { let mut a = random_vec_in_fq(N, Q_60_BITS); let a_clone = a.clone(); @@ -391,7 +397,7 @@ mod tests { .collect_vec(); for p in primes.into_iter() { - let ntt_backend = NttBackendU64::new(p, N); + let ntt_backend = NttBackendU64::_new(p, N); let modulus_backend = ModularOpsU64::new(p); for _ in 0..K { let a = random_vec_in_fq(N, p); diff --git a/src/random.rs b/src/random.rs index 80820b6..7980c47 100644 --- a/src/random.rs +++ b/src/random.rs @@ -17,26 +17,45 @@ pub(crate) trait NewWithSeed { fn new_with_seed(seed: Self::Seed) -> Self; } -pub trait RandomGaussianDist +pub trait RandomElement { + /// Sample Random element of type T + fn random(&mut self) -> T; +} + +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, { - type Parameters: ?Sized; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut M); + /// Fill container with random elements of type of its elements + fn random_fill(&mut self, container: &mut M); } -pub trait RandomUniformDist +pub trait RandomFillUniformInModulus where M: ?Sized, { - type Parameters: ?Sized; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut M); + /// Fill container with random elements in range [0, modulus) + fn random_fill(&mut self, modulus: &P, container: &mut M); } -pub trait RandomUniformDist1 +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); } @@ -67,25 +86,17 @@ impl NewWithSeed for DefaultSecureRng { } } -impl RandomUniformDist for DefaultSecureRng { - type Parameters = usize; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut usize) { - *container = self.rng.gen_range(0..*parameters); - } -} - -impl RandomUniformDist<[u8]> for DefaultSecureRng { - type Parameters = u8; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u8]) { - self.rng.fill_bytes(container); - } -} - -impl RandomUniformDist<[u32]> for DefaultSecureRng { - type Parameters = u32; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u32]) { +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(0, parameters)), + (&mut self.rng).sample_iter(Uniform::new_inclusive( + T::zero(), + modulus.largest_unsigned_value() + )), container.iter_mut() ) .for_each(|(from, to)| { @@ -94,30 +105,31 @@ impl RandomUniformDist<[u32]> for DefaultSecureRng { } } -impl RandomUniformDist1<[T], C> for DefaultSecureRng +impl RandomFillGaussianInModulus<[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() - )), + rand_distr::Normal::new(0.0, 3.19f64) + .unwrap() + .sample_iter(&mut self.rng), container.iter_mut() ) .for_each(|(from, to)| { - *to = from; + *to = modulus.from_f64(from); }); } } -impl RandomUniformDist<[u64]> for DefaultSecureRng { - type Parameters = u64; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u64]) { +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(0, parameters)), + (&mut self.rng).sample_iter(Uniform::new_inclusive(T::zero(), T::max_value())), container.iter_mut() ) .for_each(|(from, to)| { @@ -126,83 +138,31 @@ impl RandomUniformDist<[u64]> for DefaultSecureRng { } } -impl RandomGaussianDist for DefaultSecureRng { - type Parameters = u64; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut u64) { - let o = rand_distr::Normal::new(0.0, 3.19f64) - .unwrap() - .sample(&mut self.rng) - .round(); - - // let o = 0.0f64; - - let is_neg = o.is_sign_negative() && o != 0.0; - if is_neg { - *container = parameters - (o.abs() as u64); - } else { - *container = o as u64; - } +impl RandomElement for DefaultSecureRng +where + T: PrimInt + SampleUniform, +{ + fn random(&mut self) -> T { + Uniform::new_inclusive(T::zero(), T::max_value()).sample(&mut self.rng) } } -impl RandomGaussianDist for DefaultSecureRng { - type Parameters = u32; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut u32) { - let o = rand_distr::Normal::new(0.0, 3.19f32) - .unwrap() - .sample(&mut self.rng) - .round(); - - // let o = 0.0f32; - let is_neg = o.is_sign_negative() && o != 0.0; - - if is_neg { - *container = parameters - (o.abs() as u32); - } else { - *container = o as u32; - } +impl RandomElementInModulus for DefaultSecureRng +where + T: PrimInt + SampleUniform, +{ + fn random(&mut self, modulus: &T) -> T { + Uniform::new_inclusive(T::zero(), modulus).sample(&mut self.rng) } } -impl RandomGaussianDist<[u64]> for DefaultSecureRng { - type Parameters = u64; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u64]) { - izip!( +impl> RandomGaussianElementInModulus for DefaultSecureRng { + fn random(&mut self, modulus: &M) -> T { + modulus.from_f64( rand_distr::Normal::new(0.0, 3.19f64) .unwrap() - .sample_iter(&mut self.rng), - container.iter_mut() - ) - .for_each(|(oi, v)| { - let oi = oi.round(); - let is_neg = oi.is_sign_negative() && oi != 0.0; - if is_neg { - *v = parameters - (oi.abs() as u64); - } else { - *v = oi as u64; - } - }); - } -} - -impl RandomGaussianDist<[u32]> for DefaultSecureRng { - type Parameters = u32; - fn random_fill(&mut self, parameters: &Self::Parameters, container: &mut [u32]) { - izip!( - rand_distr::Normal::new(0.0, 3.19f32) - .unwrap() - .sample_iter(&mut self.rng), - container.iter_mut() + .sample(&mut self.rng), ) - .for_each(|(oi, v)| { - let oi = oi.round(); - let is_neg = oi.is_sign_negative() && oi != 0.0; - if is_neg { - *v = parameters - (oi.abs() as u32); - } else { - *v = oi as u32; - } - }); } } diff --git a/src/rgsw.rs b/src/rgsw.rs index b945a0e..1b8d279 100644 --- a/src/rgsw.rs +++ b/src/rgsw.rs @@ -9,34 +9,32 @@ use itertools::{izip, Itertools}; use num_traits::{PrimInt, Signed, ToPrimitive, Zero}; use crate::{ - backend::{ArithmeticOps, VectorOps}, + backend::{ArithmeticOps, GetModulus, Modulus, VectorOps}, decomposer::{self, Decomposer, RlweDecomposer}, ntt::{self, Ntt, NttInit}, - random::{DefaultSecureRng, NewWithSeed, RandomGaussianDist, RandomUniformDist}, - utils::{fill_random_ternary_secret_with_hamming_weight, TryConvertFrom, WithLocal}, + random::{ + DefaultSecureRng, NewWithSeed, RandomElementInModulus, RandomFill, RandomFillGaussianInModulus, + RandomFillUniformInModulus, + }, + utils::{fill_random_ternary_secret_with_hamming_weight, TryConvertFrom1, WithLocal}, Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret, }; -pub struct SeededAutoKey +pub struct SeededAutoKey where M: Matrix, { data: M, seed: S, - modulus: M::MatElement, + modulus: Mod, } -impl SeededAutoKey { - fn empty( - ring_size: usize, - auto_decomposer: &D, - seed: S, - modulus: M::MatElement, - ) -> Self { +impl> SeededAutoKey { + fn empty(ring_size: usize, auto_decomposer: &D, seed: S, modulus: Mod) -> Self { SeededAutoKey { data: M::zeros(auto_decomposer.decomposition_count(), ring_size), seed, - modulus: modulus, + modulus, } } } @@ -48,22 +46,23 @@ pub struct AutoKeyEvaluationDomain { impl< M: MatrixMut + MatrixEntity, - R: RandomUniformDist<[M::MatElement], Parameters = M::MatElement> + NewWithSeed, - N: NttInit + Ntt, - > From<&SeededAutoKey> for AutoKeyEvaluationDomain + Mod: Modulus + Clone, + R: RandomFillUniformInModulus<[M::MatElement], Mod> + NewWithSeed, + N: NttInit + Ntt, + > From<&SeededAutoKey> for AutoKeyEvaluationDomain where ::R: RowMut, M::MatElement: Copy, R::Seed: Clone, { - fn from(value: &SeededAutoKey) -> Self { + fn from(value: &SeededAutoKey) -> Self { let (d, ring_size) = value.data.dimension(); let mut data = M::zeros(2 * d, ring_size); // sample RLWE'_A(-s(X^k)) let mut p_rng = R::new_with_seed(value.seed.clone()); data.iter_rows_mut().take(d).for_each(|r| { - RandomUniformDist::random_fill(&mut p_rng, &value.modulus, r.as_mut()); + RandomFillUniformInModulus::random_fill(&mut p_rng, &value.modulus, r.as_mut()); }); // copy over RLWE'_B(-s(X^k)) @@ -72,7 +71,7 @@ where }); // send RLWE'(-s(X^k)) polynomials to evaluation domain - let ntt_op = N::new(value.modulus, ring_size); + let ntt_op = N::new(&value.modulus, ring_size); data.iter_rows_mut() .for_each(|r| ntt_op.forward(r.as_mut())); @@ -83,22 +82,22 @@ where } } -pub struct RgswCiphertext { +pub struct RgswCiphertext { /// Rgsw ciphertext polynomials pub(crate) data: M, - modulus: M::MatElement, + modulus: Mod, /// Decomposition for RLWE part A d_a: usize, /// Decomposition for RLWE part B d_b: usize, } -impl RgswCiphertext { +impl> RgswCiphertext { pub(crate) fn empty( ring_size: usize, decomposer: &D, - modulus: M::MatElement, - ) -> RgswCiphertext { + modulus: Mod, + ) -> RgswCiphertext { RgswCiphertext { data: M::zeros( decomposer.a().decomposition_count() * 2 + decomposer.b().decomposition_count() * 2, @@ -111,40 +110,40 @@ impl RgswCiphertext { } } -pub struct SeededRgswCiphertext +pub struct SeededRgswCiphertext where M: Matrix, { pub(crate) data: M, seed: S, - modulus: M::MatElement, + modulus: Mod, /// Decomposition for RLWE part A d_a: usize, /// Decomposition for RLWE part B d_b: usize, } -impl SeededRgswCiphertext { +impl SeededRgswCiphertext { pub(crate) fn empty( ring_size: usize, decomposer: &D, seed: S, - modulus: M::MatElement, - ) -> SeededRgswCiphertext { + modulus: Mod, + ) -> SeededRgswCiphertext { SeededRgswCiphertext { data: M::zeros( decomposer.a().decomposition_count() * 2 + decomposer.b().decomposition_count(), ring_size, ), seed, - modulus: modulus, + modulus, d_a: decomposer.a().decomposition_count(), d_b: decomposer.b().decomposition_count(), } } } -impl Debug for SeededRgswCiphertext +impl Debug for SeededRgswCiphertext where M::MatElement: Debug, { @@ -164,16 +163,17 @@ pub struct RgswCiphertextEvaluationDomain { impl< M: MatrixMut + MatrixEntity, - R: NewWithSeed + RandomUniformDist<[M::MatElement], Parameters = M::MatElement>, - N: NttInit + Ntt + Debug, - > From<&SeededRgswCiphertext> for RgswCiphertextEvaluationDomain + Mod: Modulus, + R: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], Mod>, + N: NttInit + Ntt + Debug, + > From<&SeededRgswCiphertext> for RgswCiphertextEvaluationDomain where ::R: RowMut, M::MatElement: Copy, R::Seed: Clone, M: Debug, { - fn from(value: &SeededRgswCiphertext) -> Self { + fn from(value: &SeededRgswCiphertext) -> Self { let mut data = M::zeros(value.d_a * 2 + value.d_b * 2, value.data.dimension().1); // copy RLWE'(-sm) @@ -203,7 +203,7 @@ where // Send polynomials to evaluation domain let ring_size = data.dimension().1; - let nttop = N::new(value.modulus, ring_size); + let nttop = N::new(&value.modulus, ring_size); data.iter_rows_mut() .for_each(|ri| nttop.forward(ri.as_mut())); @@ -216,15 +216,16 @@ where impl< M: MatrixMut + MatrixEntity, + Mod: Modulus, R, - N: NttInit + Ntt, - > From<&RgswCiphertext> for RgswCiphertextEvaluationDomain + N: NttInit + Ntt, + > From<&RgswCiphertext> for RgswCiphertextEvaluationDomain where ::R: RowMut, M::MatElement: Copy, M: Debug, { - fn from(value: &RgswCiphertext) -> Self { + fn from(value: &RgswCiphertext) -> Self { let mut data = M::zeros(value.d_a * 2 + value.d_b * 2, value.data.dimension().1); // copy RLWE'(-sm) @@ -247,7 +248,7 @@ where // Send polynomials to evaluation domain let ring_size = data.dimension().1; - let nttop = N::new(value.modulus, ring_size); + let nttop = N::new(&value.modulus, ring_size); data.iter_rows_mut() .for_each(|ri| nttop.forward(ri.as_mut())); @@ -286,17 +287,14 @@ impl AsRef<[M::R]> for RgswCiphertextEvaluationDomain } } -pub struct SeededRlweCiphertext -where - R: Row, -{ +pub struct SeededRlweCiphertext { pub(crate) data: R, pub(crate) seed: S, - pub(crate) modulus: R::Element, + pub(crate) modulus: Mod, } -impl SeededRlweCiphertext { - pub(crate) fn empty(ring_size: usize, seed: S, modulus: R::Element) -> Self { +impl SeededRlweCiphertext { + pub(crate) fn empty(ring_size: usize, seed: S, modulus: Mod) -> Self { SeededRlweCiphertext { data: R::zeros(ring_size), seed, @@ -360,20 +358,23 @@ impl IsTrivial for RlweCiphertext { } } -impl + MatrixMut, Rng: NewWithSeed> - From<&SeededRlweCiphertext> for RlweCiphertext +impl< + R: Row, + M: MatrixEntity + MatrixMut, + Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], Mod>, + Mod: Modulus, + > From<&SeededRlweCiphertext> for RlweCiphertext where Rng::Seed: Clone, - Rng: RandomUniformDist<[M::MatElement], Parameters = M::MatElement>, ::R: RowMut, R::Element: Copy, { - fn from(value: &SeededRlweCiphertext) -> Self { + fn from(value: &SeededRlweCiphertext) -> Self { let mut data = M::zeros(2, value.data.as_ref().len()); // sample a let mut p_rng = Rng::new_with_seed(value.seed.clone()); - RandomUniformDist::random_fill(&mut p_rng, &value.modulus, data.get_row_mut(0)); + RandomFillUniformInModulus::random_fill(&mut p_rng, &value.modulus, data.get_row_mut(0)); data.get_row_mut(1).copy_from_slice(value.data.as_ref()); @@ -413,7 +414,7 @@ pub struct RlwePublicKey { impl< M: MatrixMut + MatrixEntity, - Rng: NewWithSeed + RandomUniformDist<[M::MatElement], Parameters = M::MatElement>, + Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], M::MatElement>, > From<&SeededRlwePublicKey> for RlwePublicKey where ::R: RowMut, @@ -425,7 +426,7 @@ where // sample a let mut p_rng = Rng::new_with_seed(value.seed); - RandomUniformDist::random_fill(&mut p_rng, &value.modulus, data.get_row_mut(0)); + RandomFillUniformInModulus::random_fill(&mut p_rng, &value.modulus, data.get_row_mut(0)); // copy over b data.get_row_mut(1).copy_from_slice(value.data.as_ref()); @@ -956,10 +957,10 @@ pub(crate) fn rgsw_by_rgsw_inplace< pub(crate) fn secret_key_encrypt_rgsw< Mmut: MatrixMut + MatrixEntity, S, - R: RandomGaussianDist<[Mmut::MatElement], Parameters = Mmut::MatElement> - + RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>, - PR: RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>, - ModOp: VectorOps, + R: RandomFillGaussianInModulus<[Mmut::MatElement], ModOp::M> + + RandomFillUniformInModulus<[Mmut::MatElement], ModOp::M>, + PR: RandomFillUniformInModulus<[Mmut::MatElement], ModOp::M>, + ModOp: VectorOps + GetModulus, NttOp: Ntt, >( out_rgsw: &mut Mmut, @@ -972,8 +973,7 @@ pub(crate) fn secret_key_encrypt_rgsw< p_rng: &mut PR, rng: &mut R, ) where - ::R: - RowMut + RowEntity + TryConvertFrom<[S], Parameters = Mmut::MatElement> + Debug, + ::R: RowMut + RowEntity + TryConvertFrom1<[S], ModOp::M> + Debug, Mmut::MatElement: Copy + Debug, { let d_a = gadget_a.len(); @@ -1000,7 +1000,7 @@ pub(crate) fn secret_key_encrypt_rgsw< ) .for_each(|(ai, bi, beta_i)| { // Sample a_i - RandomUniformDist::random_fill(rng, &q, ai.as_mut()); + RandomFillUniformInModulus::random_fill(rng, &q, ai.as_mut()); // a_i * s scratch_space.as_mut().copy_from_slice(ai.as_ref()); @@ -1009,7 +1009,7 @@ pub(crate) fn secret_key_encrypt_rgsw< ntt_op.backward(scratch_space.as_mut()); // b_i = e_i + a_i * s - RandomGaussianDist::random_fill(rng, &q, bi.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, bi.as_mut()); mod_op.elwise_add_mut(bi.as_mut(), scratch_space.as_ref()); // a_i + \beta_i * m @@ -1022,7 +1022,7 @@ pub(crate) fn secret_key_encrypt_rgsw< // polynomials of part A of RLWE'(m) are sampled from seed let mut a = Mmut::zeros(d_b, ring_size); a.iter_rows_mut() - .for_each(|ai| RandomUniformDist::random_fill(p_rng, &q, ai.as_mut())); + .for_each(|ai| RandomFillUniformInModulus::random_fill(p_rng, &q, ai.as_mut())); a }; @@ -1041,7 +1041,7 @@ pub(crate) fn secret_key_encrypt_rgsw< mod_op.elwise_scalar_mul(scratch_space.as_mut(), m.as_ref(), beta_i); // Sample e_i - RandomGaussianDist::random_fill(rng, &q, bi.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, bi.as_mut()); // e_i + beta_i * m + ai*s mod_op.elwise_add_mut(bi.as_mut(), scratch_space.as_ref()); mod_op.elwise_add_mut(bi.as_mut(), ai.as_ref()); @@ -1051,10 +1051,10 @@ pub(crate) fn secret_key_encrypt_rgsw< pub(crate) fn public_key_encrypt_rgsw< Mmut: MatrixMut + MatrixEntity, M: Matrix, - R: RandomGaussianDist<[Mmut::MatElement], Parameters = Mmut::MatElement> - + RandomUniformDist<[u8], Parameters = u8> - + RandomUniformDist, - ModOp: VectorOps, + R: RandomFillGaussianInModulus<[Mmut::MatElement], ModOp::M> + + RandomFill<[u8]> + + RandomElementInModulus, + ModOp: VectorOps + GetModulus, NttOp: Ntt, >( out_rgsw: &mut Mmut, @@ -1066,7 +1066,7 @@ pub(crate) fn public_key_encrypt_rgsw< ntt_op: &NttOp, rng: &mut R, ) where - ::R: RowMut + RowEntity + TryConvertFrom<[i32], Parameters = Mmut::MatElement>, + ::R: RowMut + RowEntity + TryConvertFrom1<[i32], ModOp::M>, Mmut::MatElement: Copy, { let ring_size = public_key.dimension().1; @@ -1113,8 +1113,8 @@ pub(crate) fn public_key_encrypt_rgsw< ntt_op.backward(u_eval_copy.as_mut()); // sample error - RandomGaussianDist::random_fill(rng, &q, ai.as_mut()); - RandomGaussianDist::random_fill(rng, &q, bi.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, ai.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, bi.as_mut()); // a = p0*u+e0 mod_op.elwise_add_mut(ai.as_mut(), u_eval.as_ref()); @@ -1152,8 +1152,8 @@ pub(crate) fn public_key_encrypt_rgsw< ntt_op.backward(u_eval_copy.as_mut()); // sample error - RandomGaussianDist::random_fill(rng, &q, ai.as_mut()); - RandomGaussianDist::random_fill(rng, &q, bi.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, ai.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, bi.as_mut()); // a = p0*u+e0 mod_op.elwise_add_mut(ai.as_mut(), u_eval.as_ref()); @@ -1182,10 +1182,12 @@ pub(crate) fn public_key_encrypt_rgsw< /// - to_s: secret polynomial to key switch to. pub(crate) fn rlwe_ksk_gen< Mmut: MatrixMut + MatrixEntity, - ModOp: ArithmeticOps + VectorOps, + ModOp: ArithmeticOps + + VectorOps + + GetModulus, NttOp: Ntt, - R: RandomGaussianDist<[Mmut::MatElement], Parameters = Mmut::MatElement>, - PR: RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>, + R: RandomFillGaussianInModulus<[Mmut::MatElement], ModOp::M>, + PR: RandomFillUniformInModulus<[Mmut::MatElement], ModOp::M>, >( ksk_out: &mut Mmut, neg_from_s: Mmut::R, @@ -1202,7 +1204,7 @@ pub(crate) fn rlwe_ksk_gen< let d = gadget_vector.len(); assert!(ksk_out.dimension() == (d, ring_size)); - let q = ArithmeticOps::modulus(mod_op); + let q = mod_op.modulus(); ntt_op.forward(to_s.as_mut()); @@ -1210,7 +1212,7 @@ pub(crate) fn rlwe_ksk_gen< let mut part_a = { let mut a = Mmut::zeros(d, ring_size); a.iter_rows_mut() - .for_each(|ai| RandomUniformDist::random_fill(p_rng, &q, ai.as_mut())); + .for_each(|ai| RandomFillUniformInModulus::random_fill(p_rng, q, ai.as_mut())); a }; izip!( @@ -1225,7 +1227,7 @@ pub(crate) fn rlwe_ksk_gen< ntt_op.backward(ai.as_mut()); // ei + to_s*ai - RandomGaussianDist::random_fill(rng, &q, bi.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, bi.as_mut()); mod_op.elwise_add_mut(bi.as_mut(), ai.as_ref()); // beta_i * -from_s @@ -1239,11 +1241,13 @@ pub(crate) fn rlwe_ksk_gen< pub(crate) fn galois_key_gen< Mmut: MatrixMut + MatrixEntity, - ModOp: ArithmeticOps + VectorOps, + ModOp: ArithmeticOps + + VectorOps + + GetModulus, NttOp: Ntt, S, - R: RandomGaussianDist<[Mmut::MatElement], Parameters = Mmut::MatElement>, - PR: RandomUniformDist<[Mmut::MatElement], Parameters = Mmut::MatElement>, + R: RandomFillGaussianInModulus<[Mmut::MatElement], ModOp::M>, + PR: RandomFillUniformInModulus<[Mmut::MatElement], ModOp::M>, >( ksk_out: &mut Mmut, s: &[S], @@ -1255,23 +1259,23 @@ pub(crate) fn galois_key_gen< rng: &mut R, ) where ::R: RowMut, - Mmut::R: TryConvertFrom<[S], Parameters = Mmut::MatElement> + RowEntity, + Mmut::R: TryConvertFrom1<[S], ModOp::M> + RowEntity, Mmut::MatElement: Copy + Sub, { let ring_size = s.len(); let (auto_map_index, auto_map_sign) = generate_auto_map(ring_size, auto_k); - let q = ArithmeticOps::modulus(mod_op); + let q = mod_op.modulus(); // s(X) -> -s(X^k) - let s = Mmut::R::try_convert_from(s, &q); + let s = Mmut::R::try_convert_from(s, q); let mut neg_s_auto = Mmut::R::zeros(s.as_ref().len()); izip!(s.as_ref(), auto_map_index.iter(), auto_map_sign.iter()).for_each( |(el, to_index, sign)| { // if sign is +ve (true), then negate because we need -s(X) (i.e. do the // opposite than the usual case) if *sign { - neg_s_auto.as_mut()[*to_index] = q - *el; + neg_s_auto.as_mut()[*to_index] = mod_op.neg(el); } else { neg_s_auto.as_mut()[*to_index] = *el; } @@ -1298,11 +1302,11 @@ pub(crate) fn galois_key_gen< /// second rows consting polynomial `b` pub(crate) fn secret_key_encrypt_rlwe< Ro: Row + RowMut + RowEntity, - ModOp: VectorOps, + ModOp: VectorOps + GetModulus, NttOp: Ntt, S, - R: RandomGaussianDist<[Ro::Element], Parameters = Ro::Element>, - PR: RandomUniformDist<[Ro::Element], Parameters = Ro::Element>, + R: RandomFillGaussianInModulus<[Ro::Element], ModOp::M>, + PR: RandomFillUniformInModulus<[Ro::Element], ModOp::M>, >( m: &[Ro::Element], b_rlwe_out: &mut Ro, @@ -1312,7 +1316,7 @@ pub(crate) fn secret_key_encrypt_rlwe< p_rng: &mut PR, rng: &mut R, ) where - Ro: TryConvertFrom<[S], Parameters = Ro::Element> + Debug, + Ro: TryConvertFrom1<[S], ModOp::M> + Debug, { let ring_size = s.len(); assert!(m.as_ref().len() == ring_size); @@ -1323,19 +1327,19 @@ pub(crate) fn secret_key_encrypt_rlwe< // sample a let mut a = { let mut a = Ro::zeros(ring_size); - RandomUniformDist::random_fill(p_rng, &q, a.as_mut()); + RandomFillUniformInModulus::random_fill(p_rng, q, a.as_mut()); a }; // s * a - let mut sa = Ro::try_convert_from(s, &q); + let mut sa = Ro::try_convert_from(s, q); ntt_op.forward(sa.as_mut()); ntt_op.forward(a.as_mut()); mod_op.elwise_mul_mut(sa.as_mut(), a.as_ref()); ntt_op.backward(sa.as_mut()); // sample e - RandomGaussianDist::random_fill(rng, &q, b_rlwe_out.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, q, b_rlwe_out.as_mut()); mod_op.elwise_add_mut(b_rlwe_out.as_mut(), m.as_ref()); mod_op.elwise_add_mut(b_rlwe_out.as_mut(), sa.as_ref()); } @@ -1343,13 +1347,13 @@ pub(crate) fn secret_key_encrypt_rlwe< pub(crate) fn public_key_encrypt_rlwe< M: Matrix, Mmut: MatrixMut, - ModOp: VectorOps, + ModOp: VectorOps + GetModulus, NttOp: Ntt, S, - R: RandomGaussianDist<[M::MatElement], Parameters = M::MatElement> - + RandomUniformDist<[M::MatElement], Parameters = M::MatElement> - + RandomUniformDist<[u8], Parameters = u8> - + RandomUniformDist, + R: RandomFillGaussianInModulus<[M::MatElement], ModOp::M> + + RandomFillUniformInModulus<[M::MatElement], ModOp::M> + + RandomFill<[u8]> + + RandomElementInModulus, >( rlwe_out: &mut Mmut, pk: &M, @@ -1358,7 +1362,7 @@ pub(crate) fn public_key_encrypt_rlwe< ntt_op: &NttOp, rng: &mut R, ) where - ::R: RowMut + TryConvertFrom<[S], Parameters = M::MatElement> + RowEntity, + ::R: RowMut + TryConvertFrom1<[S], ModOp::M> + RowEntity, M::MatElement: Copy, S: Zero + Signed + Copy, { @@ -1369,7 +1373,7 @@ pub(crate) fn public_key_encrypt_rlwe< let mut u = vec![S::zero(); ring_size]; fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng); - let mut u = Mmut::R::try_convert_from(&u, &q); + let mut u = Mmut::R::try_convert_from(&u, q); ntt_op.forward(u.as_mut()); let mut ua = Mmut::R::zeros(ring_size); @@ -1389,7 +1393,7 @@ pub(crate) fn public_key_encrypt_rlwe< // sample error rlwe_out.iter_rows_mut().for_each(|ri| { - RandomGaussianDist::random_fill(rng, &q, ri.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, ri.as_mut()); }); // a*u + e0 @@ -1405,10 +1409,10 @@ pub(crate) fn public_key_encrypt_rlwe< pub(crate) fn gen_rlwe_public_key< Ro: RowMut + RowEntity, S, - ModOp: VectorOps, + ModOp: VectorOps + GetModulus, NttOp: Ntt, - PRng: RandomUniformDist<[Ro::Element], Parameters = Ro::Element>, - Rng: RandomGaussianDist<[Ro::Element], Parameters = Ro::Element>, + PRng: RandomFillUniformInModulus<[Ro::Element], ModOp::M>, + Rng: RandomFillGaussianInModulus<[Ro::Element], ModOp::M>, >( part_b_out: &mut Ro, s: &[S], @@ -1417,7 +1421,7 @@ pub(crate) fn gen_rlwe_public_key< p_rng: &mut PRng, rng: &mut Rng, ) where - Ro: TryConvertFrom<[S], Parameters = Ro::Element>, + Ro: TryConvertFrom1<[S], ModOp::M>, { let ring_size = s.len(); assert!(part_b_out.as_ref().len() == ring_size); @@ -1427,7 +1431,7 @@ pub(crate) fn gen_rlwe_public_key< // sample a let mut a = { let mut tmp = Ro::zeros(ring_size); - RandomUniformDist::random_fill(p_rng, &q, tmp.as_mut()); + RandomFillUniformInModulus::random_fill(p_rng, &q, tmp.as_mut()); tmp }; ntt_op.forward(a.as_mut()); @@ -1439,7 +1443,7 @@ pub(crate) fn gen_rlwe_public_key< ntt_op.backward(sa.as_mut()); // s*a + e - RandomGaussianDist::random_fill(rng, &q, part_b_out.as_mut()); + RandomFillGaussianInModulus::random_fill(rng, &q, part_b_out.as_mut()); mod_op.elwise_add_mut(part_b_out.as_mut(), sa.as_ref()); } @@ -1449,7 +1453,7 @@ pub(crate) fn gen_rlwe_public_key< pub(crate) fn decrypt_rlwe< R: RowMut, M: Matrix, - ModOp: VectorOps, + ModOp: VectorOps + GetModulus, NttOp: Ntt, S, >( @@ -1459,7 +1463,7 @@ pub(crate) fn decrypt_rlwe< ntt_op: &NttOp, mod_op: &ModOp, ) where - R: TryConvertFrom<[S], Parameters = R::Element>, + R: TryConvertFrom1<[S], ModOp::M>, R::Element: Copy, { let ring_size = s.len(); @@ -1471,7 +1475,7 @@ pub(crate) fn decrypt_rlwe< ntt_op.forward(m_out.as_mut()); // -s*a - let mut s = R::try_convert_from(&s, &mod_op.modulus()); + let mut s = R::try_convert_from(&s, mod_op.modulus()); ntt_op.forward(s.as_mut()); mod_op.elwise_mul_mut(m_out.as_mut(), s.as_ref()); mod_op.elwise_neg_mut(m_out.as_mut()); @@ -1485,7 +1489,7 @@ pub(crate) fn decrypt_rlwe< // encoded_m pub(crate) fn measure_noise< Mmut: MatrixMut + Matrix, - ModOp: VectorOps, + ModOp: VectorOps + GetModulus, NttOp: Ntt, S, >( @@ -1497,7 +1501,7 @@ pub(crate) fn measure_noise< ) -> f64 where ::R: RowMut, - Mmut::R: RowEntity + TryConvertFrom<[S], Parameters = Mmut::MatElement>, + Mmut::R: RowEntity + TryConvertFrom1<[S], ModOp::M>, Mmut::MatElement: PrimInt + ToPrimitive + Debug, { let ring_size = s.len(); @@ -1505,7 +1509,7 @@ where assert!(encoded_m_ideal.as_ref().len() == ring_size); // -(s * a) - let q = VectorOps::modulus(mod_op); + let q = mod_op.modulus(); let mut s = Mmut::R::try_convert_from(s, &q); ntt_op.forward(s.as_mut()); let mut a = Mmut::R::zeros(ring_size); @@ -1524,13 +1528,7 @@ where let mut max_diff_bits = f64::MIN; m_plus_e.as_ref().iter().for_each(|v| { - let mut v = *v; - if v >= (q >> 1) { - // v is -ve - v = q - v; - } - - let bits = (v.to_f64().unwrap()).log2(); + let bits = (q.to_i64(v).to_f64().unwrap()).log2(); if max_diff_bits < bits { max_diff_bits = bits; @@ -1548,16 +1546,16 @@ pub(crate) mod tests { use rand::{thread_rng, Rng}; use crate::{ - backend::{ModInit, ModularOpsU64, VectorOps}, + backend::{GetModulus, ModInit, ModularOpsU64, Modulus, VectorOps}, decomposer::{Decomposer, DefaultDecomposer, RlweDecomposer}, ntt::{self, Ntt, NttBackendU64, NttInit}, - random::{DefaultSecureRng, NewWithSeed, RandomUniformDist}, + random::{DefaultSecureRng, NewWithSeed, RandomFillUniformInModulus}, rgsw::{ gen_rlwe_public_key, measure_noise, public_key_encrypt_rgsw, AutoKeyEvaluationDomain, RgswCiphertext, RgswCiphertextEvaluationDomain, RlweCiphertext, RlwePublicKey, SeededAutoKey, SeededRgswCiphertext, SeededRlweCiphertext, SeededRlwePublicKey, }, - utils::{generate_prime, negacyclic_mul, Stats, TryConvertFrom}, + utils::{generate_prime, negacyclic_mul, Stats, TryConvertFrom1}, Matrix, Secret, }; @@ -1567,11 +1565,11 @@ pub(crate) mod tests { RlweSecret, }; - pub(crate) fn _sk_encrypt_rlwe( + pub(crate) fn _sk_encrypt_rlwe + Clone>( m: &[u64], s: &[i32], ntt_op: &NttBackendU64, - mod_op: &ModularOpsU64, + mod_op: &ModularOpsU64, ) -> RlweCiphertext>, DefaultSecureRng> { let ring_size = m.len(); let q = mod_op.modulus(); @@ -1581,7 +1579,7 @@ pub(crate) mod tests { let mut rlwe_seed = [0u8; 32]; rng.fill_bytes(&mut rlwe_seed); let mut seeded_rlwe_ct = - SeededRlweCiphertext::<_, [u8; 32]>::empty(ring_size as usize, rlwe_seed, q); + SeededRlweCiphertext::<_, [u8; 32], _>::empty(ring_size as usize, rlwe_seed, q.clone()); let mut p_rng = DefaultSecureRng::new_seeded(rlwe_seed); secret_key_encrypt_rlwe( &m, @@ -1597,13 +1595,13 @@ pub(crate) mod tests { } // Encrypt m as RGSW ciphertext RGSW(m) using supplied public key - pub(crate) fn _pk_encrypt_rgsw( + pub(crate) fn _pk_encrypt_rgsw + Clone>( m: &[u64], public_key: &RlwePublicKey>, DefaultSecureRng>, decomposer: &(DefaultDecomposer, DefaultDecomposer), - mod_op: &ModularOpsU64, + mod_op: &ModularOpsU64, ntt_op: &NttBackendU64, - ) -> RgswCiphertext>> { + ) -> RgswCiphertext>, T> { let (_, ring_size) = Matrix::dimension(&public_key.data); let gadget_vector_a = decomposer.a().gadget_vector(); let gadget_vector_b = decomposer.b().gadget_vector(); @@ -1613,7 +1611,7 @@ pub(crate) mod tests { assert!(m.len() == ring_size); // public key encrypt RGSW(m1) - let mut rgsw_ct = RgswCiphertext::empty(ring_size, decomposer, mod_op.modulus()); + let mut rgsw_ct = RgswCiphertext::empty(ring_size, decomposer, mod_op.modulus().clone()); public_key_encrypt_rgsw( &mut rgsw_ct.data, m, @@ -1630,13 +1628,13 @@ pub(crate) mod tests { /// Encrypts m as RGSW ciphertext RGSW(m) using supplied secret key. Returns /// unseeded RGSW ciphertext in coefficient domain - pub(crate) fn _sk_encrypt_rgsw( + pub(crate) fn _sk_encrypt_rgsw + Clone>( m: &[u64], s: &[i32], decomposer: &(DefaultDecomposer, DefaultDecomposer), - mod_op: &ModularOpsU64, + mod_op: &ModularOpsU64, ntt_op: &NttBackendU64, - ) -> SeededRgswCiphertext>, [u8; 32]> { + ) -> SeededRgswCiphertext>, [u8; 32], T> { let ring_size = s.len(); assert!(m.len() == s.len()); @@ -1648,11 +1646,11 @@ pub(crate) mod tests { let mut rng = DefaultSecureRng::new(); let mut rgsw_seed = [0u8; 32]; rng.fill_bytes(&mut rgsw_seed); - let mut seeded_rgsw_ct = SeededRgswCiphertext::>, [u8; 32]>::empty( + let mut seeded_rgsw_ct = SeededRgswCiphertext::>, [u8; 32], T>::empty( ring_size as usize, decomposer, rgsw_seed, - q, + q.clone(), ); let mut p_rng = DefaultSecureRng::new_seeded(rgsw_seed); secret_key_encrypt_rgsw( @@ -1672,12 +1670,12 @@ pub(crate) mod tests { /// Prints noise in RGSW ciphertext RGSW(m). /// /// - rgsw_ct: RGSW ciphertext in coefficient domain - pub(crate) fn _measure_noise_rgsw( + pub(crate) fn _measure_noise_rgsw + Clone>( rgsw_ct: &[Vec], m: &[u64], s: &[i32], decomposer: &(DefaultDecomposer, DefaultDecomposer), - q: u64, + q: &T, ) { let gadget_vector_a = decomposer.a().gadget_vector(); let gadget_vector_b = decomposer.b().gadget_vector(); @@ -1687,14 +1685,15 @@ pub(crate) mod tests { assert!(Matrix::dimension(&rgsw_ct) == (d_a * 2 + d_b * 2, ring_size)); assert!(m.len() == ring_size); - let mod_op = ModularOpsU64::new(q); + let mod_op = ModularOpsU64::new(q.clone()); let ntt_op = NttBackendU64::new(q, ring_size); - let mul_mod = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q as u128) as u64; - let s_poly = Vec::::try_convert_from(s, &q); + let mul_mod = + |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q.q().unwrap() as u128) as u64; + let s_poly = Vec::::try_convert_from(s, q); let mut neg_s = s_poly.clone(); mod_op.elwise_neg_mut(neg_s.as_mut()); - let neg_sm0m1 = negacyclic_mul(&neg_s, &m, mul_mod, q); + let neg_sm0m1 = negacyclic_mul(&neg_s, &m, mul_mod, q.q().unwrap()); // RLWE(\beta^j -s * m) for j in 0..d_a { @@ -1745,9 +1744,9 @@ pub(crate) mod tests { // sample m0 let mut m0 = vec![0u64; ring_size as usize]; - RandomUniformDist::<[u64]>::random_fill(&mut rng, &(1u64 << logp), m0.as_mut_slice()); + RandomFillUniformInModulus::<[u64], u64>::random_fill(&mut rng, &(1u64 << logp), m0.as_mut_slice()); - let ntt_op = NttBackendU64::new(q, ring_size as usize); + let ntt_op = NttBackendU64::new(&q, ring_size as usize); let mod_op = ModularOpsU64::new(q); // encrypt m0 @@ -1788,11 +1787,11 @@ pub(crate) mod tests { let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize); let mut m0 = vec![0u64; ring_size as usize]; - RandomUniformDist::<[u64]>::random_fill(&mut rng, &(1u64 << logp), m0.as_mut_slice()); + RandomFillUniformInModulus::<[u64], _>::random_fill(&mut rng, &(1u64 << logp), m0.as_mut_slice()); let mut m1 = vec![0u64; ring_size as usize]; m1[thread_rng().gen_range(0..ring_size) as usize] = 1; - let ntt_op = NttBackendU64::new(q, ring_size as usize); + let ntt_op = NttBackendU64::new(&q, ring_size as usize); let mod_op = ModularOpsU64::new(q); let d_rgsw = 10; let logb = 5; @@ -1911,13 +1910,13 @@ pub(crate) mod tests { let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize); let mut m = vec![0u64; ring_size as usize]; - RandomUniformDist::random_fill(&mut rng, &p, m.as_mut_slice()); + RandomFillUniformInModulus::random_fill(&mut rng, &p, m.as_mut_slice()); let encoded_m = m .iter() .map(|v| (((*v as f64 * q as f64) / (p as f64)).round() as u64)) .collect_vec(); - let ntt_op = NttBackendU64::new(q, ring_size as usize); + let ntt_op = NttBackendU64::new(&q, ring_size as usize); let mod_op = ModularOpsU64::new(q); // RLWE_{s}(m) @@ -2029,7 +2028,7 @@ pub(crate) mod tests { let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize); let mut rng = DefaultSecureRng::new(); - let ntt_op = NttBackendU64::new(q, ring_size as usize); + let ntt_op = NttBackendU64::new(&q, ring_size as usize); let mod_op = ModularOpsU64::new(q); let decomposer = ( DefaultDecomposer::new(q, logb, d_rgsw), @@ -2065,7 +2064,7 @@ pub(crate) mod tests { ) ]; - _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, q); + _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q); for i in 0..2 { let mut m = vec![0u64; ring_size as usize]; @@ -2085,12 +2084,12 @@ pub(crate) mod tests { // measure noise carry_m = negacyclic_mul(&carry_m, &m, mul_mod, q); println!("########### Noise RGSW(carrym) in {i}^th loop ###########"); - _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, q); + _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q); } { // RLWE(m) x RGSW(carry_m) let mut m = vec![0u64; ring_size as usize]; - RandomUniformDist::random_fill(&mut rng, &q, m.as_mut_slice()); + RandomFillUniformInModulus::random_fill(&mut rng, &q, m.as_mut_slice()); let mut rlwe_ct = _sk_encrypt_rlwe(&m, s.values(), &ntt_op, &mod_op); // send rgsw to evaluation domain @@ -2124,7 +2123,7 @@ pub(crate) mod tests { DefaultDecomposer::new(q, logb, d_rgsw), ); - let ntt_op = NttBackendU64::new(q, ring_size as usize); + let ntt_op = NttBackendU64::new(&q, ring_size as usize); let mod_op = ModularOpsU64::new(q); let mut rng = DefaultSecureRng::new_seeded([0u8; 32]); @@ -2180,7 +2179,7 @@ pub(crate) mod tests { // Sample m2, encrypt it as RLWE(m2) and multiply RLWE(m2)xRGSW(m0m1) let mut m2 = vec![0u64; ring_size as usize]; - RandomUniformDist::random_fill(&mut rng, &q, m2.as_mut_slice()); + RandomFillUniformInModulus::random_fill(&mut rng, &q, m2.as_mut_slice()); let mut rlwe_in_ct = { _sk_encrypt_rlwe(&m2, s.values(), &ntt_op, &mod_op) }; let mut scratch_space = vec![ vec![0u64; ring_size as usize]; diff --git a/src/utils.rs b/src/utils.rs index 2d29fb7..cd68a11 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,9 +1,12 @@ use std::{fmt::Debug, usize}; use itertools::Itertools; -use num_traits::{FromPrimitive, PrimInt, Signed}; +use num_traits::{FromPrimitive, PrimInt, Signed, Unsigned}; -use crate::RandomUniformDist; +use crate::{ + backend::Modulus, + random::{RandomElement, RandomElementInModulus, RandomFill}, +}; pub trait WithLocal { fn with_local(func: F) -> R where @@ -16,22 +19,21 @@ pub trait WithLocal { pub fn fill_random_ternary_secret_with_hamming_weight< T: Signed, - R: RandomUniformDist<[u8], Parameters = u8> + RandomUniformDist, + R: RandomFill<[u8]> + RandomElementInModulus, >( out: &mut [T], hamming_weight: usize, rng: &mut R, ) { let mut bytes = vec![0u8; hamming_weight.div_ceil(8)]; - RandomUniformDist::<[u8]>::random_fill(rng, &0, &mut bytes); + RandomFill::<[u8]>::random_fill(rng, &mut bytes); let size = out.len(); let mut secret_indices = (0..size).into_iter().map(|i| i).collect_vec(); let mut bit_index = 0; let mut byte_index = 0; for _ in 0..hamming_weight { - let mut s_index = 0usize; - RandomUniformDist::::random_fill(rng, &secret_indices.len(), &mut s_index); + let s_index = RandomElementInModulus::::random(rng, &secret_indices.len()); let curr_bit = (bytes[byte_index] >> bit_index) & 1; if curr_bit == 1 { @@ -138,97 +140,122 @@ pub fn negacyclic_mul T>( return r; } -pub trait TryConvertFrom { - type Parameters: ?Sized; - - fn try_convert_from(value: &T, parameters: &Self::Parameters) -> Self; +pub trait TryConvertFrom1 { + fn try_convert_from(value: &T, parameters: &P) -> Self; } -impl TryConvertFrom<[i32]> for Vec> { - type Parameters = u32; - fn try_convert_from(value: &[i32], parameters: &Self::Parameters) -> Self { - let row0 = value - .iter() - .map(|v| { - let is_neg = v.is_negative(); - let v_u32 = v.abs() as u32; - - assert!(v_u32 < *parameters); - - if is_neg { - parameters - v_u32 - } else { - v_u32 - } - }) - .collect_vec(); - - vec![row0] - } -} - -impl TryConvertFrom<[i32]> for Vec> { - type Parameters = u64; - fn try_convert_from(value: &[i32], parameters: &Self::Parameters) -> Self { - let row0 = value - .iter() - .map(|v| { - let is_neg = v.is_negative(); - let v_u64 = v.abs() as u64; - - assert!(v_u64 < *parameters); - - if is_neg { - parameters - v_u64 - } else { - v_u64 - } - }) - .collect_vec(); - - vec![row0] +impl> TryConvertFrom1<[i64], P> for Vec { + fn try_convert_from(value: &[i64], parameters: &P) -> Self { + value.iter().map(|v| parameters.from_i64(*v)).collect_vec() } } -impl TryConvertFrom<[i32]> for Vec { - type Parameters = u64; - fn try_convert_from(value: &[i32], parameters: &Self::Parameters) -> Self { +impl> TryConvertFrom1<[i32], P> for Vec { + fn try_convert_from(value: &[i32], parameters: &P) -> Self { value .iter() - .map(|v| { - let is_neg = v.is_negative(); - let v_u64 = v.abs() as u64; - - assert!(v_u64 < *parameters); - - if is_neg { - parameters - v_u64 - } else { - v_u64 - } - }) + .map(|v| parameters.from_i64(*v as i64)) .collect_vec() } } -impl TryConvertFrom<[u64]> for Vec { - type Parameters = u64; - fn try_convert_from(value: &[u64], parameters: &Self::Parameters) -> Self { - let q = *parameters; - let qby2 = q / 2; - value - .iter() - .map(|v| { - if *v > qby2 { - -((q - v) as i64) - } else { - *v as i64 - } - }) - .collect_vec() +impl TryConvertFrom1<[P::Element], P> for Vec { + fn try_convert_from(value: &[P::Element], parameters: &P) -> Self { + value.iter().map(|v| parameters.to_i64(v)).collect_vec() } } +// pub trait TryConvertFrom { +// type Parameters: ?Sized; + +// fn try_convert_from(value: &T, parameters: &Self::Parameters) -> Self; +// } + +// impl TryConvertFrom1<[i32]> for Vec> { +// type Parameters = u32; +// fn try_convert_from(value: &[i32], parameters: &Self::Parameters) -> Self +// { let row0 = value +// .iter() +// .map(|v| { +// let is_neg = v.is_negative(); +// let v_u32 = v.abs() as u32; + +// assert!(v_u32 < *parameters); + +// if is_neg { +// parameters - v_u32 +// } else { +// v_u32 +// } +// }) +// .collect_vec(); + +// vec![row0] +// } +// } + +// impl TryConvertFrom1<[i32]> for Vec> { +// type Parameters = u64; +// fn try_convert_from(value: &[i32], parameters: &Self::Parameters) -> Self +// { let row0 = value +// .iter() +// .map(|v| { +// let is_neg = v.is_negative(); +// let v_u64 = v.abs() as u64; + +// assert!(v_u64 < *parameters); + +// if is_neg { +// parameters - v_u64 +// } else { +// v_u64 +// } +// }) +// .collect_vec(); + +// vec![row0] +// } +// } + +// impl TryConvertFrom1<[i32]> for Vec { +// type Parameters = u64; +// fn try_convert_from(value: &[i32], parameters: &Self::Parameters) -> Self +// { value +// .iter() +// .map(|v| { +// let is_neg = v.is_negative(); +// let v_u64 = v.abs() as u64; + +// assert!(v_u64 < *parameters); + +// if is_neg { +// parameters - v_u64 +// } else { +// v_u64 +// } +// }) +// .collect_vec() +// } +// } + +// impl TryConvertFrom1<[u64]> for Vec { +// type Parameters = u64; +// fn try_convert_from(value: &[u64], parameters: &Self::Parameters) -> Self +// { let q = *parameters; +// let qby2 = q / 2; +// value +// .iter() +// .map(|v| { +// if *v > qby2 { +// -((q - v) as i64) +// } else { +// *v as i64 +// } +// }) +// .collect_vec() +// } +// } + pub(crate) struct Stats { pub(crate) samples: Vec, }