use anyhow::Result; use rand::Rng; use rand_distr::{Normal, Uniform}; use std::ops::Add; use arith::{Ring, Rq, TR}; const ERR_SIGMA: f64 = 3.2; pub struct GLWE(TR, K>, Rq); #[derive(Clone, Debug)] pub struct SecretKey(TR, K>); #[derive(Clone, Debug)] pub struct PublicKey(Rq, TR, K>); impl GLWE { pub fn new_key(mut rng: impl Rng) -> Result<(SecretKey, PublicKey)> { let Xi_key = Uniform::new(0_f64, 2_f64); let Xi_err = Normal::new(0_f64, ERR_SIGMA)?; let s: TR, K> = TR::rand(&mut rng, Xi_key); let a: TR, K> = TR::rand(&mut rng, Uniform::new(0_f64, Q as f64)); let e = Rq::::rand(&mut rng, Xi_err); let pk: PublicKey = PublicKey((&a * &s) + e, a); Ok((SecretKey(s), pk)) } // TODO delta not as input pub fn encrypt_s( mut rng: impl Rng, sk: &SecretKey, m: &Rq, delta: u64, ) -> Result { let m: Rq = m.remodule::(); let Xi_key = Uniform::new(0_f64, 2_f64); let Xi_err = Normal::new(0_f64, ERR_SIGMA)?; let a: TR, K> = TR::rand(&mut rng, Xi_key); let e = Rq::::rand(&mut rng, Xi_err); let b: Rq = (&a * &sk.0) + m * delta + e; Ok(Self(a, b)) } pub fn encrypt( mut rng: impl Rng, pk: &PublicKey, m: &Rq, delta: u64, ) -> Result { let m: Rq = m.remodule::(); let Xi_key = Uniform::new(0_f64, 2_f64); let Xi_err = Normal::new(0_f64, ERR_SIGMA)?; let u: Rq = Rq::rand(&mut rng, Xi_key); let e0 = Rq::::rand(&mut rng, Xi_err); let e1 = TR::, K>::rand(&mut rng, Xi_err); let b: Rq = pk.0 * u + m * delta + e0; let d: TR, K> = &pk.1 * &u + e1; Ok(Self(d, b)) } pub fn decrypt(&self, sk: &SecretKey, delta: u64) -> Rq { let (d, b): (TR, K>, Rq) = (self.0.clone(), self.1); let r: Rq = b - &d * &sk.0; let r_scaled: Vec = r .coeffs() .iter() .map(|e| (e.0 as f64 / delta as f64).round()) .collect(); let r = Rq::::from_vec_f64(r_scaled); r.remodule::() } } impl Add> for GLWE { type Output = Self; fn add(self, other: Self) -> Self { let a: TR, K> = self.0 + other.0; let b: Rq = self.1 + other.1; Self(a, b) } } impl Add> for GLWE { type Output = Self; fn add(self, plaintext: Rq) -> Self { let a: TR, K> = self.0; let b: Rq = self.1 + plaintext; Self(a, b) } } #[cfg(test)] mod tests { use anyhow::Result; use rand::distributions::Uniform; 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; let delta: u64 = Q / T; // floored let mut rng = rand::thread_rng(); for _ in 0..200 { let (sk, pk) = S::new_key(&mut rng)?; let msg_dist = Uniform::new(0_u64, T); let m = Rq::::rand_u64(&mut rng, msg_dist)?; let c = S::encrypt(&mut rng, &pk, &m, delta)?; let m_recovered = c.decrypt(&sk, delta); assert_eq!(m, m_recovered); } Ok(()) } #[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; let delta: u64 = Q / T; // floored let mut rng = rand::thread_rng(); for _ in 0..200 { let (sk, pk) = S::new_key(&mut rng)?; let msg_dist = Uniform::new(0_u64, T); let m1 = Rq::::rand_u64(&mut rng, msg_dist)?; let m2 = Rq::::rand_u64(&mut rng, msg_dist)?; let c1 = S::encrypt(&mut rng, &pk, &m1, delta)?; let c2 = S::encrypt(&mut rng, &pk, &m2, delta)?; let c3 = c1 + c2; let m3_recovered = c3.decrypt(&sk, delta); assert_eq!(m1 + m2, m3_recovered); } Ok(()) } #[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; let delta: u64 = Q / T; // floored let mut rng = rand::thread_rng(); for _ in 0..200 { let (sk, pk) = S::new_key(&mut rng)?; let msg_dist = Uniform::new(0_u64, T); let m1 = Rq::::rand_u64(&mut rng, msg_dist)?; let m2 = Rq::::rand_u64(&mut rng, msg_dist)?; let m2_scaled: Rq = m2.remodule::() * delta; let c1 = S::encrypt(&mut rng, &pk, &m1, delta)?; let c3 = c1 + m2_scaled; let m3_recovered = c3.decrypt(&sk, delta); assert_eq!(m1 + m2, m3_recovered); } Ok(()) } }