Browse Source

polish & clean a bit

pull/2/head
arnaucube 2 months ago
parent
commit
12bb2aa3da
18 changed files with 98 additions and 261 deletions
  1. +18
    -14
      README.md
  2. +4
    -33
      arith/src/ntt.rs
  3. +0
    -4
      arith/src/ring.rs
  4. +0
    -22
      arith/src/ring_n.rs
  5. +3
    -70
      arith/src/ring_nq.rs
  6. +0
    -22
      arith/src/ring_torus.rs
  7. +4
    -8
      arith/src/torus.rs
  8. +1
    -9
      arith/src/zq.rs
  9. +1
    -1
      ckks/src/encoder.rs
  10. +0
    -1
      ckks/src/lib.rs
  11. +4
    -5
      gfhe/src/glev.rs
  12. +18
    -16
      gfhe/src/glwe.rs
  13. +2
    -0
      tfhe/src/lib.rs
  14. +1
    -0
      tfhe/src/tggsw.rs
  15. +6
    -16
      tfhe/src/tglwe.rs
  16. +8
    -9
      tfhe/src/tgsw.rs
  17. +17
    -13
      tfhe/src/tlev.rs
  18. +11
    -18
      tfhe/src/tlwe.rs

+ 18
- 14
README.md

@ -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>`. and the line `type S = TWLE<K>` to use `CKKS<Q, N>` or `BFV<Q, N, T>`.
```rust ```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 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, &param)?;
// get two random msgs in Z_t // 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, &param.pt())?;
let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
let m3 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
// encode the msgs into the plaintext space // 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(&param, &m1); // plaintext
let p2 = TLWE::encode(&param, &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, &param, &pk, &p1)?;
let c2 = TLWE::encrypt(&mut rng, &param, &pk, &p2)?;
// now we can do encrypted operations (notice that we do them using simple // now we can do encrypted operations (notice that we do them using simple
// operation notation by rust's operator overloading): // operation notation by rust's operator overloading):
@ -48,9 +51,10 @@ let c4 = c_12 * c3_const;
// decrypt & decode // decrypt & decode
let p4_recovered = c4.decrypt(&sk); let p4_recovered = c4.decrypt(&sk);
let m4 = S::decode::<T>(&p4_recovered);
let m4 = TLWE::decode(&param, &p4_recovered);
// m4 is equal to (m1+m2)*m3 // m4 is equal to (m1+m2)*m3
assert_eq!(((m1 + m2).to_r() * m3.to_r()).to_rq(param.t), m4);
``` ```

+ 4
- 33
arith/src/ntt.rs

@ -18,18 +18,12 @@ use std::sync::{Mutex, OnceLock};
static CACHE: OnceLock<Mutex<HashMap<(u64, usize), (Vec<Zq>, Vec<Zq>, Zq)>>> = OnceLock::new(); 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) { 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())); let cache_lock = CACHE.get_or_init(|| Mutex::new(HashMap::new()));
// Lock the HashMap for this thread
let mut cache = cache_lock.lock().unwrap(); let mut cache = cache_lock.lock().unwrap();
if let Some(value) = cache.get(&(q, n)) { if let Some(value) = cache.get(&(q, n)) {
// Found an existing value — return a clone
return value.clone(); return value.clone();
} }
// Not found — compute the new triple
let n_inv: Zq = Zq { let n_inv: Zq = Zq {
q, q,
v: const_inv_mod(q, n as u64), v: const_inv_mod(q, n as u64),
@ -37,10 +31,8 @@ fn roots(q: u64, n: usize) -> (Vec, Vec, Zq) {
let root_of_unity: u64 = primitive_root_of_unity(q, 2 * n); 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: 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 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); let value = (roots_of_unity, roots_of_unity_inv, n_inv);
// Store and return
cache.insert((q, n), value.clone()); cache.insert((q, n), value.clone());
value value
} }
@ -71,7 +63,8 @@ impl NTT {
t /= 2; t /= 2;
m *= 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 { Rq {
param: RingParam { q, n }, param: RingParam { q, n },
coeffs: r, coeffs: r,
@ -107,10 +100,11 @@ impl NTT {
for i in 0..n { for i in 0..n {
r[i] = r[i] * n_inv; r[i] = r[i] * n_inv;
} }
// Rq::from_vec((a.q, n), r)
Rq { Rq {
param: RingParam { q, n }, param: RingParam { q, n },
coeffs: r, coeffs: r,
// TODO maybe at `evals` place the inputed `a` which is the evals
// format
evals: None, evals: None,
} }
} }
@ -238,27 +232,4 @@ mod tests {
} }
Ok(()) 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, &param);
// 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(())
// }
} }

+ 0
- 4
arith/src/ring.rs

@ -33,10 +33,6 @@ pub trait Ring:
{ {
/// C defines the coefficient type /// C defines the coefficient type
type C: Debug + Clone; type C: Debug + Clone;
// type Param: Debug+Clone+Copy;
// const Q: u64;
// const N: usize;
fn param(&self) -> RingParam; fn param(&self) -> RingParam;
fn coeffs(&self) -> Vec<Self::C>; fn coeffs(&self) -> Vec<Self::C>;

+ 0
- 22
arith/src/ring_n.rs

@ -15,14 +15,7 @@ pub struct R {
pub coeffs: Vec<i64>, pub coeffs: Vec<i64>,
} }
// impl<const N: usize> Ring for R<N> {
impl R { 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> { pub fn coeffs(&self) -> Vec<i64> {
self.coeffs.clone() self.coeffs.clone()
} }
@ -33,16 +26,12 @@ impl R {
} }
} }
fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, n: usize) -> Self { 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 { Self {
n, n,
coeffs: std::iter::repeat_with(|| dist.sample(&mut rng).round() as i64) coeffs: std::iter::repeat_with(|| dist.sample(&mut rng).round() as i64)
.take(n) .take(n)
.collect(), .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 { pub fn from_vec(n: usize, coeffs: Vec<i64>) -> Self {
@ -90,9 +79,6 @@ impl From for R {
} }
impl R { impl R {
// pub fn coeffs(&self) -> [i64; N] {
// self.0
// }
pub fn to_rq(self, q: u64) -> crate::Rq { pub fn to_rq(self, q: u64) -> crate::Rq {
crate::Rq::from((q, self)) crate::Rq::from((q, self))
} }
@ -196,7 +182,6 @@ impl Add<&R> for &R {
type Output = R; type Output = R;
fn add(self, rhs: &R) -> Self::Output { fn add(self, rhs: &R) -> Self::Output {
// R(array::from_fn(|i| self.0[i] + rhs.0[i]))
assert_eq!(self.n, rhs.n); assert_eq!(self.n, rhs.n);
R { R {
n: self.n, n: self.n,
@ -220,11 +205,6 @@ impl Sum for R {
where where
I: Iterator<Item = Self>, I: Iterator<Item = Self>,
{ {
// let mut acc = R::zero();
// for e in iter {
// acc += e;
// }
// acc
let first = iter.next().unwrap(); let first = iter.next().unwrap();
iter.fold(first, |acc, x| acc + x) iter.fold(first, |acc, x| acc + x)
} }
@ -234,7 +214,6 @@ impl Sub for R {
type Output = Self; type Output = Self;
fn sub(self, rhs: Self) -> Self { fn sub(self, rhs: Self) -> Self {
// Self(array::from_fn(|i| self.0[i] - rhs.0[i]))
assert_eq!(self.n, rhs.n); assert_eq!(self.n, rhs.n);
Self { Self {
n: self.n, n: self.n,
@ -248,7 +227,6 @@ impl Sub<&R> for &R {
type Output = R; type Output = R;
fn sub(self, rhs: &R) -> Self::Output { fn sub(self, rhs: &R) -> Self::Output {
// R(array::from_fn(|i| self.0[i] - rhs.0[i]))
assert_eq!(self.n, rhs.n); assert_eq!(self.n, rhs.n);
R { R {
n: self.n, n: self.n,

+ 3
- 70
arith/src/ring_nq.rs

@ -13,9 +13,6 @@ use crate::zq::{modulus_u64, Zq};
use crate::{Ring, RingParam}; 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) /// PolynomialRing element, where the PolynomialRing is R = Z_q[X]/(X^n +1)
/// The implementation assumes that q is prime. /// The implementation assumes that q is prime.
#[derive(Clone)] #[derive(Clone)]
@ -31,8 +28,6 @@ pub struct Rq {
impl Ring for Rq { impl Ring for Rq {
type C = Zq; type C = Zq;
// type Param = (u64, usize);
// type Param = Param;
fn param(&self) -> RingParam { fn param(&self) -> RingParam {
self.param self.param
@ -40,8 +35,6 @@ impl Ring for Rq {
fn coeffs(&self) -> Vec<Self::C> { fn coeffs(&self) -> Vec<Self::C> {
self.coeffs.to_vec() self.coeffs.to_vec()
} }
// fn zero(q: u64, n: usize) -> Self {
// fn zero(param: (u64, usize)) -> Self {
fn zero(param: &RingParam) -> Self { fn zero(param: &RingParam) -> Self {
Self { Self {
param: param.clone(), param: param.clone(),
@ -50,8 +43,6 @@ impl Ring for Rq {
} }
} }
fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, param: &RingParam) -> Self { 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 { Self {
param: param.clone(), param: param.clone(),
coeffs: std::iter::repeat_with(|| Self::C::rand(&mut rng, &dist, param.q)) coeffs: std::iter::repeat_with(|| Self::C::rand(&mut rng, &dist, param.q))
@ -93,23 +84,17 @@ impl Ring for Rq {
q: p, q: p,
n: self.param.n, 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(&param, self.coeffs().iter().map(|m_i| m_i.v).collect()) Rq::from_vec_u64(&param, self.coeffs().iter().map(|m_i| m_i.v).collect())
} }
/// perform the mod switch operation from Q to Q', where Q2=Q' /// 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 { fn mod_switch(&self, p: u64) -> Rq {
let param = RingParam { let param = RingParam {
q: p, q: p,
n: self.param.n, n: self.param.n,
}; };
// assert_eq!(N, M); // sanity check
Rq { Rq {
param, 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(), coeffs: self.coeffs.iter().map(|c_i| c_i.mod_switch(p)).collect(),
evals: None, evals: None,
} }
@ -135,7 +120,6 @@ impl From<(u64, crate::ring_n::R)> for Rq {
Self::from_vec( Self::from_vec(
&RingParam { q, n: r.n }, &RingParam { q, n: r.n },
// (q, r.n),
r.coeffs() r.coeffs()
.iter() .iter()
.map(|e| Zq::from_f64(q, *e as f64)) .map(|e| Zq::from_f64(q, *e as f64))
@ -161,21 +145,13 @@ impl Rq {
self.coeffs.clone() self.coeffs.clone()
} }
pub fn compute_evals(&mut self) { 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 { pub fn to_r(self) -> crate::R {
crate::R::from(self) 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 // this method is mostly for tests
pub fn from_vec_u64(param: &RingParam, coeffs: Vec<u64>) -> Self { 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(); let coeffs_mod_q: Vec<Zq> = coeffs.iter().map(|c| Zq::from_u64(param.q, *c)).collect();
@ -204,14 +180,9 @@ impl Rq {
mut rng: impl Rng, mut rng: impl Rng,
dist: impl Distribution<f64>, dist: impl Distribution<f64>,
param: &RingParam, param: &RingParam,
// q: u64,
// n: usize,
) -> Result<Self> { ) -> Result<Self> {
// let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng).abs()));
Ok(Self { Ok(Self {
param: *param, param: *param,
// q,
// n,
coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng).abs())) coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng).abs()))
.take(param.n) .take(param.n)
.collect(), .collect(),
@ -222,14 +193,9 @@ impl Rq {
mut rng: impl Rng, mut rng: impl Rng,
dist: impl Distribution<f64>, dist: impl Distribution<f64>,
param: &RingParam, param: &RingParam,
// q: u64,
// n: usize,
) -> Result<Self> { ) -> Result<Self> {
// let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng).abs()));
Ok(Self { Ok(Self {
param: *param, param: *param,
// q,
// n,
coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng).abs())) coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng).abs()))
.take(param.n) .take(param.n)
.collect(), .collect(),
@ -241,7 +207,6 @@ impl Rq {
dist: impl Distribution<f64>, dist: impl Distribution<f64>,
param: &RingParam, param: &RingParam,
) -> Result<Self> { ) -> Result<Self> {
// let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng)));
Ok(Self { Ok(Self {
param: *param, param: *param,
coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng))) coeffs: std::iter::repeat_with(|| Zq::from_f64(param.q, dist.sample(&mut rng)))
@ -255,7 +220,6 @@ impl Rq {
dist: impl Distribution<u64>, dist: impl Distribution<u64>,
param: &RingParam, param: &RingParam,
) -> Result<Self> { ) -> Result<Self> {
// let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_u64(dist.sample(&mut rng)));
Ok(Self { Ok(Self {
param: *param, param: *param,
coeffs: std::iter::repeat_with(|| Zq::from_u64(param.q, dist.sample(&mut rng))) coeffs: std::iter::repeat_with(|| Zq::from_u64(param.q, dist.sample(&mut rng)))
@ -270,7 +234,6 @@ impl Rq {
dist: impl Distribution<bool>, dist: impl Distribution<bool>,
param: &RingParam, param: &RingParam,
) -> Result<Self> { ) -> Result<Self> {
// let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_bool(dist.sample(&mut rng)));
Ok(Rq { Ok(Rq {
param: *param, param: *param,
coeffs: std::iter::repeat_with(|| Zq::from_bool(param.q, dist.sample(&mut rng))) coeffs: std::iter::repeat_with(|| Zq::from_bool(param.q, dist.sample(&mut rng)))
@ -304,7 +267,6 @@ impl Rq {
pub fn mul_by_zq(&self, s: &Zq) -> Self { pub fn mul_by_zq(&self, s: &Zq) -> Self {
Self { Self {
param: self.param, param: self.param,
// coeffs: array::from_fn(|i| self.coeffs[i] * *s),
coeffs: self.coeffs.iter().map(|c_i| *c_i * *s).collect(), coeffs: self.coeffs.iter().map(|c_i| *c_i * *s).collect(),
evals: None, evals: None,
} }
@ -313,7 +275,6 @@ impl Rq {
let s = Zq::from_u64(self.param.q, s); let s = Zq::from_u64(self.param.q, s);
Self { Self {
param: self.param, param: self.param,
// coeffs: array::from_fn(|i| self.coeffs[i] * s),
coeffs: self.coeffs.iter().map(|&e| e * s).collect(), coeffs: self.coeffs.iter().map(|&e| e * s).collect(),
evals: None, evals: None,
} }
@ -321,7 +282,6 @@ impl Rq {
pub fn mul_by_f64(&self, s: f64) -> Self { pub fn mul_by_f64(&self, s: f64) -> Self {
Self { Self {
param: self.param, param: self.param,
// coeffs: array::from_fn(|i| Zq::from_f64(self.coeffs[i].0 as f64 * s)),
coeffs: self coeffs: self
.coeffs .coeffs
.iter() .iter()
@ -450,22 +410,11 @@ impl Add for Rq {
assert_eq!(self.param, rhs.param); assert_eq!(self.param, rhs.param);
Self { Self {
param: self.param, param: self.param,
// coeffs: array::from_fn(|i| self.coeffs[i] + rhs.coeffs[i]),
coeffs: zip_eq(self.coeffs, rhs.coeffs) coeffs: zip_eq(self.coeffs, rhs.coeffs)
.map(|(l, r)| l + r) .map(|(l, r)| l + r)
.collect(), .collect(),
evals: None, 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 { impl Add<&Rq> for &Rq {
@ -475,7 +424,6 @@ impl Add<&Rq> for &Rq {
assert_eq!(self.param, rhs.param); assert_eq!(self.param, rhs.param);
Rq { Rq {
param: self.param, param: self.param,
// coeffs: array::from_fn(|i| self.coeffs[i] + rhs.coeffs[i]),
coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone()) coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone())
.map(|(l, r)| l + r) .map(|(l, r)| l + r)
.collect(), .collect(),
@ -497,11 +445,6 @@ impl Sum for Rq {
where where
I: Iterator<Item = Self>, I: Iterator<Item = Self>,
{ {
// let mut acc = Rq::zero();
// for e in iter {
// acc += e;
// }
// acc
let first = iter.next().unwrap(); let first = iter.next().unwrap();
iter.fold(first, |acc, x| acc + x) iter.fold(first, |acc, x| acc + x)
} }
@ -514,7 +457,6 @@ impl Sub for Rq {
assert_eq!(self.param, rhs.param); assert_eq!(self.param, rhs.param);
Self { Self {
param: self.param, param: self.param,
// coeffs: array::from_fn(|i| self.coeffs[i] - rhs.coeffs[i]),
coeffs: zip_eq(self.coeffs, rhs.coeffs) coeffs: zip_eq(self.coeffs, rhs.coeffs)
.map(|(l, r)| l - r) .map(|(l, r)| l - r)
.collect(), .collect(),
@ -529,7 +471,6 @@ impl Sub<&Rq> for &Rq {
debug_assert_eq!(self.param, rhs.param); debug_assert_eq!(self.param, rhs.param);
Rq { Rq {
param: self.param, param: self.param,
// coeffs: array::from_fn(|i| self.coeffs[i] - rhs.coeffs[i]),
coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone()) coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone())
.map(|(l, r)| l - r) .map(|(l, r)| l - r)
.collect(), .collect(),
@ -613,8 +554,6 @@ impl Neg for Rq {
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
Self { Self {
param: self.param, 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(), coeffs: self.coeffs.iter().map(|c_i| -*c_i).collect(),
evals: None, evals: None,
} }
@ -624,7 +563,6 @@ impl Neg for Rq {
// note: this assumes that Q is prime // note: this assumes that Q is prime
fn mul_mut(lhs: &mut Rq, rhs: &mut Rq) -> Rq { fn mul_mut(lhs: &mut Rq, rhs: &mut Rq) -> Rq {
assert_eq!(lhs.param, rhs.param); assert_eq!(lhs.param, rhs.param);
// let (q, n) = (lhs.q, lhs.n);
// reuse evaluations if already computed // reuse evaluations if already computed
if !lhs.evals.is_some() { if !lhs.evals.is_some() {
@ -636,7 +574,6 @@ fn mul_mut(lhs: &mut Rq, rhs: &mut Rq) -> Rq {
let lhs_evals = lhs.evals.clone().unwrap(); let lhs_evals = lhs.evals.clone().unwrap();
let rhs_evals = rhs.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( let c_ntt: Rq = Rq::from_vec(
&lhs.param, &lhs.param,
zip_eq(lhs_evals, rhs_evals).map(|(l, r)| l * r).collect(), zip_eq(lhs_evals, rhs_evals).map(|(l, r)| l * r).collect(),
@ -645,7 +582,7 @@ fn mul_mut(lhs: &mut Rq, rhs: &mut Rq) -> Rq {
Rq::new(&lhs.param, c.coeffs, Some(c_ntt.coeffs)) Rq::new(&lhs.param, c.coeffs, Some(c_ntt.coeffs))
} }
// note: this assumes that Q is prime // 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 { fn mul(lhs: &Rq, rhs: &Rq) -> Rq {
assert_eq!(lhs.param, rhs.param); assert_eq!(lhs.param, rhs.param);
@ -661,7 +598,6 @@ fn mul(lhs: &Rq, rhs: &Rq) -> Rq {
NTT::ntt(rhs).coeffs 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( let c_ntt: Rq = Rq::from_vec(
&lhs.param, &lhs.param,
zip_eq(lhs_evals, rhs_evals).map(|(l, r)| l * r).collect(), zip_eq(lhs_evals, rhs_evals).map(|(l, r)| l * r).collect(),
@ -758,11 +694,8 @@ mod tests {
assert_eq!(a.len(), param.n); assert_eq!(a.len(), param.n);
assert_eq!(b.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(&param, a); let mut a = Rq::from_vec_u64(&param, a);
// let b: [Zq<Q>; N] = array::from_fn(|i| Zq::from_u64(b[i]));
let mut b = Rq::from_vec_u64(&param, b); let mut b = Rq::from_vec_u64(&param, b);
// let expected_c: [Zq<Q>; N] = array::from_fn(|i| Zq::from_u64(expected_c[i]));
let expected_c = Rq::from_vec_u64(&param, expected_c); let expected_c = Rq::from_vec_u64(&param, expected_c);
let c = mul_mut(&mut a, &mut b); let c = mul_mut(&mut a, &mut b);

+ 0
- 22
arith/src/ring_torus.rs

@ -22,17 +22,12 @@ use crate::{
/// 𝕋, where Q=2^64. /// 𝕋, where Q=2^64.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Tn { pub struct Tn {
// pub n: usize,
pub param: RingParam, pub param: RingParam,
pub coeffs: Vec<T64>, pub coeffs: Vec<T64>,
} }
impl Ring for Tn { impl Ring for Tn {
type C = T64; type C = T64;
// type Param = usize; // n
// const Q: u64 = u64::MAX; // WIP
// const N: usize = N;
fn param(&self) -> RingParam { fn param(&self) -> RingParam {
RingParam { RingParam {
@ -58,7 +53,6 @@ impl Ring for Tn {
.take(param.n) .take(param.n)
.collect(), .collect(),
} }
// Self(array::from_fn(|_| T64::rand(&mut rng, &dist)))
} }
fn from_vec(param: &RingParam, coeffs: Vec<Self::C>) -> Self { fn from_vec(param: &RingParam, coeffs: Vec<Self::C>) -> Self {
@ -160,7 +154,6 @@ impl Add for Tn {
type Output = Self; type Output = Self;
fn add(self, rhs: Self) -> Self { fn add(self, rhs: Self) -> Self {
// Self(array::from_fn(|i| self.0[i] + rhs.0[i]))
assert_eq!(self.param, rhs.param); assert_eq!(self.param, rhs.param);
Self { Self {
param: self.param, param: self.param,
@ -174,7 +167,6 @@ impl Add<&Tn> for &Tn {
type Output = Tn; type Output = Tn;
fn add(self, rhs: &Tn) -> Self::Output { fn add(self, rhs: &Tn) -> Self::Output {
// Tn(array::from_fn(|i| self.0[i] + rhs.0[i]))
assert_eq!(self.param, rhs.param); assert_eq!(self.param, rhs.param);
Tn { Tn {
param: self.param, param: self.param,
@ -198,11 +190,6 @@ impl Sum for Tn {
where where
I: Iterator<Item = Self>, I: Iterator<Item = Self>,
{ {
// let mut acc = Tn::<N>::zero();
// for e in iter {
// acc += e;
// }
// acc
let first = iter.next().unwrap(); let first = iter.next().unwrap();
iter.fold(first, |acc, x| acc + x) iter.fold(first, |acc, x| acc + x)
} }
@ -225,7 +212,6 @@ impl Sub<&Tn> for &Tn {
type Output = Tn; type Output = Tn;
fn sub(self, rhs: &Tn) -> Self::Output { fn sub(self, rhs: &Tn) -> Self::Output {
// Tn(array::from_fn(|i| self.0[i] - rhs.0[i]))
assert_eq!(self.param, rhs.param); assert_eq!(self.param, rhs.param);
Tn { Tn {
param: self.param, param: self.param,
@ -238,9 +224,6 @@ impl Sub<&Tn> for &Tn {
impl SubAssign for Tn { impl SubAssign for Tn {
fn sub_assign(&mut self, rhs: Self) { fn sub_assign(&mut self, rhs: Self) {
// for i in 0..N {
// self.0[i] -= rhs.0[i];
// }
assert_eq!(self.param, rhs.param); assert_eq!(self.param, rhs.param);
for i in 0..self.param.n { for i in 0..self.param.n {
self.coeffs[i] -= rhs.coeffs[i]; self.coeffs[i] -= rhs.coeffs[i];
@ -252,7 +235,6 @@ impl Neg for Tn {
type Output = Self; type Output = Self;
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
// Tn(array::from_fn(|i| -self.0[i]))
Self { Self {
param: self.param, param: self.param,
coeffs: self.coeffs.iter().map(|c_i| -*c_i).collect(), coeffs: self.coeffs.iter().map(|c_i| -*c_i).collect(),
@ -300,7 +282,6 @@ fn naive_poly_mul(poly1: &Tn, poly2: &Tn) -> Tn {
Tn { Tn {
param, param,
// coeffs: array::from_fn(|i| T64(result[i] as u64)),
coeffs: result.iter().map(|r_i| T64(*r_i as u64)).collect(), coeffs: result.iter().map(|r_i| T64(*r_i as u64)).collect(),
} }
} }
@ -321,7 +302,6 @@ impl Mul for Tn {
fn mul(self, s: T64) -> Self { fn mul(self, s: T64) -> Self {
Self { Self {
param: self.param, param: self.param,
// coeffs: array::from_fn(|i| self.coeffs[i] * s),
coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(), coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(),
} }
} }
@ -330,7 +310,6 @@ impl Mul for Tn {
impl Mul<u64> for Tn { impl Mul<u64> for Tn {
type Output = Self; type Output = Self;
fn mul(self, s: u64) -> Self { fn mul(self, s: u64) -> Self {
// Self(array::from_fn(|i| self.0[i] * s))
Tn { Tn {
param: self.param, param: self.param,
coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(), coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(),
@ -340,7 +319,6 @@ impl Mul for Tn {
impl Mul<&u64> for &Tn { impl Mul<&u64> for &Tn {
type Output = Tn; type Output = Tn;
fn mul(self, s: &u64) -> Self::Output { fn mul(self, s: &u64) -> Self::Output {
// Tn::<N>(array::from_fn(|i| self.0[i] * *s))
Tn { Tn {
param: self.param, param: self.param,
coeffs: self.coeffs.iter().map(|c_i| c_i * s).collect(), coeffs: self.coeffs.iter().map(|c_i| c_i * s).collect(),

+ 4
- 8
arith/src/torus.rs

@ -16,10 +16,6 @@ pub struct T64(pub u64);
// `Tn<1>`. // `Tn<1>`.
impl Ring for T64 { impl Ring for T64 {
type C = T64; type C = T64;
// type Param = ();
// const Q: u64 = u64::MAX; // WIP
// const N: usize = 1;
fn param(&self) -> RingParam { fn param(&self) -> RingParam {
RingParam { RingParam {
@ -45,13 +41,13 @@ impl Ring for T64 {
// TODO rm beta & l from inputs, make it always beta=2,l=64. // TODO rm beta & l from inputs, make it always beta=2,l=64.
/// Note: only beta=2 and l=64 is supported. /// Note: only beta=2 and l=64 is supported.
fn decompose(&self, beta: u32, l: u32) -> Vec<Self> { 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..64)
(0..l)
(0..l as u64)
.rev() .rev()
.map(|i| T64(((self.0 >> i) & 1) as u64))
.map(|i| T64((self.0 >> i) & 1))
.collect() .collect()
} }
fn remodule(&self, p: u64) -> T64 { fn remodule(&self, p: u64) -> T64 {

+ 1
- 9
arith/src/zq.rs

@ -9,13 +9,6 @@ pub struct Zq {
pub v: 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 { pub(crate) fn modulus_u64(q: u64, e: u64) -> u64 {
(e % q + q) % q (e % q + q) % q
} }
@ -24,7 +17,6 @@ impl Zq {
// TODO WIP // TODO WIP
let r: f64 = dist.sample(&mut rng); let r: f64 = dist.sample(&mut rng);
Self::from_f64(q, r) Self::from_f64(q, r)
// Self::from_u64(r.round() as u64)
} }
pub fn from_u64(q: u64, v: u64) -> Self { pub fn from_u64(q: u64, v: u64) -> Self {
if v >= q { if v >= q {
@ -81,7 +73,7 @@ impl Zq {
// for rem != Self(0) { // for rem != Self(0) {
while rem != Self::zero(self.q) { while rem != Self::zero(self.q) {
// if odd // if odd
// TODO use a more readible expression
// TODO use a more readeable expression
if 1 - ((rem.v & 1) << 1) as i64 == -1 { if 1 - ((rem.v & 1) << 1) as i64 == -1 {
res = res * exp; res = res * exp;
} }

+ 1
- 1
ckks/src/encoder.rs

@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use arith::{Matrix, Ring, Rq, C, R};
use arith::{Matrix, Rq, C, R};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SecretKey(Rq); pub struct SecretKey(Rq);

+ 0
- 1
ckks/src/lib.rs

@ -154,7 +154,6 @@ mod tests {
.map(|e| (*e as f64 / (scale_factor_u64 as f64)).round() as u64) .map(|e| (*e as f64 / (scale_factor_u64 as f64)).round() as u64)
.collect(); .collect();
let m_decrypted = Rq::from_vec_u64(&param.ring, m_decrypted); let m_decrypted = Rq::from_vec_u64(&param.ring, m_decrypted);
// assert_eq!(m_decrypted, Rq::from(m_raw));
assert_eq!(m_decrypted, m_raw.to_rq(q)); assert_eq!(m_decrypted, m_raw.to_rq(q));
} }

+ 4
- 5
gfhe/src/glev.rs

@ -1,10 +1,9 @@
use anyhow::Result; use anyhow::Result;
use itertools::zip_eq; use itertools::zip_eq;
use rand::Rng; 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}; use crate::glwe::{Param, PublicKey, SecretKey, GLWE};
@ -41,7 +40,6 @@ impl GLev {
l: u32, l: u32,
sk: &SecretKey<R>, sk: &SecretKey<R>,
m: &R, m: &R,
// delta: u64,
) -> Result<Self> { ) -> Result<Self> {
let glev: Vec<GLWE<R>> = (1..l + 1) let glev: Vec<GLWE<R>> = (1..l + 1)
.map(|i| { .map(|i| {
@ -69,6 +67,7 @@ impl GLev {
impl<R: Ring> Mul<Vec<R>> for GLev<R> { impl<R: Ring> Mul<Vec<R>> for GLev<R> {
type Output = GLWE<R>; type Output = GLWE<R>;
fn mul(self, v: Vec<R>) -> GLWE<R> { fn mul(self, v: Vec<R>) -> GLWE<R> {
debug_assert_eq!(self.0.len(), v.len());
// TODO debug_assert_eq of param // TODO debug_assert_eq of param
// l times GLWES // l times GLWES
@ -91,6 +90,7 @@ mod tests {
#[test] #[test]
fn test_encrypt_decrypt() -> Result<()> { fn test_encrypt_decrypt() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::glwe::ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: 2u64.pow(16) + 1, q: 2u64.pow(16) + 1,
n: 128, n: 128,
@ -103,7 +103,6 @@ mod tests {
let beta: u32 = 2; let beta: u32 = 2;
let l: u32 = 16; let l: u32 = 16;
// let delta: u64 = Q / T; // floored
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let msg_dist = Uniform::new(0_u64, param.t); let msg_dist = Uniform::new(0_u64, param.t);

+ 18
- 16
gfhe/src/glwe.rs

@ -12,11 +12,13 @@ use arith::{Ring, RingParam, Rq, Zq, TR};
use crate::glev::GLev; 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)] #[derive(Clone, Copy, Debug)]
pub struct Param { pub struct Param {
pub err_sigma: f64,
pub ring: RingParam, pub ring: RingParam,
pub k: usize, pub k: usize,
pub t: u64, pub t: u64,
@ -38,6 +40,7 @@ impl Param {
/// TRLWE sk: s \in B_N[X]^K /// TRLWE sk: s \in B_N[X]^K
pub fn lwe(&self) -> Self { pub fn lwe(&self) -> Self {
Self { Self {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: self.ring.q, q: self.ring.q,
n: 1, n: 1,
@ -72,7 +75,7 @@ impl GLWE {
pub fn new_key(mut rng: impl Rng, param: &Param) -> Result<(SecretKey<R>, PublicKey<R>)> { 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_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, &param.ring); let s: TR<R> = TR::rand(&mut rng, Xi_key, param.k, &param.ring);
let a: TR<R> = TR::rand( let a: TR<R> = TR::rand(
@ -87,7 +90,7 @@ impl GLWE {
Ok((SecretKey(s), pk)) Ok((SecretKey(s), pk))
} }
pub fn pk_from_sk(mut rng: impl Rng, param: &Param, sk: SecretKey<R>) -> Result<PublicKey<R>> { 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( let a: TR<R> = TR::rand(
&mut rng, &mut rng,
@ -141,7 +144,7 @@ impl GLWE {
m: &R, // already scaled m: &R, // already scaled
) -> Result<Self> { ) -> Result<Self> {
let Xi_key = Uniform::new(0_f64, 2_f64); 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, &param.ring); let a: TR<R> = TR::rand(&mut rng, Xi_key, param.k, &param.ring);
let e = R::rand(&mut rng, Xi_err, &param.ring); let e = R::rand(&mut rng, Xi_err, &param.ring);
@ -156,7 +159,7 @@ impl GLWE {
m: &R, // already scaled m: &R, // already scaled
) -> Result<Self> { ) -> Result<Self> {
let Xi_key = Uniform::new(0_f64, 2_f64); 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, &param.ring); let u: R = R::rand(&mut rng, Xi_key, &param.ring);
@ -240,11 +243,6 @@ impl Sum> for GLWE {
where where
I: Iterator<Item = Self>, I: Iterator<Item = Self>,
{ {
// let mut acc = GLWE::<R>::zero();
// for e in iter {
// acc += e;
// }
// acc
let first = iter.next().unwrap(); let first = iter.next().unwrap();
iter.fold(first, |acc, e| acc + e) iter.fold(first, |acc, e| acc + e)
} }
@ -328,6 +326,7 @@ mod tests {
#[test] #[test]
fn test_encrypt_decrypt_ring_nq() -> Result<()> { fn test_encrypt_decrypt_ring_nq() -> Result<()> {
let param = Param { let param = Param {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: 2u64.pow(16) + 1, q: 2u64.pow(16) + 1,
n: 128, n: 128,
@ -335,7 +334,6 @@ mod tests {
k: 16, k: 16,
t: 32, // plaintext modulus t: 32, // plaintext modulus
}; };
// let k: usize = 16;
type S = GLWE<Rq>; type S = GLWE<Rq>;
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
@ -345,9 +343,8 @@ mod tests {
let (sk, pk) = S::new_key(&mut rng, &param)?; let (sk, pk) = S::new_key(&mut rng, &param)?;
let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; // msg let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; // msg
// let m: Rq<Q, N> = m.remodule::<Q>();
let p = S::encode(&param, &m); // plaintext let p = S::encode(&param, &m); // plaintext
let c = S::encrypt(&mut rng, &param, &pk, &p)?; // ciphertext let c = S::encrypt(&mut rng, &param, &pk, &p)?; // ciphertext
let p_recovered = c.decrypt(&sk); let p_recovered = c.decrypt(&sk);
let m_recovered = S::decode(&param, &p_recovered); let m_recovered = S::decode(&param, &p_recovered);
@ -370,8 +367,6 @@ mod tests {
let p = m.param.q; // plaintext space let p = m.param.q; // plaintext space
let delta = u64::MAX / p; // floored let delta = u64::MAX / p; // floored
let coeffs = m.coeffs(); 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 { Tn {
param: *param, param: *param,
coeffs: coeffs.iter().map(|c_i| T64(c_i.v * delta)).collect(), coeffs: coeffs.iter().map(|c_i| T64(c_i.v * delta)).collect(),
@ -384,6 +379,7 @@ mod tests {
#[test] #[test]
fn test_encrypt_decrypt_torus() -> Result<()> { fn test_encrypt_decrypt_torus() -> Result<()> {
let param = Param { let param = Param {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: u64::MAX, q: u64::MAX,
n: 128, n: 128,
@ -422,6 +418,7 @@ mod tests {
#[test] #[test]
fn test_addition() -> Result<()> { fn test_addition() -> Result<()> {
let param = Param { let param = Param {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: 2u64.pow(16) + 1, q: 2u64.pow(16) + 1,
n: 128, n: 128,
@ -459,6 +456,7 @@ mod tests {
#[test] #[test]
fn test_add_plaintext() -> Result<()> { fn test_add_plaintext() -> Result<()> {
let param = Param { let param = Param {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: 2u64.pow(16) + 1, q: 2u64.pow(16) + 1,
n: 128, n: 128,
@ -495,6 +493,7 @@ mod tests {
#[test] #[test]
fn test_mul_plaintext() -> Result<()> { fn test_mul_plaintext() -> Result<()> {
let param = Param { let param = Param {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: 2u64.pow(16) + 1, q: 2u64.pow(16) + 1,
n: 16, n: 16,
@ -530,6 +529,7 @@ mod tests {
#[test] #[test]
fn test_mod_switch() -> Result<()> { fn test_mod_switch() -> Result<()> {
let param = Param { let param = Param {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: 2u64.pow(16) + 1, q: 2u64.pow(16) + 1,
n: 8, n: 8,
@ -561,6 +561,7 @@ mod tests {
let p_recovered = c2.decrypt(&sk2); let p_recovered = c2.decrypt(&sk2);
let new_param = Param { let new_param = Param {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: new_q, q: new_q,
n: param.ring.n, n: param.ring.n,
@ -579,6 +580,7 @@ mod tests {
#[test] #[test]
fn test_key_switch() -> Result<()> { fn test_key_switch() -> Result<()> {
let param = Param { let param = Param {
err_sigma: ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: 2u64.pow(16) + 1, q: 2u64.pow(16) + 1,
n: 128, n: 128,

+ 2
- 0
tfhe/src/lib.rs

@ -10,3 +10,5 @@ pub mod tglwe;
pub mod tgsw; pub mod tgsw;
pub mod tlev; pub mod tlev;
pub mod tlwe; pub mod tlwe;
pub(crate) const ERR_SIGMA: f64 = 3.2;

+ 1
- 0
tfhe/src/tggsw.rs

@ -157,6 +157,7 @@ mod tests {
#[test] #[test]
fn test_external_product() -> Result<()> { fn test_external_product() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 64 }, ring: RingParam { q: u64::MAX, n: 64 },
k: 4, k: 4,
t: 16, // plaintext modulus t: 16, // plaintext modulus

+ 6
- 16
tfhe/src/tglwe.rs

@ -1,22 +1,15 @@
use anyhow::Result; use anyhow::Result;
use itertools::zip_eq;
use rand::distributions::Standard;
use rand::Rng; use rand::Rng;
use rand_distr::{Normal, Uniform};
use std::array;
use std::iter::Sum; use std::iter::Sum;
use std::ops::{Add, AddAssign, Mul, Sub}; use std::ops::{Add, AddAssign, Mul, Sub};
use arith::{Ring, RingParam, Rq, Tn, T64, TR}; use arith::{Ring, RingParam, Rq, Tn, T64, TR};
use gfhe::{glwe, glwe::Param, GLWE}; use gfhe::{glwe, glwe::Param, GLWE};
use crate::tlev::TLev;
use crate::{tlwe, tlwe::TLWE}; use crate::{tlwe, tlwe::TLWE};
// pub type SecretKey<const N: usize, const K: usize> = glwe::SecretKey<Tn<N>, K>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SecretKey(pub glwe::SecretKey<Tn>); pub struct SecretKey(pub glwe::SecretKey<Tn>);
// pub struct SecretKey<const K: usize>(pub tlwe::SecretKey<K>);
impl SecretKey { impl SecretKey {
pub fn to_tlwe(&self, param: &Param) -> tlwe::SecretKey { pub fn to_tlwe(&self, param: &Param) -> tlwe::SecretKey {
@ -45,11 +38,9 @@ impl TGLWE {
} }
pub fn new_key(mut rng: impl Rng, param: &Param) -> Result<(SecretKey, PublicKey)> { 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, &param.lwe())?; //param.lwe() so that it uses K*N let (sk_tlwe, _) = TLWE::new_key(&mut rng, &param.lwe())?; //param.lwe() so that it uses K*N
debug_assert_eq!(sk_tlwe.0 .0.r.len(), param.lwe().k); // =KN (sanity check) 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 sk = sk_tlwe.to_tglwe(param);
let pk: PublicKey = GLWE::pk_from_sk(rng, param, sk.0.clone())?; let pk: PublicKey = GLWE::pk_from_sk(rng, param, sk.0.clone())?;
Ok((sk, pk)) Ok((sk, pk))
@ -60,7 +51,6 @@ impl TGLWE {
let p = param.t; let p = param.t;
let delta = u64::MAX / p; // floored let delta = u64::MAX / p; // floored
let coeffs = m.coeffs(); let coeffs = m.coeffs();
// Tn(array::from_fn(|i| T64(coeffs[i].0 * delta)))
Tn { Tn {
param: param.ring, param: param.ring,
coeffs: coeffs.iter().map(|c_i| T64(c_i.v * delta)).collect(), coeffs: coeffs.iter().map(|c_i| T64(c_i.v * delta)).collect(),
@ -72,7 +62,7 @@ impl TGLWE {
Rq::from_vec_u64(&param.pt(), pt.coeffs().iter().map(|c| c.0).collect()) Rq::from_vec_u64(&param.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> { 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)?; let glwe = GLWE::encrypt_s(rng, param, &sk.0, p)?;
Ok(Self(glwe)) Ok(Self(glwe))
@ -141,11 +131,6 @@ impl Sum for TGLWE {
where where
I: Iterator<Item = Self>, I: Iterator<Item = Self>,
{ {
// let mut acc = TGLWE::<N, K>::zero();
// for e in iter {
// acc += e;
// }
// acc
let first = iter.next().unwrap(); let first = iter.next().unwrap();
iter.fold(first, |acc, e| acc + e) iter.fold(first, |acc, e| acc + e)
} }
@ -208,6 +193,7 @@ mod tests {
#[test] #[test]
fn test_encrypt_decrypt() -> Result<()> { fn test_encrypt_decrypt() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 64 }, ring: RingParam { q: u64::MAX, n: 64 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -242,6 +228,7 @@ mod tests {
#[test] #[test]
fn test_addition() -> Result<()> { fn test_addition() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 64 }, ring: RingParam { q: u64::MAX, n: 64 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -275,6 +262,7 @@ mod tests {
#[test] #[test]
fn test_add_plaintext() -> Result<()> { fn test_add_plaintext() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 64 }, ring: RingParam { q: u64::MAX, n: 64 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -307,6 +295,7 @@ mod tests {
#[test] #[test]
fn test_mul_plaintext() -> Result<()> { fn test_mul_plaintext() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 64 }, ring: RingParam { q: u64::MAX, n: 64 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -342,6 +331,7 @@ mod tests {
#[test] #[test]
fn test_sample_extraction() -> Result<()> { fn test_sample_extraction() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 64 }, ring: RingParam { q: u64::MAX, n: 64 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus

+ 8
- 9
tfhe/src/tgsw.rs

@ -1,17 +1,13 @@
use anyhow::Result; use anyhow::Result;
use itertools::zip_eq; use itertools::zip_eq;
use rand::Rng; 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::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] /// vector of length K+1 = [K], [1]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -73,10 +69,12 @@ mod tests {
use rand::distributions::Uniform; use rand::distributions::Uniform;
use super::*; use super::*;
use arith::{RingParam, Rq};
#[test] #[test]
fn test_encrypt_decrypt() -> Result<()> { fn test_encrypt_decrypt() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 16, k: 16,
t: 2, // plaintext modulus t: 2, // plaintext modulus
@ -106,6 +104,7 @@ mod tests {
#[test] #[test]
fn test_external_product() -> Result<()> { fn test_external_product() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 32, k: 32,
t: 2, // plaintext modulus t: 2, // plaintext modulus
@ -130,7 +129,6 @@ mod tests {
let res: TLWE = tgsw * tlwe; let res: TLWE = tgsw * tlwe;
// let p_recovered = res.decrypt(&sk, beta);
let p_recovered = res.decrypt(&sk); let p_recovered = res.decrypt(&sk);
// downscaled by delta^-1 // downscaled by delta^-1
let res_recovered = TLWE::decode(&param, &p_recovered); let res_recovered = TLWE::decode(&param, &p_recovered);
@ -145,6 +143,7 @@ mod tests {
#[test] #[test]
fn test_cmux() -> Result<()> { fn test_cmux() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 32, k: 32,
t: 2, // plaintext modulus t: 2, // plaintext modulus

+ 17
- 13
tfhe/src/tlev.rs

@ -1,12 +1,10 @@
use anyhow::Result; use anyhow::Result;
use itertools::zip_eq; use itertools::zip_eq;
use rand::Rng; 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 crate::tlwe::{PublicKey, SecretKey, TLWE};
use gfhe::glwe::Param; use gfhe::glwe::Param;
@ -23,7 +21,6 @@ impl TLev {
} }
pub fn decode(param: &Param, p: &T64) -> Rq { pub fn decode(param: &Param, p: &T64) -> Rq {
Rq::from_vec_u64( Rq::from_vec_u64(
// &RingParam { q: u64::MAX, n: 1 },
&RingParam { q: param.t, n: 1 }, &RingParam { q: param.t, n: 1 },
p.coeffs().iter().map(|c| c.0).collect(), p.coeffs().iter().map(|c| c.0).collect(),
) )
@ -38,14 +35,16 @@ impl TLev {
) -> Result<Self> { ) -> Result<Self> {
debug_assert_eq!(pk.1.k, param.k); 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| { .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<_>>>()?; .collect::<Result<Vec<_>>>()?;
@ -59,6 +58,8 @@ impl TLev {
sk: &SecretKey, sk: &SecretKey,
m: &T64, m: &T64,
) -> Result<Self> { ) -> Result<Self> {
debug_assert_eq!(sk.0 .0.k, param.k);
let tlev: Vec<TLWE> = (1..l as u64 + 1) let tlev: Vec<TLWE> = (1..l as u64 + 1)
.map(|i| { .map(|i| {
let aux = if i < 64 { let aux = if i < 64 {
@ -113,6 +114,7 @@ mod tests {
#[test] #[test]
fn test_encrypt_decrypt() -> Result<()> { fn test_encrypt_decrypt() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 16, k: 16,
t: 2, // plaintext modulus t: 2, // plaintext modulus
@ -143,13 +145,15 @@ mod tests {
#[test] #[test]
fn test_tlev_vect64_product() -> Result<()> { fn test_tlev_vect64_product() -> Result<()> {
let param = Param { let param = Param {
err_sigma: 0.1, // WIP
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 16, k: 16,
t: 2, // plaintext modulus t: 2, // plaintext modulus
}; };
let beta: u32 = 2; let beta: u32 = 2;
let l: u32 = 16;
// let l: u32 = 16;
let l: u32 = 64;
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let msg_dist = Uniform::new(0_u64, param.t); let msg_dist = Uniform::new(0_u64, param.t);

+ 11
- 18
tfhe/src/tlwe.rs

@ -49,11 +49,10 @@ impl TLWE {
Ok((SecretKey(sk), pk)) Ok((SecretKey(sk), pk))
} }
// TODO use param instead of p:u64
pub fn encode(param: &Param, m: &Rq) -> T64 { pub fn encode(param: &Param, m: &Rq) -> T64 {
assert_eq!(param.ring.n, 1); assert_eq!(param.ring.n, 1);
debug_assert_eq!(param.t, m.param.q); // plaintext modulus debug_assert_eq!(param.t, m.param.q); // plaintext modulus
//
let delta = u64::MAX / param.t; // floored let delta = u64::MAX / param.t; // floored
let coeffs = m.coeffs(); let coeffs = m.coeffs();
T64(coeffs[0].v * delta) T64(coeffs[0].v * delta)
@ -112,14 +111,14 @@ impl TLWE {
Self(GLWE(a, b)) Self(GLWE(a, b))
} }
} }
// NOTE: the ugly const generics are temporary
pub fn blind_rotation( pub fn blind_rotation(
param: &Param, param: &Param,
c: TLWE, // kn c: TLWE, // kn
btk: BootstrappingKey, btk: BootstrappingKey,
table: TGLWE, // n,k table: TGLWE, // n,k
) -> TGLWE { ) -> 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()` // TODO replace `param.k*param.ring.n` by `param.kn()`
let c_kn: TLWE = c.mod_switch((param.k * param.ring.n) as u64); let c_kn: TLWE = c.mod_switch((param.k * param.ring.n) as u64);
@ -138,7 +137,6 @@ pub fn blind_rotation(
c_j.clone(), c_j.clone(),
c_j.clone().left_rotate(a.r[j].0 as usize), c_j.clone().left_rotate(a.r[j].0 as usize),
); );
// dbg!(&c_j);
}); });
c_j c_j
} }
@ -164,7 +162,7 @@ pub struct BootstrappingKey(
impl BootstrappingKey { impl BootstrappingKey {
pub fn from_sk(mut rng: impl Rng, param: &Param, sk: &tglwe::SecretKey) -> Result<Self> { pub fn from_sk(mut rng: impl Rng, param: &Param, sk: &tglwe::SecretKey) -> Result<Self> {
let (beta, l) = (2u32, 64u32); // TMP let (beta, l) = (2u32, 64u32); // TMP
//
let s: TR<Tn> = sk.0 .0.clone(); let s: TR<Tn> = sk.0 .0.clone();
let (sk2, _) = TLWE::new_key(&mut rng, &param.lwe())?; // TLWE<KN> compatible with TGLWE<N,K> let (sk2, _) = TLWE::new_key(&mut rng, &param.lwe())?; // TLWE<KN> compatible with TGLWE<N,K>
@ -229,11 +227,6 @@ impl Sum for TLWE {
where where
I: Iterator<Item = Self>, I: Iterator<Item = Self>,
{ {
// let mut acc = TLWE::<K>::zero();
// for e in iter {
// acc += e;
// }
// acc
let first = iter.next().unwrap(); let first = iter.next().unwrap();
iter.fold(first, |acc, e| acc + e) iter.fold(first, |acc, e| acc + e)
} }
@ -290,6 +283,7 @@ mod tests {
#[test] #[test]
fn test_encrypt_decrypt() -> Result<()> { fn test_encrypt_decrypt() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -324,6 +318,7 @@ mod tests {
#[test] #[test]
fn test_addition() -> Result<()> { fn test_addition() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -357,6 +352,7 @@ mod tests {
#[test] #[test]
fn test_add_plaintext() -> Result<()> { fn test_add_plaintext() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -389,6 +385,7 @@ mod tests {
#[test] #[test]
fn test_mul_plaintext() -> Result<()> { fn test_mul_plaintext() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -404,7 +401,6 @@ mod tests {
let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
let p1: T64 = TLWE::encode(&param, &m1); let p1: T64 = TLWE::encode(&param, &m1);
// don't scale up p2, set it directly from m2 // 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 p2: T64 = T64(m2.coeffs()[0].v);
let c1 = TLWE::encrypt(&mut rng, &param, &pk, &p1)?; let c1 = TLWE::encrypt(&mut rng, &param, &pk, &p1)?;
@ -422,6 +418,7 @@ mod tests {
#[test] #[test]
fn test_key_switch() -> Result<()> { fn test_key_switch() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { q: u64::MAX, n: 1 }, ring: RingParam { q: u64::MAX, n: 1 },
k: 16, k: 16,
t: 128, // plaintext modulus t: 128, // plaintext modulus
@ -440,7 +437,7 @@ mod tests {
let msg_dist = Uniform::new(0_u64, param.t); let msg_dist = Uniform::new(0_u64, param.t);
let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
let p = TLWE::encode(&param, &m); // plaintext let p = TLWE::encode(&param, &m); // plaintext
//
let c = TLWE::encrypt_s(&mut rng, &param, &sk, &p)?; let c = TLWE::encrypt_s(&mut rng, &param, &sk, &p)?;
let c2 = c.key_switch(&param, beta, l, &ksk); let c2 = c.key_switch(&param, beta, l, &ksk);
@ -463,6 +460,7 @@ mod tests {
#[test] #[test]
fn test_bootstrapping() -> Result<()> { fn test_bootstrapping() -> Result<()> {
let param = Param { let param = Param {
err_sigma: crate::ERR_SIGMA,
ring: RingParam { ring: RingParam {
q: u64::MAX, q: u64::MAX,
n: 1024, n: 1024,
@ -470,10 +468,6 @@ mod tests {
k: 1, k: 1,
t: 128, // plaintext modulus 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 mut rng = rand::thread_rng();
let start = Instant::now(); let start = Instant::now();
@ -494,7 +488,6 @@ mod tests {
let c = TLWE::encrypt_s(&mut rng, &param.lwe(), &sk_tlwe, &p)?; let c = TLWE::encrypt_s(&mut rng, &param.lwe(), &sk_tlwe, &p)?;
let start = Instant::now(); let start = Instant::now();
// the ugly const generics are temporary
let bootstrapped: TLWE = bootstrapping(&param, btk, table, c); let bootstrapped: TLWE = bootstrapping(&param, btk, table, c);
println!("bootstrapping took: {:?}", start.elapsed()); println!("bootstrapping took: {:?}", start.elapsed());

Loading…
Cancel
Save