polish & clean a bit
jump to
@@ -19,27 +19,30 @@ work for using CKKS & BFV, the only thing to be changed would be the parameters
and the line `type S = TWLE<K>` to use `CKKS<Q, N>` or `BFV<Q, N, T>`. ```rust -const T: u64 = 128; // msg space (msg modulus) -type M = Rq<T, 1>; // msg space -type S = TLWE<256>; +let param = Param { + err_sigma: crate::ERR_SIGMA, + ring: RingParam { q: u64::MAX, n: 1 }, + k: 256, + t: 128, // plaintext modulus +}; let mut rng = rand::thread_rng(); -let msg_dist = Uniform::new(0_u64, T); +let msg_dist = Uniform::new(0_u64, param.t); -let (sk, pk) = S::new_key(&mut rng)?; +let (sk, pk) = TLWE::new_key(&mut rng, ¶m)?; // get two random msgs in Z_t -let m1 = M::rand_u64(&mut rng, msg_dist)?; -let m2 = M::rand_u64(&mut rng, msg_dist)?; -let m3 = M::rand_u64(&mut rng, msg_dist)?; +let m1 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; +let m2 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; +let m3 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; // encode the msgs into the plaintext space -let p1 = S::encode::<T>(&m1); // plaintext -let p2 = S::encode::<T>(&m2); // plaintext -let c3_const: Tn<1> = Tn(array::from_fn(|i| T64(m3.coeffs()[i].0))); // encode it as constant +let p1 = TLWE::encode(¶m, &m1); // plaintext +let p2 = TLWE::encode(¶m, &m2); // plaintext +let c3_const: T64 = T64(m3.coeffs()[0].v); // encode it as constant -let c1 = S::encrypt(&mut rng, &pk, &p1)?; -let c2 = S::encrypt(&mut rng, &pk, &p2)?; +let c1 = TLWE::encrypt(&mut rng, ¶m, &pk, &p1)?; +let c2 = TLWE::encrypt(&mut rng, ¶m, &pk, &p2)?; // now we can do encrypted operations (notice that we do them using simple // operation notation by rust's operator overloading):@@ -48,9 +51,10 @@ let c4 = c_12 * c3_const;
// decrypt & decode let p4_recovered = c4.decrypt(&sk); -let m4 = S::decode::<T>(&p4_recovered); +let m4 = TLWE::decode(¶m, &p4_recovered); // m4 is equal to (m1+m2)*m3 +assert_eq!(((m1 + m2).to_r() * m3.to_r()).to_rq(param.t), m4); ```
@@ -18,18 +18,12 @@
static CACHE: OnceLock<Mutex<HashMap<(u64, usize), (Vec<Zq>, Vec<Zq>, Zq)>>> = OnceLock::new(); fn roots(q: u64, n: usize) -> (Vec<Zq>, Vec<Zq>, Zq) { - // Initialize CACHE with an empty HashMap on first use let cache_lock = CACHE.get_or_init(|| Mutex::new(HashMap::new())); - - // Lock the HashMap for this thread let mut cache = cache_lock.lock().unwrap(); - if let Some(value) = cache.get(&(q, n)) { - // Found an existing value — return a clone return value.clone(); } - // Not found — compute the new triple let n_inv: Zq = Zq { q, v: const_inv_mod(q, n as u64),@@ -37,10 +31,8 @@ };
let root_of_unity: u64 = primitive_root_of_unity(q, 2 * n); let roots_of_unity: Vec<Zq> = roots_of_unity(q, n, root_of_unity); let roots_of_unity_inv: Vec<Zq> = roots_of_unity_inv(q, n, roots_of_unity.clone()); - let value = (roots_of_unity, roots_of_unity_inv, n_inv); - // Store and return cache.insert((q, n), value.clone()); value }@@ -71,7 +63,8 @@ }
t /= 2; m *= 2; } - // Rq::from_vec((a.q, n), r) + // TODO think if maybe not return a Rq type, or if returned Rq, maybe + // fill the `evals` field, which is what we're actually returning here Rq { param: RingParam { q, n }, coeffs: r,@@ -107,10 +100,11 @@ }
for i in 0..n { r[i] = r[i] * n_inv; } - // Rq::from_vec((a.q, n), r) Rq { param: RingParam { q, n }, coeffs: r, + // TODO maybe at `evals` place the inputed `a` which is the evals + // format evals: None, } }@@ -238,27 +232,4 @@ assert_eq!(a, a_intt);
} Ok(()) } - - // #[test] - // fn test_ntt_loop_2() -> Result<()> { - // // let q: u64 = 2u64.pow(16) + 1; - // // let n: usize = 512; - // let q: u64 = 35184371138561; - // let n: usize = 1 << 14; - // let param = RingParam { q, n }; - // - // use rand::distributions::Uniform; - // let mut rng = rand::thread_rng(); - // let dist = Uniform::new(0_f64, q as f64); - // - // let a: Rq = Rq::rand(&mut rng, dist, ¶m); - // let start = std::time::Instant::now(); - // for _ in 0..10_000 { - // let a_ntt = NTT::ntt(&a); - // let a_intt = NTT::intt(&a_ntt); - // assert_eq!(a, a_intt); - // } - // dbg!(start.elapsed()); - // Ok(()) - // } }
@@ -33,10 +33,6 @@ + Sum<<Self as Mul>::Output>
{ /// C defines the coefficient type type C: Debug + Clone; - // type Param: Debug+Clone+Copy; - - // const Q: u64; - // const N: usize; fn param(&self) -> RingParam; fn coeffs(&self) -> Vec<Self::C>;
@@ -15,14 +15,7 @@ pub n: usize,
pub coeffs: Vec<i64>, } -// impl<const N: usize> Ring for R<N> { impl R { - // type C = i64; - // type Param = usize; // n - - // const Q: u64 = i64::MAX as u64; // WIP - // const N: usize = N; - pub fn coeffs(&self) -> Vec<i64> { self.coeffs.clone() }@@ -33,16 +26,12 @@ coeffs: vec![0i64; n],
} } fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, n: usize) -> Self { - // let coeffs: [i64; N] = array::from_fn(|_| Self::C::rand(&mut rng, &dist)); - // let coeffs: [i64; N] = array::from_fn(|_| dist.sample(&mut rng).round() as i64); Self { n, coeffs: std::iter::repeat_with(|| dist.sample(&mut rng).round() as i64) .take(n) .collect(), } - // let coeffs: [C; N] = array::from_fn(|_| Zq::from_u64(dist.sample(&mut rng))); - // Self(coeffs) } pub fn from_vec(n: usize, coeffs: Vec<i64>) -> Self {@@ -90,9 +79,6 @@ }
} impl R { - // pub fn coeffs(&self) -> [i64; N] { - // self.0 - // } pub fn to_rq(self, q: u64) -> crate::Rq { crate::Rq::from((q, self)) }@@ -196,7 +182,6 @@ impl Add<&R> for &R {
type Output = R; fn add(self, rhs: &R) -> Self::Output { - // R(array::from_fn(|i| self.0[i] + rhs.0[i])) assert_eq!(self.n, rhs.n); R { n: self.n,@@ -220,11 +205,6 @@ fn sum<I>(mut iter: I) -> Self
where I: Iterator<Item = Self>, { - // let mut acc = R::zero(); - // for e in iter { - // acc += e; - // } - // acc let first = iter.next().unwrap(); iter.fold(first, |acc, x| acc + x) }@@ -234,7 +214,6 @@ impl Sub<R> for R {
type Output = Self; fn sub(self, rhs: Self) -> Self { - // Self(array::from_fn(|i| self.0[i] - rhs.0[i])) assert_eq!(self.n, rhs.n); Self { n: self.n,@@ -248,7 +227,6 @@ impl Sub<&R> for &R {
type Output = R; fn sub(self, rhs: &R) -> Self::Output { - // R(array::from_fn(|i| self.0[i] - rhs.0[i])) assert_eq!(self.n, rhs.n); R { n: self.n,
@@ -13,9 +13,6 @@ use crate::zq::{modulus_u64, Zq};
use crate::{Ring, RingParam}; -// NOTE: currently using fixed-size arrays, but pending to see if with -// real-world parameters the stack can keep up; if not will move everything to -// use Vec. /// PolynomialRing element, where the PolynomialRing is R = Z_q[X]/(X^n +1) /// The implementation assumes that q is prime. #[derive(Clone)]@@ -31,8 +28,6 @@ }
impl Ring for Rq { type C = Zq; - // type Param = (u64, usize); - // type Param = Param; fn param(&self) -> RingParam { self.param@@ -40,8 +35,6 @@ }
fn coeffs(&self) -> Vec<Self::C> { self.coeffs.to_vec() } - // fn zero(q: u64, n: usize) -> Self { - // fn zero(param: (u64, usize)) -> Self { fn zero(param: &RingParam) -> Self { Self { param: param.clone(),@@ -50,8 +43,6 @@ evals: None,
} } fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, param: &RingParam) -> Self { - // let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_u64(dist.sample(&mut rng))); - // let coeffs: [Zq<Q>; N] = array::from_fn(|_| Self::C::rand(&mut rng, &dist)); Self { param: param.clone(), coeffs: std::iter::repeat_with(|| Self::C::rand(&mut rng, &dist, param.q))@@ -93,23 +84,17 @@ let param = RingParam {
q: p, n: self.param.n, }; - // Rq::from_vec_u64(p, self.n, self.coeffs().iter().map(|m_i| m_i.v).collect()) Rq::from_vec_u64(¶m, self.coeffs().iter().map(|m_i| m_i.v).collect()) } /// perform the mod switch operation from Q to Q', where Q2=Q' - // fn mod_switch<const P: u64, const M: usize>(&self) -> impl Ring { fn mod_switch(&self, p: u64) -> Rq { let param = RingParam { q: p, n: self.param.n, }; - // assert_eq!(N, M); // sanity check Rq { param, - // q: p, - // n: self.n, - // coeffs: array::from_fn(|i| self.coeffs[i].mod_switch::<P>()), coeffs: self.coeffs.iter().map(|c_i| c_i.mod_switch(p)).collect(), evals: None, }@@ -135,7 +120,6 @@ assert_eq!(r.n, r.coeffs.len());
Self::from_vec( &RingParam { q, n: r.n }, - // (q, r.n), r.coeffs() .iter() .map(|e| Zq::from_f64(q, *e as f64))@@ -161,21 +145,13 @@ pub fn coeffs(&self) -> Vec<Zq> {
self.coeffs.clone() } pub fn compute_evals(&mut self) { - self.evals = Some(NTT::ntt(self).coeffs); // TODO improve, ntt returns Rq but here - // just needs Vec<Zq> + self.evals = Some(NTT::ntt(self).coeffs); + // TODO improve, ntt returns Rq but here just needs Vec<Zq> } pub fn to_r(self) -> crate::R { crate::R::from(self) } - // TODO rm since it is implemented in Ring trait impl - // pub fn zero() -> Self { - // let coeffs = array::from_fn(|_| Zq::zero()); - // Self { - // coeffs, - // evals: None, - // } - // } // this method is mostly for tests pub fn from_vec_u64(param: &RingParam, coeffs: Vec<u64>) -> Self { let coeffs_mod_q: Vec<Zq> = coeffs.iter().map(|c| Zq::from_u64(param.q, *c)).collect();@@ -204,14 +180,9 @@ pub fn rand_abs(
mut rng: impl Rng, dist: impl Distribution<f64>, param: &RingParam, - // q: u64, - // n: usize, ) -> Result<Self> { - // let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng).abs())); Ok(Self { param: *param, - // q, - // n, coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng).abs())) .take(param.n) .collect(),@@ -222,14 +193,9 @@ pub fn rand_f64_abs(
mut rng: impl Rng, dist: impl Distribution<f64>, param: &RingParam, - // q: u64, - // n: usize, ) -> Result<Self> { - // let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng).abs())); Ok(Self { param: *param, - // q, - // n, coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng).abs())) .take(param.n) .collect(),@@ -241,7 +207,6 @@ mut rng: impl Rng,
dist: impl Distribution<f64>, param: &RingParam, ) -> Result<Self> { - // let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng))); Ok(Self { param: *param, coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng)))@@ -255,7 +220,6 @@ mut rng: impl Rng,
dist: impl Distribution<u64>, param: &RingParam, ) -> Result<Self> { - // let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_u64(dist.sample(&mut rng))); Ok(Self { param: *param, coeffs: std::iter::repeat_with(|| Zq::from_u64(param.q, dist.sample(&mut rng)))@@ -270,7 +234,6 @@ mut rng: impl Rng,
dist: impl Distribution<bool>, param: &RingParam, ) -> Result<Self> { - // let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_bool(dist.sample(&mut rng))); Ok(Rq { param: *param, coeffs: std::iter::repeat_with(|| Zq::from_bool(param.q, dist.sample(&mut rng)))@@ -304,7 +267,6 @@ }
pub fn mul_by_zq(&self, s: &Zq) -> Self { Self { param: self.param, - // coeffs: array::from_fn(|i| self.coeffs[i] * *s), coeffs: self.coeffs.iter().map(|c_i| *c_i * *s).collect(), evals: None, }@@ -313,7 +275,6 @@ pub fn mul_by_u64(&self, s: u64) -> Self {
let s = Zq::from_u64(self.param.q, s); Self { param: self.param, - // coeffs: array::from_fn(|i| self.coeffs[i] * s), coeffs: self.coeffs.iter().map(|&e| e * s).collect(), evals: None, }@@ -321,7 +282,6 @@ }
pub fn mul_by_f64(&self, s: f64) -> Self { Self { param: self.param, - // coeffs: array::from_fn(|i| Zq::from_f64(self.coeffs[i].0 as f64 * s)), coeffs: self .coeffs .iter()@@ -450,22 +410,11 @@ fn add(self, rhs: Self) -> Self {
assert_eq!(self.param, rhs.param); Self { param: self.param, - // coeffs: array::from_fn(|i| self.coeffs[i] + rhs.coeffs[i]), coeffs: zip_eq(self.coeffs, rhs.coeffs) .map(|(l, r)| l + r) .collect(), evals: None, } - // Self { - // coeffs: self - // .coeffs - // .iter() - // .zip(rhs.coeffs) - // .map(|(a, b)| *a + b) - // .collect(), - // evals: None, - // } - // Self(r.iter_mut().map(|e| e.r#mod()).collect()) // TODO mod should happen auto in + } } impl Add<&Rq> for &Rq {@@ -475,7 +424,6 @@ fn add(self, rhs: &Rq) -> Self::Output {
assert_eq!(self.param, rhs.param); Rq { param: self.param, - // coeffs: array::from_fn(|i| self.coeffs[i] + rhs.coeffs[i]), coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone()) .map(|(l, r)| l + r) .collect(),@@ -497,11 +445,6 @@ fn sum<I>(mut iter: I) -> Self
where I: Iterator<Item = Self>, { - // let mut acc = Rq::zero(); - // for e in iter { - // acc += e; - // } - // acc let first = iter.next().unwrap(); iter.fold(first, |acc, x| acc + x) }@@ -514,7 +457,6 @@ fn sub(self, rhs: Self) -> Self {
assert_eq!(self.param, rhs.param); Self { param: self.param, - // coeffs: array::from_fn(|i| self.coeffs[i] - rhs.coeffs[i]), coeffs: zip_eq(self.coeffs, rhs.coeffs) .map(|(l, r)| l - r) .collect(),@@ -529,7 +471,6 @@ fn sub(self, rhs: &Rq) -> Self::Output {
debug_assert_eq!(self.param, rhs.param); Rq { param: self.param, - // coeffs: array::from_fn(|i| self.coeffs[i] - rhs.coeffs[i]), coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone()) .map(|(l, r)| l - r) .collect(),@@ -613,8 +554,6 @@
fn neg(self) -> Self::Output { Self { param: self.param, - // coeffs: array::from_fn(|i| -self.coeffs[i]), - // coeffs: self.coeffs.iter().map(|c_i| -c_i).collect(), coeffs: self.coeffs.iter().map(|c_i| -*c_i).collect(), evals: None, }@@ -624,7 +563,6 @@
// note: this assumes that Q is prime fn mul_mut(lhs: &mut Rq, rhs: &mut Rq) -> Rq { assert_eq!(lhs.param, rhs.param); - // let (q, n) = (lhs.q, lhs.n); // reuse evaluations if already computed if !lhs.evals.is_some() {@@ -636,7 +574,6 @@ };
let lhs_evals = lhs.evals.clone().unwrap(); let rhs_evals = rhs.evals.clone().unwrap(); - // let c_ntt: [Zq<Q>; N] = array::from_fn(|i| lhs_evals[i] * rhs_evals[i]); let c_ntt: Rq = Rq::from_vec( &lhs.param, zip_eq(lhs_evals, rhs_evals).map(|(l, r)| l * r).collect(),@@ -645,7 +582,7 @@ let c = NTT::intt(&c_ntt);
Rq::new(&lhs.param, c.coeffs, Some(c_ntt.coeffs)) } // note: this assumes that Q is prime -// TODO impl karatsuba for non-prime Q +// TODO impl karatsuba for non-prime Q. Alternatively check NTT with RNS trick. fn mul(lhs: &Rq, rhs: &Rq) -> Rq { assert_eq!(lhs.param, rhs.param);@@ -661,7 +598,6 @@ } else {
NTT::ntt(rhs).coeffs }; - // let c_ntt: [Zq<Q>; N] = array::from_fn(|i| lhs_evals[i] * rhs_evals[i]); let c_ntt: Rq = Rq::from_vec( &lhs.param, zip_eq(lhs_evals, rhs_evals).map(|(l, r)| l * r).collect(),@@ -758,11 +694,8 @@ ) -> Result<()> {
assert_eq!(a.len(), param.n); assert_eq!(b.len(), param.n); - // let a: [Zq<Q>; N] = array::from_fn(|i| Zq::from_u64(a[i])); let mut a = Rq::from_vec_u64(¶m, a); - // let b: [Zq<Q>; N] = array::from_fn(|i| Zq::from_u64(b[i])); let mut b = Rq::from_vec_u64(¶m, b); - // let expected_c: [Zq<Q>; N] = array::from_fn(|i| Zq::from_u64(expected_c[i])); let expected_c = Rq::from_vec_u64(¶m, expected_c); let c = mul_mut(&mut a, &mut b);
@@ -22,17 +22,12 @@ /// 𝕋_<N,Q>[X] = 𝕋<Q>[X]/(X^N +1), polynomials modulo X^N+1 with coefficients in
/// 𝕋, where Q=2^64. #[derive(Clone, Debug)] pub struct Tn { - // pub n: usize, pub param: RingParam, pub coeffs: Vec<T64>, } impl Ring for Tn { type C = T64; - // type Param = usize; // n - - // const Q: u64 = u64::MAX; // WIP - // const N: usize = N; fn param(&self) -> RingParam { RingParam {@@ -58,7 +53,6 @@ coeffs: std::iter::repeat_with(|| T64::rand(&mut rng, &dist, ¶m))
.take(param.n) .collect(), } - // Self(array::from_fn(|_| T64::rand(&mut rng, &dist))) } fn from_vec(param: &RingParam, coeffs: Vec<Self::C>) -> Self {@@ -160,7 +154,6 @@ impl Add<Tn> for Tn {
type Output = Self; fn add(self, rhs: Self) -> Self { - // Self(array::from_fn(|i| self.0[i] + rhs.0[i])) assert_eq!(self.param, rhs.param); Self { param: self.param,@@ -174,7 +167,6 @@ impl Add<&Tn> for &Tn {
type Output = Tn; fn add(self, rhs: &Tn) -> Self::Output { - // Tn(array::from_fn(|i| self.0[i] + rhs.0[i])) assert_eq!(self.param, rhs.param); Tn { param: self.param,@@ -198,11 +190,6 @@ fn sum<I>(mut iter: I) -> Self
where I: Iterator<Item = Self>, { - // let mut acc = Tn::<N>::zero(); - // for e in iter { - // acc += e; - // } - // acc let first = iter.next().unwrap(); iter.fold(first, |acc, x| acc + x) }@@ -225,7 +212,6 @@ impl Sub<&Tn> for &Tn {
type Output = Tn; fn sub(self, rhs: &Tn) -> Self::Output { - // Tn(array::from_fn(|i| self.0[i] - rhs.0[i])) assert_eq!(self.param, rhs.param); Tn { param: self.param,@@ -238,9 +224,6 @@ }
impl SubAssign for Tn { fn sub_assign(&mut self, rhs: Self) { - // for i in 0..N { - // self.0[i] -= rhs.0[i]; - // } assert_eq!(self.param, rhs.param); for i in 0..self.param.n { self.coeffs[i] -= rhs.coeffs[i];@@ -252,7 +235,6 @@ impl Neg for Tn {
type Output = Self; fn neg(self) -> Self::Output { - // Tn(array::from_fn(|i| -self.0[i])) Self { param: self.param, coeffs: self.coeffs.iter().map(|c_i| -*c_i).collect(),@@ -300,7 +282,6 @@ modulus_u128(n, &mut result);
Tn { param, - // coeffs: array::from_fn(|i| T64(result[i] as u64)), coeffs: result.iter().map(|r_i| T64(*r_i as u64)).collect(), } }@@ -321,7 +302,6 @@ type Output = Self;
fn mul(self, s: T64) -> Self { Self { param: self.param, - // coeffs: array::from_fn(|i| self.coeffs[i] * s), coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(), } }@@ -330,7 +310,6 @@ // mul by u64
impl Mul<u64> for Tn { type Output = Self; fn mul(self, s: u64) -> Self { - // Self(array::from_fn(|i| self.0[i] * s)) Tn { param: self.param, coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(),@@ -340,7 +319,6 @@ }
impl Mul<&u64> for &Tn { type Output = Tn; fn mul(self, s: &u64) -> Self::Output { - // Tn::<N>(array::from_fn(|i| self.0[i] * *s)) Tn { param: self.param, coeffs: self.coeffs.iter().map(|c_i| c_i * s).collect(),
@@ -16,10 +16,6 @@ // implement the `Ring` trait for T64, so that it can be used where we would use
// `Tn<1>`. impl Ring for T64 { type C = T64; - // type Param = (); - - // const Q: u64 = u64::MAX; // WIP - // const N: usize = 1; fn param(&self) -> RingParam { RingParam {@@ -45,13 +41,13 @@
// TODO rm beta & l from inputs, make it always beta=2,l=64. /// Note: only beta=2 and l=64 is supported. fn decompose(&self, beta: u32, l: u32) -> Vec<Self> { - assert_eq!(beta, 2u32); // only beta=2 supported - // assert_eq!(l, 64u32); // only l=64 supported + assert_eq!(beta, 2u32, "only beta=2 supported"); + // assert_eq!(l, 64u32, "only l=64 supported"); // (0..64) - (0..l) + (0..l as u64) .rev() - .map(|i| T64(((self.0 >> i) & 1) as u64)) + .map(|i| T64((self.0 >> i) & 1)) .collect() } fn remodule(&self, p: u64) -> T64 {
@@ -9,13 +9,6 @@ pub q: u64,
pub v: u64, } -// WIP -// impl<const Q: u64> From<Vec<u64>> for Vec<Zq<Q>> { -// fn from(v: Vec<u64>) -> Self { -// v.into_iter().map(Zq::new).collect() -// } -// } - pub(crate) fn modulus_u64(q: u64, e: u64) -> u64 { (e % q + q) % q }@@ -24,7 +17,6 @@ pub fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, q: u64) -> Self {
// TODO WIP let r: f64 = dist.sample(&mut rng); Self::from_f64(q, r) - // Self::from_u64(r.round() as u64) } pub fn from_u64(q: u64, v: u64) -> Self { if v >= q {@@ -81,7 +73,7 @@ let mut exp = self;
// for rem != Self(0) { while rem != Self::zero(self.q) { // if odd - // TODO use a more readible expression + // TODO use a more readeable expression if 1 - ((rem.v & 1) << 1) as i64 == -1 { res = res * exp; }
@@ -1,6 +1,6 @@
use anyhow::Result; -use arith::{Matrix, Ring, Rq, C, R}; +use arith::{Matrix, Rq, C, R}; #[derive(Clone, Debug)] pub struct SecretKey(Rq);
@@ -154,7 +154,6 @@ .iter()
.map(|e| (*e as f64 / (scale_factor_u64 as f64)).round() as u64) .collect(); let m_decrypted = Rq::from_vec_u64(¶m.ring, m_decrypted); - // assert_eq!(m_decrypted, Rq::from(m_raw)); assert_eq!(m_decrypted, m_raw.to_rq(q)); }
@@ -1,10 +1,9 @@
use anyhow::Result; use itertools::zip_eq; use rand::Rng; -use rand_distr::{Normal, Uniform}; -use std::ops::{Add, Mul}; +use std::ops::Mul; -use arith::{Ring, TR}; +use arith::Ring; use crate::glwe::{Param, PublicKey, SecretKey, GLWE};@@ -41,7 +40,6 @@ beta: u32,
l: u32, sk: &SecretKey<R>, m: &R, - // delta: u64, ) -> Result<Self> { let glev: Vec<GLWE<R>> = (1..l + 1) .map(|i| {@@ -69,6 +67,7 @@ // GLev * Vec<R> --> GLWE
impl<R: Ring> Mul<Vec<R>> for GLev<R> { type Output = GLWE<R>; fn mul(self, v: Vec<R>) -> GLWE<R> { + debug_assert_eq!(self.0.len(), v.len()); // TODO debug_assert_eq of param // l times GLWES@@ -91,6 +90,7 @@
#[test] fn test_encrypt_decrypt() -> Result<()> { let param = Param { + err_sigma: crate::glwe::ERR_SIGMA, ring: RingParam { q: 2u64.pow(16) + 1, n: 128,@@ -103,7 +103,6 @@
let beta: u32 = 2; let l: u32 = 16; - // let delta: u64 = Q / T; // floored let mut rng = rand::thread_rng(); let msg_dist = Uniform::new(0_u64, param.t);
@@ -12,11 +12,13 @@ use arith::{Ring, RingParam, Rq, Zq, TR};
use crate::glev::GLev; -// const ERR_SIGMA: f64 = 3.2; -const ERR_SIGMA: f64 = 0.0; // TODO WIP +// error deviation for the Gaussian(Normal) distribution +// sigma=3.2 from: https://eprint.iacr.org/2022/162.pdf page 5 +pub(crate) const ERR_SIGMA: f64 = 3.2; #[derive(Clone, Copy, Debug)] pub struct Param { + pub err_sigma: f64, pub ring: RingParam, pub k: usize, pub t: u64,@@ -38,6 +40,7 @@ /// TLWE sk: s \in B^n , where n=K*N
/// TRLWE sk: s \in B_N[X]^K pub fn lwe(&self) -> Self { Self { + err_sigma: ERR_SIGMA, ring: RingParam { q: self.ring.q, n: 1,@@ -72,7 +75,7 @@ }
pub fn new_key(mut rng: impl Rng, param: &Param) -> Result<(SecretKey<R>, PublicKey<R>)> { let Xi_key = Uniform::new(0_f64, 2_f64); - let Xi_err = Normal::new(0_f64, ERR_SIGMA)?; + let Xi_err = Normal::new(0_f64, param.err_sigma)?; let s: TR<R> = TR::rand(&mut rng, Xi_key, param.k, ¶m.ring); let a: TR<R> = TR::rand(@@ -87,7 +90,7 @@ let pk: PublicKey<R> = PublicKey((&a * &s) + e, a);
Ok((SecretKey(s), pk)) } pub fn pk_from_sk(mut rng: impl Rng, param: &Param, sk: SecretKey<R>) -> Result<PublicKey<R>> { - let Xi_err = Normal::new(0_f64, ERR_SIGMA)?; + let Xi_err = Normal::new(0_f64, param.err_sigma)?; let a: TR<R> = TR::rand( &mut rng,@@ -141,7 +144,7 @@ sk: &SecretKey<R>,
m: &R, // already scaled ) -> Result<Self> { let Xi_key = Uniform::new(0_f64, 2_f64); - let Xi_err = Normal::new(0_f64, ERR_SIGMA)?; + let Xi_err = Normal::new(0_f64, param.err_sigma)?; let a: TR<R> = TR::rand(&mut rng, Xi_key, param.k, ¶m.ring); let e = R::rand(&mut rng, Xi_err, ¶m.ring);@@ -156,7 +159,7 @@ pk: &PublicKey<R>,
m: &R, // already scaled ) -> Result<Self> { let Xi_key = Uniform::new(0_f64, 2_f64); - let Xi_err = Normal::new(0_f64, ERR_SIGMA)?; + let Xi_err = Normal::new(0_f64, param.err_sigma)?; let u: R = R::rand(&mut rng, Xi_key, ¶m.ring);@@ -240,11 +243,6 @@ fn sum<I>(mut iter: I) -> Self
where I: Iterator<Item = Self>, { - // let mut acc = GLWE::<R>::zero(); - // for e in iter { - // acc += e; - // } - // acc let first = iter.next().unwrap(); iter.fold(first, |acc, e| acc + e) }@@ -328,6 +326,7 @@
#[test] fn test_encrypt_decrypt_ring_nq() -> Result<()> { let param = Param { + err_sigma: ERR_SIGMA, ring: RingParam { q: 2u64.pow(16) + 1, n: 128,@@ -335,7 +334,6 @@ },
k: 16, t: 32, // plaintext modulus }; - // let k: usize = 16; type S = GLWE<Rq>; let mut rng = rand::thread_rng();@@ -345,9 +343,8 @@ for _ in 0..200 {
let (sk, pk) = S::new_key(&mut rng, ¶m)?; let m = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; // msg - // let m: Rq<Q, N> = m.remodule::<Q>(); + let p = S::encode(¶m, &m); // plaintext - let p = S::encode(¶m, &m); // plaintext let c = S::encrypt(&mut rng, ¶m, &pk, &p)?; // ciphertext let p_recovered = c.decrypt(&sk); let m_recovered = S::decode(¶m, &p_recovered);@@ -370,8 +367,6 @@ pub fn t_encode(param: &RingParam, m: &Rq) -> Tn {
let p = m.param.q; // plaintext space let delta = u64::MAX / p; // floored let coeffs = m.coeffs(); - // Tn(array::from_fn(|i| T64(coeffs[i].0 * delta))) - // Tn{param, coeffs: array::from_fn(|i| T64(coeffs[i].0 * delta))) Tn { param: *param, coeffs: coeffs.iter().map(|c_i| T64(c_i.v * delta)).collect(),@@ -384,6 +379,7 @@ }
#[test] fn test_encrypt_decrypt_torus() -> Result<()> { let param = Param { + err_sigma: ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 128,@@ -422,6 +418,7 @@
#[test] fn test_addition() -> Result<()> { let param = Param { + err_sigma: ERR_SIGMA, ring: RingParam { q: 2u64.pow(16) + 1, n: 128,@@ -459,6 +456,7 @@
#[test] fn test_add_plaintext() -> Result<()> { let param = Param { + err_sigma: ERR_SIGMA, ring: RingParam { q: 2u64.pow(16) + 1, n: 128,@@ -495,6 +493,7 @@
#[test] fn test_mul_plaintext() -> Result<()> { let param = Param { + err_sigma: ERR_SIGMA, ring: RingParam { q: 2u64.pow(16) + 1, n: 16,@@ -530,6 +529,7 @@
#[test] fn test_mod_switch() -> Result<()> { let param = Param { + err_sigma: ERR_SIGMA, ring: RingParam { q: 2u64.pow(16) + 1, n: 8,@@ -561,6 +561,7 @@ });
let p_recovered = c2.decrypt(&sk2); let new_param = Param { + err_sigma: ERR_SIGMA, ring: RingParam { q: new_q, n: param.ring.n,@@ -579,6 +580,7 @@
#[test] fn test_key_switch() -> Result<()> { let param = Param { + err_sigma: ERR_SIGMA, ring: RingParam { q: 2u64.pow(16) + 1, n: 128,
@@ -10,3 +10,5 @@ pub mod tglwe;
pub mod tgsw; pub mod tlev; pub mod tlwe; + +pub(crate) const ERR_SIGMA: f64 = 3.2;
@@ -157,6 +157,7 @@ use super::*;
#[test] fn test_external_product() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 64 }, k: 4, t: 16, // plaintext modulus
@@ -1,22 +1,15 @@
use anyhow::Result; -use itertools::zip_eq; -use rand::distributions::Standard; use rand::Rng; -use rand_distr::{Normal, Uniform}; -use std::array; use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, Sub}; use arith::{Ring, RingParam, Rq, Tn, T64, TR}; use gfhe::{glwe, glwe::Param, GLWE}; -use crate::tlev::TLev; use crate::{tlwe, tlwe::TLWE}; -// pub type SecretKey<const N: usize, const K: usize> = glwe::SecretKey<Tn<N>, K>; #[derive(Clone, Debug)] pub struct SecretKey(pub glwe::SecretKey<Tn>); -// pub struct SecretKey<const K: usize>(pub tlwe::SecretKey<K>); impl SecretKey { pub fn to_tlwe(&self, param: &Param) -> tlwe::SecretKey {@@ -45,11 +38,9 @@ Self(GLWE::<Tn>::from_plaintext(k, param, p))
} pub fn new_key(mut rng: impl Rng, param: &Param) -> Result<(SecretKey, PublicKey)> { - // assert_eq!(KN, K * N); // this is wip, while not being able to compute K*N let (sk_tlwe, _) = TLWE::new_key(&mut rng, ¶m.lwe())?; //param.lwe() so that it uses K*N debug_assert_eq!(sk_tlwe.0 .0.r.len(), param.lwe().k); // =KN (sanity check) - // let sk = crate::tlwe::sk_to_tglwe::<N, K, KN>(sk_tlwe); let sk = sk_tlwe.to_tglwe(param); let pk: PublicKey = GLWE::pk_from_sk(rng, param, sk.0.clone())?; Ok((sk, pk))@@ -60,7 +51,6 @@ debug_assert_eq!(param.t, m.param.q); // plaintext modulus
let p = param.t; let delta = u64::MAX / p; // floored let coeffs = m.coeffs(); - // Tn(array::from_fn(|i| T64(coeffs[i].0 * delta))) Tn { param: param.ring, coeffs: coeffs.iter().map(|c_i| T64(c_i.v * delta)).collect(),@@ -72,7 +62,7 @@ let pt = pt.mul_div_round(p, u64::MAX);
Rq::from_vec_u64(¶m.pt(), pt.coeffs().iter().map(|c| c.0).collect()) } - // encrypts with the given SecretKey (instead of PublicKey) + /// encrypts with the given SecretKey (instead of PublicKey) pub fn encrypt_s(rng: impl Rng, param: &Param, sk: &SecretKey, p: &Tn) -> Result<Self> { let glwe = GLWE::encrypt_s(rng, param, &sk.0, p)?; Ok(Self(glwe))@@ -141,11 +131,6 @@ fn sum<I>(mut iter: I) -> Self
where I: Iterator<Item = Self>, { - // let mut acc = TGLWE::<N, K>::zero(); - // for e in iter { - // acc += e; - // } - // acc let first = iter.next().unwrap(); iter.fold(first, |acc, e| acc + e) }@@ -208,6 +193,7 @@
#[test] fn test_encrypt_decrypt() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 64 }, k: 16, t: 128, // plaintext modulus@@ -242,6 +228,7 @@
#[test] fn test_addition() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 64 }, k: 16, t: 128, // plaintext modulus@@ -275,6 +262,7 @@
#[test] fn test_add_plaintext() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 64 }, k: 16, t: 128, // plaintext modulus@@ -307,6 +295,7 @@
#[test] fn test_mul_plaintext() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 64 }, k: 16, t: 128, // plaintext modulus@@ -342,6 +331,7 @@
#[test] fn test_sample_extraction() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 64 }, k: 16, t: 128, // plaintext modulus
@@ -1,17 +1,13 @@
use anyhow::Result; use itertools::zip_eq; use rand::Rng; -use std::array; -use std::ops::{Add, Mul}; +use std::ops::Mul; -use arith::{Ring, RingParam, Rq, Tn, T64, TR}; +use arith::{Ring, T64}; use crate::tlev::TLev; -use crate::{ - tglwe::TGLWE, - tlwe::{PublicKey, SecretKey, TLWE}, -}; -use gfhe::glwe::{Param, GLWE}; +use crate::tlwe::{SecretKey, TLWE}; +use gfhe::glwe::Param; /// vector of length K+1 = [K], [1] #[derive(Clone, Debug)]@@ -73,10 +69,12 @@ use anyhow::Result;
use rand::distributions::Uniform; use super::*; + use arith::{RingParam, Rq}; #[test] fn test_encrypt_decrypt() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 16, t: 2, // plaintext modulus@@ -106,6 +104,7 @@
#[test] fn test_external_product() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 32, t: 2, // plaintext modulus@@ -130,7 +129,6 @@ let tlwe = TLWE::encrypt_s(&mut rng, ¶m, &sk, &p2)?;
let res: TLWE = tgsw * tlwe; - // let p_recovered = res.decrypt(&sk, beta); let p_recovered = res.decrypt(&sk); // downscaled by delta^-1 let res_recovered = TLWE::decode(¶m, &p_recovered);@@ -145,6 +143,7 @@
#[test] fn test_cmux() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 32, t: 2, // plaintext modulus
@@ -1,12 +1,10 @@
use anyhow::Result; use itertools::zip_eq; use rand::Rng; -use std::array; -use std::ops::{Add, Mul}; +use std::ops::Mul; -use arith::{Ring, RingParam, Rq, Tn, T64, TR}; +use arith::{Ring, RingParam, Rq, T64}; -use crate::tglwe::TGLWE; use crate::tlwe::{PublicKey, SecretKey, TLWE}; use gfhe::glwe::Param;@@ -23,7 +21,6 @@ T64(coeffs[0].v) // N=1, so take the only coeff
} pub fn decode(param: &Param, p: &T64) -> Rq { Rq::from_vec_u64( - // &RingParam { q: u64::MAX, n: 1 }, &RingParam { q: param.t, n: 1 }, p.coeffs().iter().map(|c| c.0).collect(), )@@ -38,14 +35,16 @@ m: &T64,
) -> Result<Self> { debug_assert_eq!(pk.1.k, param.k); - let tlev: Vec<TLWE> = (1..l + 1) + let tlev: Vec<TLWE> = (1..l as u64 + 1) .map(|i| { - TLWE::encrypt( - &mut rng, - param, - pk, - &(*m * (u64::MAX / beta.pow(i as u32) as u64)), - ) + let aux = if i < 64 { + *m * (u64::MAX / (1u64 << i)) + } else { + // 1<<64 would overflow, and anyways we're dividing u64::MAX + // by it, which would be equal to 1 + *m + }; + TLWE::encrypt(&mut rng, param, pk, &aux) }) .collect::<Result<Vec<_>>>()?;@@ -59,6 +58,8 @@ l: u32,
sk: &SecretKey, m: &T64, ) -> Result<Self> { + debug_assert_eq!(sk.0 .0.k, param.k); + let tlev: Vec<TLWE> = (1..l as u64 + 1) .map(|i| { let aux = if i < 64 {@@ -113,6 +114,7 @@
#[test] fn test_encrypt_decrypt() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 16, t: 2, // plaintext modulus@@ -143,13 +145,15 @@
#[test] fn test_tlev_vect64_product() -> Result<()> { let param = Param { + err_sigma: 0.1, // WIP ring: RingParam { q: u64::MAX, n: 1 }, k: 16, t: 2, // plaintext modulus }; let beta: u32 = 2; - let l: u32 = 16; + // let l: u32 = 16; + let l: u32 = 64; let mut rng = rand::thread_rng(); let msg_dist = Uniform::new(0_u64, param.t);
@@ -49,11 +49,10 @@ let (sk, pk): (glwe::SecretKey<T64>, glwe::PublicKey<T64>) = GLWE::new_key(rng, param)?;
Ok((SecretKey(sk), pk)) } - // TODO use param instead of p:u64 pub fn encode(param: &Param, m: &Rq) -> T64 { assert_eq!(param.ring.n, 1); debug_assert_eq!(param.t, m.param.q); // plaintext modulus - // + let delta = u64::MAX / param.t; // floored let coeffs = m.coeffs(); T64(coeffs[0].v * delta)@@ -112,14 +111,14 @@ let b: T64 = self.0 .1.mod_switch(q2);
Self(GLWE(a, b)) } } -// NOTE: the ugly const generics are temporary + pub fn blind_rotation( param: &Param, c: TLWE, // kn btk: BootstrappingKey, table: TGLWE, // n,k ) -> TGLWE { - debug_assert_eq!(c.0 .0.k, param.k); // TODO or k*n? + debug_assert_eq!(c.0 .0.k, param.k); // TODO replace `param.k*param.ring.n` by `param.kn()` let c_kn: TLWE = c.mod_switch((param.k * param.ring.n) as u64);@@ -138,7 +137,6 @@ btk.0[j].clone(),
c_j.clone(), c_j.clone().left_rotate(a.r[j].0 as usize), ); - // dbg!(&c_j); }); c_j }@@ -164,7 +162,7 @@ );
impl BootstrappingKey { pub fn from_sk(mut rng: impl Rng, param: &Param, sk: &tglwe::SecretKey) -> Result<Self> { let (beta, l) = (2u32, 64u32); // TMP - // + let s: TR<Tn> = sk.0 .0.clone(); let (sk2, _) = TLWE::new_key(&mut rng, ¶m.lwe())?; // TLWE<KN> compatible with TGLWE<N,K>@@ -229,11 +227,6 @@ fn sum<I>(mut iter: I) -> Self
where I: Iterator<Item = Self>, { - // let mut acc = TLWE::<K>::zero(); - // for e in iter { - // acc += e; - // } - // acc let first = iter.next().unwrap(); iter.fold(first, |acc, e| acc + e) }@@ -290,6 +283,7 @@
#[test] fn test_encrypt_decrypt() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 16, t: 128, // plaintext modulus@@ -324,6 +318,7 @@
#[test] fn test_addition() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 16, t: 128, // plaintext modulus@@ -357,6 +352,7 @@
#[test] fn test_add_plaintext() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 16, t: 128, // plaintext modulus@@ -389,6 +385,7 @@
#[test] fn test_mul_plaintext() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 16, t: 128, // plaintext modulus@@ -404,7 +401,6 @@ let m1 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
let m2 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let p1: T64 = TLWE::encode(¶m, &m1); // don't scale up p2, set it directly from m2 - // let p2: T64 = Tn(array::from_fn(|i| T64(m2.coeffs()[i].0))); let p2: T64 = T64(m2.coeffs()[0].v); let c1 = TLWE::encrypt(&mut rng, ¶m, &pk, &p1)?;@@ -422,6 +418,7 @@
#[test] fn test_key_switch() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1 }, k: 16, t: 128, // plaintext modulus@@ -440,7 +437,7 @@
let msg_dist = Uniform::new(0_u64, param.t); let m = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let p = TLWE::encode(¶m, &m); // plaintext - // + let c = TLWE::encrypt_s(&mut rng, ¶m, &sk, &p)?; let c2 = c.key_switch(¶m, beta, l, &ksk);@@ -463,6 +460,7 @@
#[test] fn test_bootstrapping() -> Result<()> { let param = Param { + err_sigma: crate::ERR_SIGMA, ring: RingParam { q: u64::MAX, n: 1024,@@ -470,10 +468,6 @@ },
k: 1, t: 128, // plaintext modulus }; - // const T: u64 = 128; // plaintext modulus - // const K: usize = 1; - // const N: usize = 1024; - // const KN: usize = K * N; let mut rng = rand::thread_rng(); let start = Instant::now();@@ -494,7 +488,6 @@
let c = TLWE::encrypt_s(&mut rng, ¶m.lwe(), &sk_tlwe, &p)?; let start = Instant::now(); - // the ugly const generics are temporary let bootstrapped: TLWE = bootstrapping(¶m, btk, table, c); println!("bootstrapping took: {:?}", start.elapsed());