use anyhow::Result; use rand::Rng; 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::{tlwe, tlwe::TLWE}; #[derive(Clone, Debug)] pub struct SecretKey(pub glwe::SecretKey); impl SecretKey { pub fn to_tlwe(&self, param: &Param) -> tlwe::SecretKey { let s: TR = self.0 .0.clone(); debug_assert_eq!(s.r.len(), param.k); // sanity check let kn = param.k * param.ring.n; let r: Vec> = s.r.iter().map(|s_i| s_i.coeffs()).collect(); let r: Vec = r.into_iter().flatten().collect(); debug_assert_eq!(r.len(), kn); // sanity check tlwe::SecretKey(glwe::SecretKey::(TR::::new(kn, r))) } } pub type PublicKey = glwe::PublicKey; #[derive(Clone, Debug)] pub struct TGLWE(pub GLWE); impl TGLWE { pub fn zero(k: usize, param: &RingParam) -> Self { Self(GLWE::::zero(k, param)) } pub fn from_plaintext(k: usize, param: &RingParam, p: Tn) -> Self { Self(GLWE::::from_plaintext(k, param, p)) } pub fn new_key(mut rng: impl Rng, param: &Param) -> Result<(SecretKey, PublicKey)> { 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 = sk_tlwe.to_tglwe(param); let pk: PublicKey = GLWE::pk_from_sk(rng, param, sk.0.clone())?; Ok((sk, pk)) } pub fn encode(param: &Param, m: &Rq) -> Tn { 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 { param: param.ring, coeffs: coeffs.iter().map(|c_i| T64(c_i.v * delta)).collect(), } } pub fn decode(param: &Param, pt: &Tn) -> Rq { let p = param.t; 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) pub fn encrypt_s(rng: impl Rng, param: &Param, sk: &SecretKey, p: &Tn) -> Result { let glwe = GLWE::encrypt_s(rng, param, &sk.0, p)?; Ok(Self(glwe)) } pub fn encrypt(rng: impl Rng, param: &Param, pk: &PublicKey, p: &Tn) -> Result { let glwe = GLWE::encrypt(rng, param, &pk, p)?; Ok(Self(glwe)) } pub fn decrypt(&self, sk: &SecretKey) -> Tn { self.0.decrypt(&sk.0) } /// Sample extraction / Coefficient extraction pub fn sample_extraction(&self, param: &Param, h: usize) -> TLWE { let n = param.ring.n; assert!(h < n); let a: TR = self.0 .0.clone(); // set a_{n*i+j} = a_{i, h-j} if j \in {0, h} // -a_{i, n+h-j} if j \in {h+1, n-1} let new_a: Vec = a .iter() .flat_map(|a_i| { let a_i = a_i.coeffs(); (0..n) .map(|j| if j <= h { a_i[h - j] } else { -a_i[n + h - j] }) .collect::>() }) .collect::>(); debug_assert_eq!(new_a.len(), param.k * param.ring.n); // sanity check TLWE(GLWE( TR { // TODO use constructor `new`, which will check len with k k: param.k * param.ring.n, r: new_a, }, self.0 .1.coeffs()[h], )) } pub fn left_rotate(&self, h: usize) -> Self { let (a, b): (TR, Tn) = (self.0 .0.clone(), self.0 .1.clone()); Self(GLWE(a.left_rotate(h), b.left_rotate(h))) } } impl Add for TGLWE { type Output = Self; fn add(self, other: Self) -> Self { debug_assert_eq!(self.0 .0.k, other.0 .0.k); debug_assert_eq!(self.0 .1.param(), other.0 .1.param()); Self(self.0 + other.0) } } impl AddAssign for TGLWE { fn add_assign(&mut self, other: Self) { debug_assert_eq!(self.0 .0.k, other.0 .0.k); debug_assert_eq!(self.0 .1.param(), other.0 .1.param()); self.0 += other.0 } } impl Sum for TGLWE { fn sum(mut iter: I) -> Self where I: Iterator, { let first = iter.next().unwrap(); iter.fold(first, |acc, e| acc + e) } } impl Sub for TGLWE { type Output = Self; fn sub(self, other: Self) -> Self { debug_assert_eq!(self.0 .0.k, other.0 .0.k); debug_assert_eq!(self.0 .1.param(), other.0 .1.param()); Self(self.0 - other.0) } } // plaintext addition impl Add for TGLWE { type Output = Self; fn add(self, plaintext: Tn) -> Self { debug_assert_eq!(self.0 .1.param(), plaintext.param()); let a: TR = self.0 .0; let b: Tn = self.0 .1 + plaintext; Self(GLWE(a, b)) } } // plaintext substraction impl Sub for TGLWE { type Output = Self; fn sub(self, plaintext: Tn) -> Self { debug_assert_eq!(self.0 .1.param(), plaintext.param()); let a: TR = self.0 .0; let b: Tn = self.0 .1 - plaintext; Self(GLWE(a, b)) } } // plaintext multiplication impl Mul for TGLWE { type Output = Self; fn mul(self, plaintext: Tn) -> Self { debug_assert_eq!(self.0 .1.param(), plaintext.param()); let a: TR = TR { k: self.0 .0.k, r: self.0 .0.r.iter().map(|r_i| r_i * &plaintext).collect(), }; let b: Tn = self.0 .1 * plaintext; Self(GLWE(a, b)) } } #[cfg(test)] mod tests { use anyhow::Result; use rand::distributions::Uniform; use super::*; #[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 }; let mut rng = rand::thread_rng(); let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { let (sk, pk) = TGLWE::new_key(&mut rng, ¶m)?; let m = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let p: Tn = TGLWE::encode(¶m, &m); let c = TGLWE::encrypt(&mut rng, ¶m, &pk, &p)?; let p_recovered = c.decrypt(&sk); let m_recovered = TGLWE::decode(¶m, &p_recovered); assert_eq!(m, m_recovered); // same but using encrypt_s (with sk instead of pk)) let c = TGLWE::encrypt_s(&mut rng, ¶m, &sk, &p)?; let p_recovered = c.decrypt(&sk); let m_recovered = TGLWE::decode(¶m, &p_recovered); assert_eq!(m, m_recovered); } Ok(()) } #[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 }; let mut rng = rand::thread_rng(); let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { let (sk, pk) = TGLWE::new_key(&mut rng, ¶m)?; let m1 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let m2 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let p1: Tn = TGLWE::encode(¶m, &m1); // plaintext let p2: Tn = TGLWE::encode(¶m, &m2); // plaintext let c1 = TGLWE::encrypt(&mut rng, ¶m, &pk, &p1)?; let c2 = TGLWE::encrypt(&mut rng, ¶m, &pk, &p2)?; let c3 = c1 + c2; let p3_recovered = c3.decrypt(&sk); let m3_recovered = TGLWE::decode(¶m, &p3_recovered); assert_eq!((m1 + m2).remodule(param.t), m3_recovered.remodule(param.t)); } Ok(()) } #[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 }; let mut rng = rand::thread_rng(); let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { let (sk, pk) = TGLWE::new_key(&mut rng, ¶m)?; let m1 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let m2 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let p1: Tn = TGLWE::encode(¶m, &m1); // plaintext let p2: Tn = TGLWE::encode(¶m, &m2); // plaintext let c1 = TGLWE::encrypt(&mut rng, ¶m, &pk, &p1)?; let c3 = c1 + p2; let p3_recovered = c3.decrypt(&sk); let m3_recovered = TGLWE::decode(¶m, &p3_recovered); assert_eq!(m1 + m2, m3_recovered); } Ok(()) } #[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 }; let mut rng = rand::thread_rng(); let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..200 { let (sk, pk) = TGLWE::new_key(&mut rng, ¶m)?; let m1 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let m2 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let p1: Tn = TGLWE::encode(¶m, &m1); // don't scale up p2, set it directly from m2 let p2: Tn = Tn { param: param.ring, coeffs: m2.coeffs().iter().map(|c_i| T64(c_i.v)).collect(), }; let c1 = TGLWE::encrypt(&mut rng, ¶m, &pk, &p1)?; let c3 = c1 * p2; let p3_recovered: Tn = c3.decrypt(&sk); let m3_recovered = TGLWE::decode(¶m, &p3_recovered); assert_eq!((m1.to_r() * m2.to_r()).to_rq(param.t), m3_recovered); } Ok(()) } #[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 }; let mut rng = rand::thread_rng(); let msg_dist = Uniform::new(0_u64, param.t); for _ in 0..20 { let (sk, pk) = TGLWE::new_key(&mut rng, ¶m)?; let sk_tlwe = sk.to_tlwe(¶m); let m = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?; let p: Tn = TGLWE::encode(¶m, &m); let c = TGLWE::encrypt(&mut rng, ¶m, &pk, &p)?; for h in 0..param.ring.n { let c_h: TLWE = c.sample_extraction(¶m, h); let p_recovered = c_h.decrypt(&sk_tlwe); let m_recovered = TLWE::decode(¶m, &p_recovered); assert_eq!(m.coeffs()[h], m_recovered.coeffs()[0]); } } Ok(()) } }