arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
gfhe: get rid of constant generics
arnaucube git@arnaucube.com
Tue, 12 Aug 2025 17:10:20 +0000
commit

2a9cbc71dec629a1c9ac2288a1a435b255393635

parent

9e90f094a9cb82401cb58378edca4f4aff1c425a

M arith/src/lib.rs → arith/src/lib.rs

@@ -6,14 +6,14 @@ #![allow(dead_code)] // TMP

pub mod complex; pub mod matrix; -// pub mod torus; +pub mod torus; pub mod zq; pub mod ring; pub mod ring_n; pub mod ring_nq; -// pub mod ring_torus; -// pub mod tuple_ring; +pub mod ring_torus; +pub mod tuple_ring; // mod naive_ntt; // note: for dev only pub mod ntt;

@@ -22,13 +22,13 @@ // expose objects

pub use complex::C; pub use matrix::Matrix; -// pub use torus::T64; +pub use torus::T64; pub use zq::Zq; pub use ring::{Ring, RingParam}; pub use ring_n::R; pub use ring_nq::Rq; -// pub use ring_torus::Tn; -// pub use tuple_ring::TR; +pub use ring_torus::Tn; +pub use tuple_ring::TR; pub use ntt::NTT;
M arith/src/ntt.rs → arith/src/ntt.rs

@@ -241,4 +241,27 @@ 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, &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(()) + // } }
M arith/src/ring_torus.rs → arith/src/ring_torus.rs

@@ -13,51 +13,62 @@ use std::array;

use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}; -use crate::{ring::Ring, torus::T64, Rq, Zq}; +use crate::{ + ring::{Ring, RingParam}, + torus::T64, + Rq, Zq, +}; /// 𝕋_<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 n: usize, + pub param: RingParam, pub coeffs: Vec<T64>, } impl Ring for Tn { type C = T64; - type Param = usize; // n + // type Param = usize; // n // const Q: u64 = u64::MAX; // WIP // const N: usize = N; - fn param(&self) -> Self::Param { - self.n + fn param(&self) -> RingParam { + RingParam { + q: u64::MAX, + n: self.param.n, + } } fn coeffs(&self) -> Vec<T64> { self.coeffs.to_vec() } - fn zero(n: usize) -> Self { + fn zero(param: &RingParam) -> Self { Self { - n, - coeffs: vec![T64::zero(()); n], + param: *param, + coeffs: vec![T64::zero(param); param.n], } } - fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, n: usize) -> Self { + fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, param: &RingParam) -> Self { Self { - n, - coeffs: std::iter::repeat_with(|| T64::rand(&mut rng, &dist, ())) - .take(n) + param: *param, + coeffs: std::iter::repeat_with(|| T64::rand(&mut rng, &dist, &param)) + .take(param.n) .collect(), } // Self(array::from_fn(|_| T64::rand(&mut rng, &dist))) } - fn from_vec(n: usize, coeffs: Vec<Self::C>) -> Self { + fn from_vec(param: &RingParam, coeffs: Vec<Self::C>) -> Self { let mut p = coeffs; - modulus(n, &mut p); - Self { n, coeffs: p } + modulus(param, &mut p); + Self { + param: *param, + coeffs: p, + } } fn decompose(&self, beta: u32, l: u32) -> Vec<Self> {

@@ -68,7 +79,7 @@ .map(|i| (0..elems.len()).map(|j| elems[j][i]).collect())

.collect(); // convert it to Tn r.iter() - .map(|a_i| Self::from_vec(self.n, a_i.clone())) + .map(|a_i| Self::from_vec(&self.param, a_i.clone())) .collect() }

@@ -87,8 +98,10 @@ .iter()

.map(|c_i| Zq::from_u64(p, c_i.mod_switch(p).0)) .collect(); Rq { - q: p, - n: self.n, + param: RingParam { + q: p, + n: self.param.n, + }, coeffs, evals: None, }

@@ -103,14 +116,14 @@ .coeffs()

.iter() .map(|e| T64(((num as f64 * e.0 as f64) / den as f64).round() as u64)) .collect(); - Self::from_vec(self.n, r) + Self::from_vec(&self.param, r) } } impl Tn { // multiply self by X^-h pub fn left_rotate(&self, h: usize) -> Self { - let n = self.n; + let n = self.param.n; let h = h % n; assert!(h < n);

@@ -122,23 +135,24 @@ .iter()

.copied() .chain(c[0..h].iter().map(|&x| -x)) .collect(); - Self::from_vec(self.n, r) + Self::from_vec(&self.param, r) } - pub fn from_vec_u64(n: usize, v: Vec<u64>) -> Self { + pub fn from_vec_u64(param: &RingParam, v: Vec<u64>) -> Self { let coeffs = v.iter().map(|c| T64(*c)).collect(); - Self::from_vec(n, coeffs) + Self::from_vec(param, coeffs) } } // apply mod (X^N+1) -pub fn modulus(n: usize, p: &mut Vec<T64>) { +pub fn modulus(param: &RingParam, p: &mut Vec<T64>) { + let n = param.n; if p.len() < n { return; } for i in n..p.len() { p[i - n] = p[i - n].clone() - p[i].clone(); - p[i] = T64::zero(()); + p[i] = T64::zero(param); } p.truncate(n); }

@@ -148,9 +162,9 @@ type Output = Self;

fn add(self, rhs: Self) -> Self { // Self(array::from_fn(|i| self.0[i] + rhs.0[i])) - assert_eq!(self.n, rhs.n); + assert_eq!(self.param, rhs.param); Self { - n: self.n, + param: self.param, coeffs: zip_eq(self.coeffs, rhs.coeffs) .map(|(l, r)| l + r) .collect(),

@@ -162,9 +176,9 @@ type Output = Tn;

fn add(self, rhs: &Tn) -> Self::Output { // Tn(array::from_fn(|i| self.0[i] + rhs.0[i])) - assert_eq!(self.n, rhs.n); + assert_eq!(self.param, rhs.param); Tn { - n: self.n, + param: self.param, coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone()) .map(|(l, r)| l + r) .collect(),

@@ -173,15 +187,15 @@ }

} impl AddAssign for Tn { fn add_assign(&mut self, rhs: Self) { - assert_eq!(self.n, rhs.n); - for i in 0..self.n { + assert_eq!(self.param, rhs.param); + for i in 0..self.param.n { self.coeffs[i] += rhs.coeffs[i]; } } } impl Sum<Tn> for Tn { - fn sum<I>(iter: I) -> Self + fn sum<I>(mut iter: I) -> Self where I: Iterator<Item = Self>, {

@@ -190,7 +204,7 @@ // for e in iter {

// acc += e; // } // acc - let first = *iter.next().unwrap().borrow(); + let first = iter.next().unwrap(); iter.fold(first, |acc, x| acc + x) } }

@@ -199,9 +213,9 @@ impl Sub<Tn> for Tn {

type Output = Self; fn sub(self, rhs: Self) -> Self { - assert_eq!(self.n, rhs.n); + assert_eq!(self.param, rhs.param); Self { - n: self.n, + param: self.param, coeffs: zip_eq(self.coeffs, rhs.coeffs) .map(|(l, r)| l - r) .collect(),

@@ -213,9 +227,9 @@ type Output = Tn;

fn sub(self, rhs: &Tn) -> Self::Output { // Tn(array::from_fn(|i| self.0[i] - rhs.0[i])) - assert_eq!(self.n, rhs.n); + assert_eq!(self.param, rhs.param); Tn { - n: self.n, + param: self.param, coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone()) .map(|(l, r)| l - r) .collect(),

@@ -228,8 +242,8 @@ fn sub_assign(&mut self, rhs: Self) {

// for i in 0..N { // self.0[i] -= rhs.0[i]; // } - assert_eq!(self.n, rhs.n); - for i in 0..self.n { + assert_eq!(self.param, rhs.param); + for i in 0..self.param.n { self.coeffs[i] -= rhs.coeffs[i]; } }

@@ -241,7 +255,7 @@

fn neg(self) -> Self::Output { // Tn(array::from_fn(|i| -self.0[i])) Self { - n: self.n, + param: self.param, coeffs: self.coeffs.iter().map(|c_i| -*c_i).collect(), } }

@@ -249,7 +263,7 @@ }

impl PartialEq for Tn { fn eq(&self, other: &Self) -> bool { - self.coeffs == other.coeffs && self.n == other.n + self.coeffs == other.coeffs && self.param == other.param } }

@@ -269,8 +283,9 @@ }

} fn naive_poly_mul(poly1: &Tn, poly2: &Tn) -> Tn { - assert_eq!(poly1.n, poly2.n); - let n = poly1.n; + assert_eq!(poly1.param, poly2.param); + let n = poly1.param.n; + let param = poly1.param; let poly1: Vec<u128> = poly1.coeffs.iter().map(|c| c.0 as u128).collect(); let poly2: Vec<u128> = poly2.coeffs.iter().map(|c| c.0 as u128).collect();

@@ -285,7 +300,7 @@ // apply mod (X^n + 1))

modulus_u128(n, &mut result); Tn { - n, + param, // coeffs: array::from_fn(|i| T64(result[i] as u64)), coeffs: result.iter().map(|r_i| T64(*r_i as u64)).collect(), }

@@ -306,7 +321,7 @@ impl Mul<T64> for Tn {

type Output = Self; fn mul(self, s: T64) -> Self { Self { - n: self.n, + param: self.param, // coeffs: array::from_fn(|i| self.coeffs[i] * s), coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(), }

@@ -318,7 +333,7 @@ type Output = Self;

fn mul(self, s: u64) -> Self { // Self(array::from_fn(|i| self.0[i] * s)) Tn { - n: self.n, + param: self.param, coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(), } }

@@ -327,8 +342,8 @@ 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)) - Self { - n: self.n, + Tn { + param: self.param, coeffs: self.coeffs.iter().map(|c_i| c_i * s).collect(), } }

@@ -340,9 +355,9 @@ use super::*;

#[test] fn test_left_rotate() { - let n: usize = 4; + let param = RingParam { q: u64::MAX, n: 4 }; let f = Tn::from_vec( - n, + &param, vec![2i64, 3, -4, -1] .iter() .map(|c| T64(*c as u64))

@@ -353,7 +368,7 @@ // expect f*x^-3 == -1 -2x -3x^2 +4x^3

assert_eq!( f.left_rotate(3), Tn::from_vec( - n, + &param, vec![-1i64, -2, -3, 4] .iter() .map(|c| T64(*c as u64))

@@ -364,7 +379,7 @@ // expect f*x^-1 == 3 -4x -1x^2 -2x^3

assert_eq!( f.left_rotate(1), Tn::from_vec( - n, + &param, vec![3i64, -4, -1, -2] .iter() .map(|c| T64(*c as u64))
M arith/src/torus.rs → arith/src/torus.rs

@@ -4,7 +4,7 @@ iter::Sum,

ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; -use crate::ring::Ring; +use crate::ring::{Ring, RingParam}; /// Let 𝕋 = ℝ/β„€, where 𝕋 is a β„€-module, with homogeneous external product. /// Let 𝕋q

@@ -21,20 +21,23 @@

// const Q: u64 = u64::MAX; // WIP // const N: usize = 1; - fn param(&self) -> Self::Param { - () + fn param(&self) -> RingParam { + RingParam { + q: u64::MAX, // WIP + n: 1, + } } fn coeffs(&self) -> Vec<T64> { vec![self.clone()] } - fn zero(_: ()) -> Self { + fn zero(_: &RingParam) -> Self { Self(0u64) } - fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, _: ()) -> Self { + fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, _: &RingParam) -> Self { let r: f64 = dist.sample(&mut rng); Self(r.round() as u64) } - fn from_vec(_n: (), coeffs: Vec<Self::C>) -> Self { + fn from_vec(_n: &RingParam, coeffs: Vec<Self::C>) -> Self { assert_eq!(coeffs.len(), 1); coeffs[0] }

@@ -178,9 +181,13 @@ let x = T64(u64::MAX - 1);

let d = x.decompose(beta, l); assert_eq!(recompose(d), T64(u64::MAX - 1)); + let param = RingParam { + q: u64::MAX, // WIP + n: 1, + }; let mut rng = rand::thread_rng(); for _ in 0..1000 { - let x = T64::rand(&mut rng, Standard, ()); + let x = T64::rand(&mut rng, Standard, &param); let d = x.decompose(beta, l); assert_eq!(recompose(d), x); }
M arith/src/tuple_ring.rs → arith/src/tuple_ring.rs

@@ -113,7 +113,7 @@

fn neg(self) -> Self::Output { Self { k: self.k, - r: self.r.iter().map(|e_i| -*e_i).collect(), + r: self.r.iter().map(|e_i| -e_i.clone()).collect(), } } }
M gfhe/src/glev.rs → gfhe/src/glev.rs

@@ -6,23 +6,29 @@ use std::ops::{Add, Mul};

use arith::{Ring, TR}; -use crate::glwe::{PublicKey, SecretKey, GLWE}; +use crate::glwe::{Param, PublicKey, SecretKey, GLWE}; // l GLWEs #[derive(Clone, Debug)] -pub struct GLev<R: Ring, const K: usize>(pub(crate) Vec<GLWE<R, K>>); +pub struct GLev<R: Ring>(pub(crate) Vec<GLWE<R>>); -impl<R: Ring, const K: usize> GLev<R, K> { +impl<R: Ring> GLev<R> { pub fn encrypt( mut rng: impl Rng, + param: &Param, beta: u32, l: u32, - pk: &PublicKey<R, K>, + pk: &PublicKey<R>, m: &R, ) -> Result<Self> { - let glev: Vec<GLWE<R, K>> = (0..l) + let glev: Vec<GLWE<R>> = (0..l) .map(|i| { - GLWE::<R, K>::encrypt(&mut rng, pk, &(*m * (R::Q / beta.pow(i as u32) as u64))) + GLWE::<R>::encrypt( + &mut rng, + param, + pk, + &(m.clone() * (param.ring.q / beta.pow(i as u32) as u64)), + ) }) .collect::<Result<Vec<_>>>()?;

@@ -30,38 +36,46 @@ Ok(Self(glev))

} pub fn encrypt_s( mut rng: impl Rng, + param: &Param, beta: u32, l: u32, - sk: &SecretKey<R, K>, + sk: &SecretKey<R>, m: &R, // delta: u64, ) -> Result<Self> { - let glev: Vec<GLWE<R, K>> = (1..l + 1) + let glev: Vec<GLWE<R>> = (1..l + 1) .map(|i| { - GLWE::<R, K>::encrypt_s(&mut rng, sk, &(*m * (R::Q / beta.pow(i as u32) as u64))) + GLWE::<R>::encrypt_s( + &mut rng, + param, + sk, + &(m.clone() * (param.ring.q / beta.pow(i as u32) as u64)), // TODO rm clone + ) }) .collect::<Result<Vec<_>>>()?; Ok(Self(glev)) } - pub fn decrypt<const T: u64>(&self, sk: &SecretKey<R, K>, beta: u32) -> R { + pub fn decrypt(&self, param: &Param, sk: &SecretKey<R>, beta: u32) -> R { let pt = self.0[1].decrypt(sk); - pt.mul_div_round(beta as u64, R::Q) + pt.mul_div_round(beta as u64, param.ring.q) } } // dot product between a GLev and Vec<R>. // Used for operating decompositions with KSK_i. // GLev * Vec<R> --> GLWE -impl<R: Ring, const K: usize> Mul<Vec<R>> for GLev<R, K> { - type Output = GLWE<R, K>; - fn mul(self, v: Vec<R>) -> GLWE<R, K> { +impl<R: Ring> Mul<Vec<R>> for GLev<R> { + type Output = GLWE<R>; + fn mul(self, v: Vec<R>) -> GLWE<R> { + // TODO debug_assert_eq of params + // l times GLWES - let glwes: Vec<GLWE<R, K>> = self.0; + let glwes: Vec<GLWE<R>> = self.0; // l iterations - let r: GLWE<R, K> = zip_eq(v, glwes).map(|(v_i, glwe_i)| glwe_i * v_i).sum(); + let r: GLWE<R> = zip_eq(v, glwes).map(|(v_i, glwe_i)| glwe_i * v_i).sum(); r } }

@@ -72,33 +86,37 @@ use anyhow::Result;

use rand::distributions::Uniform; use super::*; - use arith::Rq; + use arith::{RingParam, Rq}; #[test] fn test_encrypt_decrypt() -> Result<()> { - const Q: u64 = 2u64.pow(16) + 1; - const N: usize = 128; - const T: u64 = 2; // plaintext modulus - const K: usize = 16; - type S = GLev<Rq<Q, N>, K>; + let param = Param { + ring: RingParam { + q: 2u64.pow(16) + 1, + n: 128, + }, + k: 16, + t: 2, // plaintext modulus + }; + type S = GLev<Rq>; 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, T); + let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { - let (sk, pk) = GLWE::<Rq<Q, N>, K>::new_key(&mut rng)?; + let (sk, pk) = GLWE::<Rq>::new_key(&mut rng, &param)?; - let m = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; - let m: Rq<Q, N> = m.remodule::<Q>(); + let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; + let m: Rq = m.remodule(param.ring.q); - let c = S::encrypt(&mut rng, beta, l, &pk, &m)?; - let m_recovered = c.decrypt::<T>(&sk, beta); + let c = S::encrypt(&mut rng, &param, beta, l, &pk, &m)?; + let m_recovered = c.decrypt(&param, &sk, beta); - assert_eq!(m.remodule::<T>(), m_recovered.remodule::<T>()); + assert_eq!(m.remodule(param.t), m_recovered.remodule(param.t)); } Ok(())
M gfhe/src/glwe.rs → gfhe/src/glwe.rs

@@ -8,79 +8,108 @@ use rand_distr::{Normal, Uniform};

use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, Sub}; -use arith::{Ring, Rq, Zq, TR}; +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 +#[derive(Clone, Copy, Debug)] +pub struct Param { + pub ring: RingParam, + pub k: usize, + pub t: u64, +} +impl Param { + // returns the plaintext params + pub fn pt(&self) -> RingParam { + RingParam { + q: self.t, + n: self.ring.n, + } + } +} + /// GLWE implemented over the `Ring` trait, so that it can be also instantiated /// over the Torus polynomials 𝕋_<N,q>[X] = 𝕋_q[X]/ (X^N+1). #[derive(Clone, Debug)] -pub struct GLWE<R: Ring, const K: usize>(pub TR<R, K>, pub R); +pub struct GLWE<R: Ring>(pub TR<R>, pub R); #[derive(Clone, Debug)] -pub struct SecretKey<R: Ring, const K: usize>(pub TR<R, K>); +pub struct SecretKey<R: Ring>(pub TR<R>); #[derive(Clone, Debug)] -pub struct PublicKey<R: Ring, const K: usize>(pub R, pub TR<R, K>); +pub struct PublicKey<R: Ring>(pub R, pub TR<R>); // K GLevs, each KSK_i=l GLWEs #[derive(Clone, Debug)] -pub struct KSK<R: Ring, const K: usize>(Vec<GLev<R, K>>); +pub struct KSK<R: Ring>(Vec<GLev<R>>); -impl<R: Ring, const K: usize> GLWE<R, K> { - pub fn zero() -> Self { - Self(TR::zero(), R::zero()) +impl<R: Ring> GLWE<R> { + pub fn zero(k: usize, params: &RingParam) -> Self { + Self(TR::zero(k, &params), R::zero(&params)) } - pub fn from_plaintext(p: R) -> Self { - Self(TR::zero(), p) + pub fn from_plaintext(k: usize, param: &RingParam, p: R) -> Self { + Self(TR::zero(k, &param), p) } - pub fn new_key(mut rng: impl Rng) -> Result<(SecretKey<R, K>, PublicKey<R, K>)> { + 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 s: TR<R, K> = TR::rand(&mut rng, Xi_key); - let a: TR<R, K> = TR::rand(&mut rng, Uniform::new(0_f64, R::Q as f64)); - let e = R::rand(&mut rng, Xi_err); + let s: TR<R> = TR::rand(&mut rng, Xi_key, param.k, &param.ring); + let a: TR<R> = TR::rand( + &mut rng, + Uniform::new(0_f64, param.ring.q as f64), + param.k, + &param.ring, + ); + let e = R::rand(&mut rng, Xi_err, &param.ring); - let pk: PublicKey<R, K> = PublicKey((&a * &s) + e, a); + let pk: PublicKey<R> = PublicKey((&a * &s) + e, a); Ok((SecretKey(s), pk)) } - pub fn pk_from_sk(mut rng: impl Rng, sk: SecretKey<R, K>) -> Result<PublicKey<R, K>> { + 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 a: TR<R, K> = TR::rand(&mut rng, Uniform::new(0_f64, R::Q as f64)); - let e = R::rand(&mut rng, Xi_err); + let a: TR<R> = TR::rand( + &mut rng, + Uniform::new(0_f64, param.ring.q as f64), + param.k, + &param.ring, + ); + let e = R::rand(&mut rng, Xi_err, &param.ring); - let pk: PublicKey<R, K> = PublicKey((&a * &sk.0) + e, a); + let pk: PublicKey<R> = PublicKey((&a * &sk.0) + e, a); Ok(pk) } pub fn new_ksk( mut rng: impl Rng, + param: &Param, beta: u32, l: u32, - sk: &SecretKey<R, K>, - new_sk: &SecretKey<R, K>, - ) -> Result<KSK<R, K>> { - let r: Vec<GLev<R, K>> = (0..K) + sk: &SecretKey<R>, + new_sk: &SecretKey<R>, + ) -> Result<KSK<R>> { + debug_assert_eq!(param.k, sk.0.k); + let k = sk.0.k; + let r: Vec<GLev<R>> = (0..k) .into_iter() .map(|i| // treat sk_i as the msg being encrypted - GLev::<R, K>::encrypt_s(&mut rng, beta, l, &new_sk, &sk.0 .0[i])) + GLev::<R>::encrypt_s(&mut rng, param, beta, l, &new_sk, &sk.0 .r[i])) .collect::<Result<Vec<_>>>()?; Ok(KSK(r)) } - pub fn key_switch(&self, beta: u32, l: u32, ksk: &KSK<R, K>) -> Self { - let (a, b): (TR<R, K>, R) = (self.0.clone(), self.1); + pub fn key_switch(&self, param: &Param, beta: u32, l: u32, ksk: &KSK<R>) -> Self { + let (a, b): (TR<R>, R) = (self.0.clone(), self.1.clone()); // TODO rm clones - let lhs: GLWE<R, K> = GLWE(TR::zero(), b); + let lhs: GLWE<R> = GLWE(TR::zero(param.k, &param.ring), b); // K iterations, ksk.0 contains K times GLev - let rhs: GLWE<R, K> = zip_eq(a.0, ksk.0.clone()) + let rhs: GLWE<R> = zip_eq(a.r, ksk.0.clone()) .map(|(a_i, ksk_i)| ksk_i * a_i.decompose(beta, l)) // dot_product .sum();

@@ -90,121 +119,136 @@

// encrypts with the given SecretKey (instead of PublicKey) pub fn encrypt_s( mut rng: impl Rng, - sk: &SecretKey<R, K>, + param: &Param, + 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 a: TR<R, K> = TR::rand(&mut rng, Xi_key); - let e = R::rand(&mut rng, Xi_err); + 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 b: R = (&a * &sk.0) + *m + e; + let b: R = (&a * &sk.0) + m.clone() + e; // TODO rm clone Ok(Self(a, b)) } pub fn encrypt( mut rng: impl Rng, - pk: &PublicKey<R, K>, + param: &Param, + 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 u: R = R::rand(&mut rng, Xi_key); + let u: R = R::rand(&mut rng, Xi_key, &param.ring); - let e0 = R::rand(&mut rng, Xi_err); - let e1 = TR::<R, K>::rand(&mut rng, Xi_err); + let e0 = R::rand(&mut rng, Xi_err, &param.ring); + let e1 = TR::<R>::rand(&mut rng, Xi_err, param.k, &param.ring); - let b: R = pk.0.clone() * u.clone() + *m + e0; - let d: TR<R, K> = &pk.1 * &u + e1; + let b: R = pk.0.clone() * u.clone() + m.clone() + e0; // TODO rm clones + let d: TR<R> = &pk.1 * &u + e1; Ok(Self(d, b)) } // returns m' not downscaled - pub fn decrypt(&self, sk: &SecretKey<R, K>) -> R { - let (d, b): (TR<R, K>, R) = (self.0.clone(), self.1); + pub fn decrypt(&self, sk: &SecretKey<R>) -> R { + let (d, b): (TR<R>, R) = (self.0.clone(), self.1.clone()); let p: R = b - &d * &sk.0; p } } // Methods for when Ring=Rq<Q,N> -impl<const Q: u64, const N: usize, const K: usize> GLWE<Rq<Q, N>, K> { +impl GLWE<Rq> { // scale up - pub fn encode<const T: u64>(m: &Rq<T, N>) -> Rq<Q, N> { - let m = m.remodule::<Q>(); - let delta = Q / T; // floored + pub fn encode(param: &Param, m: &Rq) -> Rq { + debug_assert_eq!(param.t, m.param.q); + let m = m.remodule(param.ring.q); + let delta = param.ring.q / param.t; // floored m * delta } // scale down - pub fn decode<const T: u64>(m: &Rq<Q, N>) -> Rq<T, N> { - let r = m.mul_div_round(T, Q); - let r: Rq<T, N> = r.remodule::<T>(); + pub fn decode(param: &Param, m: &Rq) -> Rq { + let r = m.mul_div_round(param.t, param.ring.q); + let r: Rq = r.remodule(param.t); r } - pub fn mod_switch<const P: u64>(&self) -> GLWE<Rq<P, N>, K> { - let a: TR<Rq<P, N>, K> = TR(self - .0 - .0 - .iter() - .map(|r| r.mod_switch::<P>()) - .collect::<Vec<_>>()); - let b: Rq<P, N> = self.1.mod_switch::<P>(); + pub fn mod_switch(&self, p: u64) -> GLWE<Rq> { + let a: TR<Rq> = TR { + k: self.0.k, + r: self.0.r.iter().map(|r| r.mod_switch(p)).collect::<Vec<_>>(), + }; + let b: Rq = self.1.mod_switch(p); GLWE(a, b) } } -impl<R: Ring, const K: usize> Add<GLWE<R, K>> for GLWE<R, K> { +impl<R: Ring> Add<GLWE<R>> for GLWE<R> { type Output = Self; fn add(self, other: Self) -> Self { - let a: TR<R, K> = self.0 + other.0; + let a: TR<R> = self.0 + other.0; let b: R = self.1 + other.1; Self(a, b) } } -impl<R: Ring, const K: usize> Add<R> for GLWE<R, K> { +impl<R: Ring> Add<R> for GLWE<R> { type Output = Self; fn add(self, plaintext: R) -> Self { - let a: TR<R, K> = self.0; + let a: TR<R> = self.0; let b: R = self.1 + plaintext; Self(a, b) } } -impl<R: Ring, const K: usize> AddAssign for GLWE<R, K> { +impl<R: Ring> AddAssign for GLWE<R> { fn add_assign(&mut self, rhs: Self) { - for i in 0..K { - self.0 .0[i] = self.0 .0[i].clone() + rhs.0 .0[i].clone(); + debug_assert_eq!(self.0.k, rhs.0.k); + debug_assert_eq!(self.1.param(), rhs.1.param()); + + let k = self.0.k; + for i in 0..k { + self.0.r[i] = self.0.r[i].clone() + rhs.0.r[i].clone(); } self.1 = self.1.clone() + rhs.1.clone(); } } -impl<R: Ring, const K: usize> Sum<GLWE<R, K>> for GLWE<R, K> { - fn sum<I>(iter: I) -> Self +impl<R: Ring> Sum<GLWE<R>> for GLWE<R> { + fn sum<I>(mut iter: I) -> Self where I: Iterator<Item = Self>, { - let mut acc = GLWE::<R, K>::zero(); - for e in iter { - acc += e; - } - acc + // 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) } } -impl<R: Ring, const K: usize> Sub<GLWE<R, K>> for GLWE<R, K> { +impl<R: Ring> Sub<GLWE<R>> for GLWE<R> { type Output = Self; fn sub(self, other: Self) -> Self { - let a: TR<R, K> = self.0 - other.0; + let a: TR<R> = self.0 - other.0; let b: R = self.1 - other.1; Self(a, b) } } -impl<R: Ring, const K: usize> Mul<R> for GLWE<R, K> { +impl<R: Ring> Mul<R> for GLWE<R> { type Output = Self; fn mul(self, plaintext: R) -> Self { - let a: TR<R, K> = TR(self.0 .0.iter().map(|r_i| *r_i * plaintext).collect()); + let a: TR<R> = TR { + k: self.0.k, + r: self + .0 + .r + .iter() + .map(|r_i| r_i.clone() * plaintext.clone()) + .collect(), + }; let b: R = self.1 * plaintext; Self(a, b) }

@@ -255,77 +299,93 @@

use super::*; #[test] - fn test_encrypt_decrypt() -> Result<()> { - const Q: u64 = 2u64.pow(16) + 1; - const N: usize = 128; - const T: u64 = 32; // plaintext modulus - const K: usize = 16; - type S = GLWE<Rq<Q, N>, K>; + fn test_encrypt_decrypt_ring_nq() -> Result<()> { + let param = Param { + ring: RingParam { + q: 2u64.pow(16) + 1, + n: 128, + }, + k: 16, + t: 32, // plaintext modulus + }; + // let k: usize = 16; + type S = GLWE<Rq>; let mut rng = rand::thread_rng(); - let msg_dist = Uniform::new(0_u64, T); + let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { - let (sk, pk) = S::new_key(&mut rng)?; + let (sk, pk) = S::new_key(&mut rng, &param)?; - let m = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; // msg - // let m: Rq<Q, N> = m.remodule::<Q>(); + let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; // msg + // let m: Rq<Q, N> = m.remodule::<Q>(); - let p = S::encode::<T>(&m); // plaintext - let c = S::encrypt(&mut rng, &pk, &p)?; // ciphertext + let p = S::encode(&param, &m); // plaintext + let c = S::encrypt(&mut rng, &param, &pk, &p)?; // ciphertext let p_recovered = c.decrypt(&sk); - let m_recovered = S::decode::<T>(&p_recovered); + let m_recovered = S::decode(&param, &p_recovered); - assert_eq!(m.remodule::<T>(), m_recovered.remodule::<T>()); + assert_eq!(m.remodule(param.t), m_recovered.remodule(param.t)); // same but using encrypt_s (with sk instead of pk)) - let c = S::encrypt_s(&mut rng, &sk, &p)?; + let c = S::encrypt_s(&mut rng, &param, &sk, &p)?; let p_recovered = c.decrypt(&sk); - let m_recovered = S::decode::<T>(&p_recovered); + let m_recovered = S::decode(&param, &p_recovered); - assert_eq!(m.remodule::<T>(), m_recovered.remodule::<T>()); + assert_eq!(m.remodule(param.t), m_recovered.remodule(param.t)); } Ok(()) } use arith::{Tn, T64}; - use std::array; - pub fn t_encode<const P: u64>(m: &Rq<P, 4>) -> Tn<4> { - let delta = u64::MAX / P; // floored + 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(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(), + } } - pub fn t_decode<const P: u64>(p: &Tn<4>) -> Rq<P, 4> { - let p = p.mul_div_round(P, u64::MAX); - Rq::<P, 4>::from_vec_u64(p.coeffs().iter().map(|c| c.0).collect()) + pub fn t_decode(param: &Param, pt: &Tn) -> Rq { + let p = param.t; + let pt = pt.mul_div_round(p, u64::MAX); + Rq::from_vec_u64(&param.pt(), pt.coeffs().iter().map(|c| c.0).collect()) } #[test] fn test_encrypt_decrypt_torus() -> Result<()> { - const N: usize = 128; - const T: u64 = 32; // plaintext modulus - const K: usize = 16; - type S = GLWE<Tn<4>, K>; + let param = Param { + ring: RingParam { + q: u64::MAX, + n: 128, + }, + k: 16, + t: 32, // plaintext modulus + }; + type S = GLWE<Tn>; let mut rng = rand::thread_rng(); - let msg_dist = Uniform::new(0_f64, T as f64); + let msg_dist = Uniform::new(0_f64, param.t as f64); for _ in 0..200 { - let (sk, pk) = S::new_key(&mut rng)?; + let (sk, pk) = S::new_key(&mut rng, &param)?; - let m = Rq::<T, 4>::rand(&mut rng, msg_dist); // msg + let m = Rq::rand(&mut rng, msg_dist, &param.pt()); // msg - let p = t_encode::<T>(&m); // plaintext - let c = S::encrypt(&mut rng, &pk, &p)?; // ciphertext + let p = t_encode(&param.ring, &m); // plaintext + let c = S::encrypt(&mut rng, &param, &pk, &p)?; // ciphertext let p_recovered = c.decrypt(&sk); - let m_recovered = t_decode::<T>(&p_recovered); + let m_recovered = t_decode(&param, &p_recovered); assert_eq!(m, m_recovered); // same but using encrypt_s (with sk instead of pk)) - let c = S::encrypt_s(&mut rng, &sk, &p)?; + let c = S::encrypt_s(&mut rng, &param, &sk, &p)?; let p_recovered = c.decrypt(&sk); - let m_recovered = t_decode::<T>(&p_recovered); + let m_recovered = t_decode(&param, &p_recovered); assert_eq!(m, m_recovered); }

@@ -335,32 +395,36 @@ }

#[test] fn test_addition() -> Result<()> { - const Q: u64 = 2u64.pow(16) + 1; - const N: usize = 128; - const T: u64 = 20; - const K: usize = 16; - type S = GLWE<Rq<Q, N>, K>; + let param = Param { + ring: RingParam { + q: 2u64.pow(16) + 1, + n: 128, + }, + k: 16, + t: 20, // plaintext modulus + }; + type S = GLWE<Rq>; let mut rng = rand::thread_rng(); - let msg_dist = Uniform::new(0_u64, T); + let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { - let (sk, pk) = S::new_key(&mut rng)?; + let (sk, pk) = S::new_key(&mut rng, &param)?; - let m1 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; - let m2 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; - let p1: Rq<Q, N> = S::encode::<T>(&m1); // plaintext - let p2: Rq<Q, N> = S::encode::<T>(&m2); // plaintext + let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; + let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; + let p1: Rq = S::encode(&param, &m1); // plaintext + let p2: Rq = S::encode(&param, &m2); // plaintext - let c1 = S::encrypt(&mut rng, &pk, &p1)?; - let c2 = S::encrypt(&mut rng, &pk, &p2)?; + let c1 = S::encrypt(&mut rng, &param, &pk, &p1)?; + let c2 = S::encrypt(&mut rng, &param, &pk, &p2)?; let c3 = c1 + c2; let p3_recovered = c3.decrypt(&sk); - let m3_recovered = S::decode::<T>(&p3_recovered); + let m3_recovered = S::decode(&param, &p3_recovered); - assert_eq!((m1 + m2).remodule::<T>(), m3_recovered.remodule::<T>()); + assert_eq!((m1 + m2).remodule(param.t), m3_recovered.remodule(param.t)); } Ok(())

@@ -368,31 +432,35 @@ }

#[test] fn test_add_plaintext() -> Result<()> { - const Q: u64 = 2u64.pow(16) + 1; - const N: usize = 128; - const T: u64 = 32; - const K: usize = 16; - type S = GLWE<Rq<Q, N>, K>; + let param = Param { + ring: RingParam { + q: 2u64.pow(16) + 1, + n: 128, + }, + k: 16, + t: 32, // plaintext modulus + }; + type S = GLWE<Rq>; let mut rng = rand::thread_rng(); - let msg_dist = Uniform::new(0_u64, T); + let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { - let (sk, pk) = S::new_key(&mut rng)?; + let (sk, pk) = S::new_key(&mut rng, &param)?; - let m1 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; - let m2 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; - let p1: Rq<Q, N> = S::encode::<T>(&m1); // plaintext - let p2: Rq<Q, N> = S::encode::<T>(&m2); // plaintext + let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; + let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; + let p1: Rq = S::encode(&param, &m1); // plaintext + let p2: Rq = S::encode(&param, &m2); // plaintext - let c1 = S::encrypt(&mut rng, &pk, &p1)?; + let c1 = S::encrypt(&mut rng, &param, &pk, &p1)?; let c3 = c1 + p2; let p3_recovered = c3.decrypt(&sk); - let m3_recovered = S::decode::<T>(&p3_recovered); + let m3_recovered = S::decode(&param, &p3_recovered); - assert_eq!((m1 + m2).remodule::<T>(), m3_recovered.remodule::<T>()); + assert_eq!((m1 + m2).remodule(param.t), m3_recovered.remodule(param.t)); } Ok(())

@@ -400,30 +468,34 @@ }

#[test] fn test_mul_plaintext() -> Result<()> { - const Q: u64 = 2u64.pow(16) + 1; - const N: usize = 16; - const T: u64 = 4; - const K: usize = 16; - type S = GLWE<Rq<Q, N>, K>; + let param = Param { + ring: RingParam { + q: 2u64.pow(16) + 1, + n: 16, + }, + k: 16, + t: 4, // plaintext modulus + }; + type S = GLWE<Rq>; let mut rng = rand::thread_rng(); - let msg_dist = Uniform::new(0_u64, T); + let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { - let (sk, pk) = S::new_key(&mut rng)?; + let (sk, pk) = S::new_key(&mut rng, &param)?; - let m1 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; - let m2 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; - let p1: Rq<Q, N> = S::encode::<T>(&m1); // plaintext - let p2 = m2.remodule::<Q>(); // notice we don't encode (scale by delta) + let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; + let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; + let p1: Rq = S::encode(&param, &m1); // plaintext + let p2 = m2.remodule(param.ring.q); // notice we don't encode (scale by delta) - let c1 = S::encrypt(&mut rng, &pk, &p1)?; + let c1 = S::encrypt(&mut rng, &param, &pk, &p1)?; let c3 = c1 * p2; - let p3_recovered: Rq<Q, N> = c3.decrypt(&sk); - let m3_recovered: Rq<T, N> = S::decode::<T>(&p3_recovered); - assert_eq!((m1.to_r() * m2.to_r()).to_rq::<T>(), m3_recovered); + let p3_recovered: Rq = c3.decrypt(&sk); + let m3_recovered: Rq = S::decode(&param, &p3_recovered); + assert_eq!((m1.to_r() * m2.to_r()).to_rq(param.t), m3_recovered); } Ok(())

@@ -431,33 +503,48 @@ }

#[test] fn test_mod_switch() -> Result<()> { - const Q: u64 = 2u64.pow(16) + 1; - const P: u64 = 2u64.pow(8) + 1; + let param = Param { + ring: RingParam { + q: 2u64.pow(16) + 1, + n: 8, + }, + k: 16, + t: 4, // plaintext modulus, must be a prime or power of a prime + }; + let new_q: u64 = 2u64.pow(8) + 1; // note: wip, Q and P chosen so that P/Q is an integer - const N: usize = 8; - const T: u64 = 4; // plaintext modulus, must be a prime or power of a prime - const K: usize = 16; - type S = GLWE<Rq<Q, N>, K>; + type S = GLWE<Rq>; let mut rng = rand::thread_rng(); - let msg_dist = Uniform::new(0_u64, T); + let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { - let (sk, pk) = S::new_key(&mut rng)?; + let (sk, pk) = S::new_key(&mut rng, &param)?; - let m = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; + let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; - let p = S::encode::<T>(&m); - let c = S::encrypt(&mut rng, &pk, &p)?; + let p = S::encode(&param, &m); + let c = S::encrypt(&mut rng, &param, &pk, &p)?; - let c2: GLWE<Rq<P, N>, K> = c.mod_switch::<P>(); - let sk2: SecretKey<Rq<P, N>, K> = - SecretKey(TR(sk.0 .0.iter().map(|s_i| s_i.remodule::<P>()).collect())); + let c2: GLWE<Rq> = c.mod_switch(new_q); + assert_eq!(c2.1.param.q, new_q); + let sk2: SecretKey<Rq> = SecretKey(TR { + k: param.k, + r: sk.0.r.iter().map(|s_i| s_i.remodule(new_q)).collect(), + }); let p_recovered = c2.decrypt(&sk2); - let m_recovered = GLWE::<Rq<P, N>, K>::decode::<T>(&p_recovered); + let new_param = Param { + ring: RingParam { + q: new_q, + n: param.ring.n, + }, + k: param.k, + t: param.t, + }; + let m_recovered = GLWE::<Rq>::decode(&new_param, &p_recovered); - assert_eq!(m.remodule::<T>(), m_recovered.remodule::<T>()); + assert_eq!(m.remodule(param.t), m_recovered.remodule(param.t)); } Ok(())

@@ -465,40 +552,44 @@ }

#[test] fn test_key_switch() -> Result<()> { - const Q: u64 = 2u64.pow(16) + 1; - const N: usize = 128; - const T: u64 = 2; // plaintext modulus - const K: usize = 16; - type S = GLWE<Rq<Q, N>, K>; + let param = Param { + ring: RingParam { + q: 2u64.pow(16) + 1, + n: 128, + }, + k: 16, + t: 2, + }; + type S = GLWE<Rq>; let beta: u32 = 2; let l: u32 = 16; let mut rng = rand::thread_rng(); - let (sk, pk) = S::new_key(&mut rng)?; - let (sk2, _) = S::new_key(&mut rng)?; + let (sk, pk) = S::new_key(&mut rng, &param)?; + let (sk2, _) = S::new_key(&mut rng, &param)?; // ksk to switch from sk to sk2 - let ksk = S::new_ksk(&mut rng, beta, l, &sk, &sk2)?; + let ksk = S::new_ksk(&mut rng, &param, beta, l, &sk, &sk2)?; - let msg_dist = Uniform::new(0_u64, T); - let m = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?; - let p = S::encode::<T>(&m); // plaintext - // - let c = S::encrypt_s(&mut rng, &sk, &p)?; + let msg_dist = Uniform::new(0_u64, param.t); + let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?; + let p = S::encode(&param, &m); // plaintext + // + let c = S::encrypt_s(&mut rng, &param, &sk, &p)?; - let c2 = c.key_switch(beta, l, &ksk); + let c2 = c.key_switch(&param, beta, l, &ksk); // decrypt with the 2nd secret key let p_recovered = c2.decrypt(&sk2); - let m_recovered = S::decode::<T>(&p_recovered); - assert_eq!(m.remodule::<T>(), m_recovered.remodule::<T>()); + let m_recovered = S::decode(&param, &p_recovered); + assert_eq!(m.remodule(param.t), m_recovered.remodule(param.t)); // do the same but now encrypting with pk - let c = S::encrypt(&mut rng, &pk, &p)?; - let c2 = c.key_switch(beta, l, &ksk); + let c = S::encrypt(&mut rng, &param, &pk, &p)?; + let c2 = c.key_switch(&param, beta, l, &ksk); let p_recovered = c2.decrypt(&sk2); - let m_recovered = S::decode::<T>(&p_recovered); + let m_recovered = S::decode(&param, &p_recovered); assert_eq!(m, m_recovered); Ok(())