arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/tfhe/src/tlwe.rs
tfhe/src/tlwe.rsRaw
  1use anyhow::Result;
  2use itertools::zip_eq;
  3use rand::distributions::Standard;
  4use rand::Rng;
  5use rand_distr::{Normal, Uniform};
  6use std::array;
  7use std::iter::Sum;
  8use std::ops::{Add, AddAssign, Mul, Sub};
  9
 10use arith::{Ring, Rq, Tn, T64, TR};
 11
 12const ERR_SIGMA: f64 = 3.2;
 13
 14#[derive(Clone, Debug)]
 15pub struct TLWE<const K: usize>(TR<Tn<1>, K>, Tn<1>);
 16
 17#[derive(Clone, Debug)]
 18pub struct SecretKey<const K: usize>(TR<Tn<1>, K>);
 19#[derive(Clone, Debug)]
 20pub struct PublicKey<const K: usize>(Tn<1>, TR<Tn<1>, K>);
 21
 22impl<const K: usize> TLWE<K> {
 23    pub fn zero() -> Self {
 24        Self(TR::zero(), Tn::zero())
 25    }
 26
 27    pub fn new_key(mut rng: impl Rng) -> Result<(SecretKey<K>, PublicKey<K>)> {
 28        let Xi_key = Uniform::new(0_f64, 2_f64);
 29        let Xi_err = Normal::new(0_f64, ERR_SIGMA)?;
 30
 31        let s: TR<Tn<1>, K> = TR::rand(&mut rng, Xi_key);
 32        let a: TR<Tn<1>, K> = TR::rand(&mut rng, Standard);
 33        let e = Tn::rand(&mut rng, Xi_err);
 34
 35        let pk: PublicKey<K> = PublicKey((&a * &s) + e, a);
 36        Ok((SecretKey(s), pk))
 37    }
 38
 39    pub fn encode<const P: u64>(m: &Rq<P, 1>) -> Tn<1> {
 40        let delta = u64::MAX / P; // floored
 41        let coeffs = m.coeffs();
 42        Tn(array::from_fn(|i| T64(coeffs[i].0 * delta)))
 43    }
 44    pub fn decode<const P: u64>(p: &Tn<1>) -> Rq<P, 1> {
 45        let p = p.mul_div_round(P, u64::MAX);
 46        Rq::<P, 1>::from_vec_u64(p.coeffs().iter().map(|c| c.0).collect())
 47    }
 48
 49    // encrypts with the given SecretKey (instead of PublicKey)
 50    pub fn encrypt_s(mut rng: impl Rng, sk: &SecretKey<K>, m: &Tn<1>) -> Result<Self> {
 51        let Xi_key = Uniform::new(0_f64, 2_f64);
 52        let Xi_err = Normal::new(0_f64, ERR_SIGMA)?;
 53
 54        let a: TR<Tn<1>, K> = TR::rand(&mut rng, Xi_key);
 55        let e = Tn::rand(&mut rng, Xi_err);
 56
 57        let b: Tn<1> = (&a * &sk.0) + *m + e;
 58        Ok(Self(a, b))
 59    }
 60    pub fn encrypt(mut rng: impl Rng, pk: &PublicKey<K>, m: &Tn<1>) -> Result<Self> {
 61        let Xi_key = Uniform::new(0_f64, 2_f64);
 62        let Xi_err = Normal::new(0_f64, ERR_SIGMA)?;
 63
 64        let u: Tn<1> = Tn::rand(&mut rng, Xi_key);
 65
 66        let e0: Tn<1> = Tn::rand(&mut rng, Xi_err);
 67        let e1 = TR::<Tn<1>, K>::rand(&mut rng, Xi_err);
 68
 69        let b: Tn<1> = pk.0 * u + *m + e0;
 70        let d: TR<Tn<1>, K> = &pk.1 * &u + e1;
 71
 72        Ok(Self(d, b))
 73    }
 74    pub fn decrypt(&self, sk: &SecretKey<K>) -> Tn<1> {
 75        let (d, b): (TR<Tn<1>, K>, Tn<1>) = (self.0.clone(), self.1);
 76        b - &d * &sk.0
 77    }
 78}
 79
 80impl<const K: usize> Add<TLWE<K>> for TLWE<K> {
 81    type Output = Self;
 82    fn add(self, other: Self) -> Self {
 83        let a: TR<Tn<1>, K> = self.0 + other.0;
 84        let b: Tn<1> = self.1 + other.1;
 85        Self(a, b)
 86    }
 87}
 88impl<const K: usize> AddAssign for TLWE<K> {
 89    fn add_assign(&mut self, rhs: Self) {
 90        for i in 0..K {
 91            self.0 .0[i] = self.0 .0[i] + rhs.0 .0[i];
 92        }
 93        self.1 = self.1 + rhs.1;
 94    }
 95}
 96impl<const K: usize> Sum<TLWE<K>> for TLWE<K> {
 97    fn sum<I>(iter: I) -> Self
 98    where
 99        I: Iterator<Item = Self>,
100    {
101        let mut acc = TLWE::<K>::zero();
102        for e in iter {
103            acc += e;
104        }
105        acc
106    }
107}
108
109impl<const K: usize> Sub<TLWE<K>> for TLWE<K> {
110    type Output = Self;
111    fn sub(self, other: Self) -> Self {
112        let a: TR<Tn<1>, K> = self.0 - other.0;
113        let b: Tn<1> = self.1 - other.1;
114        Self(a, b)
115    }
116}
117
118// plaintext addition
119impl<const K: usize> Add<Tn<1>> for TLWE<K> {
120    type Output = Self;
121    fn add(self, plaintext: Tn<1>) -> Self {
122        let a: TR<Tn<1>, K> = self.0;
123        let b: Tn<1> = self.1 + plaintext;
124        Self(a, b)
125    }
126}
127// plaintext substraction
128impl<const K: usize> Sub<Tn<1>> for TLWE<K> {
129    type Output = Self;
130    fn sub(self, plaintext: Tn<1>) -> Self {
131        let a: TR<Tn<1>, K> = self.0;
132        let b: Tn<1> = self.1 - plaintext;
133        Self(a, b)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use anyhow::Result;
140    use rand::distributions::Uniform;
141
142    use super::*;
143
144    #[test]
145    fn test_encrypt_decrypt() -> Result<()> {
146        const T: u64 = 128; // msg space (msg modulus)
147        const K: usize = 16;
148        type S = TLWE<K>;
149
150        let mut rng = rand::thread_rng();
151
152        for _ in 0..200 {
153            let (sk, pk) = S::new_key(&mut rng)?;
154
155            let msg_dist = Uniform::new(0_u64, T);
156            let m = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
157            dbg!(&m);
158            let p: Tn<1> = S::encode::<T>(&m);
159            dbg!(&p);
160
161            let c = S::encrypt(&mut rng, &pk, &p)?;
162            let p_recovered = c.decrypt(&sk);
163            let m_recovered = S::decode::<T>(&p_recovered);
164
165            assert_eq!(m, m_recovered);
166
167            // same but using encrypt_s (with sk instead of pk))
168            let c = S::encrypt_s(&mut rng, &sk, &p)?;
169            let p_recovered = c.decrypt(&sk);
170            let m_recovered = S::decode::<T>(&p_recovered);
171
172            assert_eq!(m, m_recovered);
173        }
174
175        Ok(())
176    }
177
178    #[test]
179    fn test_addition() -> Result<()> {
180        const T: u64 = 128;
181        const K: usize = 16;
182        type S = TLWE<K>;
183
184        let mut rng = rand::thread_rng();
185
186        for _ in 0..200 {
187            let (sk, pk) = S::new_key(&mut rng)?;
188
189            let msg_dist = Uniform::new(0_u64, T);
190            let m1 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
191            let m2 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
192            let p1: Tn<1> = S::encode::<T>(&m1); // plaintext
193            let p2: Tn<1> = S::encode::<T>(&m2); // plaintext
194
195            let c1 = S::encrypt(&mut rng, &pk, &p1)?;
196            let c2 = S::encrypt(&mut rng, &pk, &p2)?;
197
198            let c3 = c1 + c2;
199
200            let p3_recovered = c3.decrypt(&sk);
201            let m3_recovered = S::decode::<T>(&p3_recovered);
202
203            assert_eq!((m1 + m2).remodule::<T>(), m3_recovered.remodule::<T>());
204        }
205
206        Ok(())
207    }
208
209    #[test]
210    fn test_add_plaintext() -> Result<()> {
211        const T: u64 = 128;
212        const K: usize = 16;
213        type S = TLWE<K>;
214
215        let mut rng = rand::thread_rng();
216
217        for _ in 0..200 {
218            let (sk, pk) = S::new_key(&mut rng)?;
219
220            let msg_dist = Uniform::new(0_u64, T);
221            let m1 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
222            let m2 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
223            let p1: Tn<1> = S::encode::<T>(&m1); // plaintext
224            let p2: Tn<1> = S::encode::<T>(&m2); // plaintext
225
226            let c1 = S::encrypt(&mut rng, &pk, &p1)?;
227
228            let c3 = c1 + p2;
229
230            let p3_recovered = c3.decrypt(&sk);
231            let m3_recovered = S::decode::<T>(&p3_recovered);
232
233            assert_eq!((m1 + m2).remodule::<T>(), m3_recovered);
234        }
235
236        Ok(())
237    }
238}