arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/tfhe/src/tglwe.rs
tfhe/src/tglwe.rsRaw
  1use anyhow::Result;
  2use rand::Rng;
  3use std::iter::Sum;
  4use std::ops::{Add, AddAssign, Mul, Sub};
  5
  6use arith::{Ring, RingParam, Rq, Tn, T64, TR};
  7use gfhe::{glwe, glwe::Param, GLWE};
  8
  9use crate::{tlwe, tlwe::TLWE};
 10
 11#[derive(Clone, Debug)]
 12pub struct SecretKey(pub glwe::SecretKey<Tn>);
 13
 14impl SecretKey {
 15    pub fn to_tlwe(&self, param: &Param) -> tlwe::SecretKey {
 16        let s: TR<Tn> = self.0 .0.clone();
 17        debug_assert_eq!(s.r.len(), param.k); // sanity check
 18
 19        let kn = param.k * param.ring.n;
 20        let r: Vec<Vec<T64>> = s.r.iter().map(|s_i| s_i.coeffs()).collect();
 21        let r: Vec<T64> = r.into_iter().flatten().collect();
 22        debug_assert_eq!(r.len(), kn); // sanity check
 23        tlwe::SecretKey(glwe::SecretKey::<T64>(TR::<T64>::new(kn, r)))
 24    }
 25}
 26
 27pub type PublicKey = glwe::PublicKey<Tn>;
 28
 29#[derive(Clone, Debug)]
 30pub struct TGLWE(pub GLWE<Tn>);
 31
 32impl TGLWE {
 33    pub fn zero(k: usize, param: &RingParam) -> Self {
 34        Self(GLWE::<Tn>::zero(k, param))
 35    }
 36    pub fn from_plaintext(k: usize, param: &RingParam, p: Tn) -> Self {
 37        Self(GLWE::<Tn>::from_plaintext(k, param, p))
 38    }
 39
 40    pub fn new_key(mut rng: impl Rng, param: &Param) -> Result<(SecretKey, PublicKey)> {
 41        let (sk_tlwe, _) = TLWE::new_key(&mut rng, &param.lwe())?; //param.lwe() so that it uses K*N
 42        debug_assert_eq!(sk_tlwe.0 .0.r.len(), param.lwe().k); // =KN (sanity check)
 43
 44        let sk = sk_tlwe.to_tglwe(param);
 45        let pk: PublicKey = GLWE::pk_from_sk(rng, param, sk.0.clone())?;
 46        Ok((sk, pk))
 47    }
 48
 49    pub fn encode(param: &Param, m: &Rq) -> Tn {
 50        debug_assert_eq!(param.t, m.param.q); // plaintext modulus
 51        let p = param.t;
 52        let delta = u64::MAX / p; // floored
 53        let coeffs = m.coeffs();
 54        Tn {
 55            param: param.ring,
 56            coeffs: coeffs.iter().map(|c_i| T64(c_i.v * delta)).collect(),
 57        }
 58    }
 59    pub fn decode(param: &Param, pt: &Tn) -> Rq {
 60        let p = param.t;
 61        let pt = pt.mul_div_round(p, u64::MAX);
 62        Rq::from_vec_u64(&param.pt(), pt.coeffs().iter().map(|c| c.0).collect())
 63    }
 64
 65    /// encrypts with the given SecretKey (instead of PublicKey)
 66    pub fn encrypt_s(rng: impl Rng, param: &Param, sk: &SecretKey, p: &Tn) -> Result<Self> {
 67        let glwe = GLWE::encrypt_s(rng, param, &sk.0, p)?;
 68        Ok(Self(glwe))
 69    }
 70    pub fn encrypt(rng: impl Rng, param: &Param, pk: &PublicKey, p: &Tn) -> Result<Self> {
 71        let glwe = GLWE::encrypt(rng, param, &pk, p)?;
 72        Ok(Self(glwe))
 73    }
 74    pub fn decrypt(&self, sk: &SecretKey) -> Tn {
 75        self.0.decrypt(&sk.0)
 76    }
 77
 78    /// Sample extraction / Coefficient extraction
 79    pub fn sample_extraction(&self, param: &Param, h: usize) -> TLWE {
 80        let n = param.ring.n;
 81        assert!(h < n);
 82
 83        let a: TR<Tn> = self.0 .0.clone();
 84        // set a_{n*i+j} = a_{i, h-j}     if j \in {0, h}
 85        //                 -a_{i, n+h-j}  if j \in {h+1, n-1}
 86        let new_a: Vec<T64> = a
 87            .iter()
 88            .flat_map(|a_i| {
 89                let a_i = a_i.coeffs();
 90                (0..n)
 91                    .map(|j| if j <= h { a_i[h - j] } else { -a_i[n + h - j] })
 92                    .collect::<Vec<T64>>()
 93            })
 94            .collect::<Vec<T64>>();
 95        debug_assert_eq!(new_a.len(), param.k * param.ring.n); // sanity check
 96
 97        TLWE(GLWE(
 98            TR {
 99                // TODO use constructor `new`, which will check len with k
100                k: param.k * param.ring.n,
101                r: new_a,
102            },
103            self.0 .1.coeffs()[h],
104        ))
105    }
106    pub fn left_rotate(&self, h: usize) -> Self {
107        let (a, b): (TR<Tn>, Tn) = (self.0 .0.clone(), self.0 .1.clone());
108        Self(GLWE(a.left_rotate(h), b.left_rotate(h)))
109    }
110}
111
112impl Add<TGLWE> for TGLWE {
113    type Output = Self;
114    fn add(self, other: Self) -> Self {
115        debug_assert_eq!(self.0 .0.k, other.0 .0.k);
116        debug_assert_eq!(self.0 .1.param(), other.0 .1.param());
117
118        Self(self.0 + other.0)
119    }
120}
121impl AddAssign for TGLWE {
122    fn add_assign(&mut self, other: Self) {
123        debug_assert_eq!(self.0 .0.k, other.0 .0.k);
124        debug_assert_eq!(self.0 .1.param(), other.0 .1.param());
125
126        self.0 += other.0
127    }
128}
129impl Sum<TGLWE> for TGLWE {
130    fn sum<I>(mut iter: I) -> Self
131    where
132        I: Iterator<Item = Self>,
133    {
134        let first = iter.next().unwrap();
135        iter.fold(first, |acc, e| acc + e)
136    }
137}
138
139impl Sub<TGLWE> for TGLWE {
140    type Output = Self;
141    fn sub(self, other: Self) -> Self {
142        debug_assert_eq!(self.0 .0.k, other.0 .0.k);
143        debug_assert_eq!(self.0 .1.param(), other.0 .1.param());
144
145        Self(self.0 - other.0)
146    }
147}
148
149// plaintext addition
150impl Add<Tn> for TGLWE {
151    type Output = Self;
152    fn add(self, plaintext: Tn) -> Self {
153        debug_assert_eq!(self.0 .1.param(), plaintext.param());
154
155        let a: TR<Tn> = self.0 .0;
156        let b: Tn = self.0 .1 + plaintext;
157        Self(GLWE(a, b))
158    }
159}
160// plaintext substraction
161impl Sub<Tn> for TGLWE {
162    type Output = Self;
163    fn sub(self, plaintext: Tn) -> Self {
164        debug_assert_eq!(self.0 .1.param(), plaintext.param());
165
166        let a: TR<Tn> = self.0 .0;
167        let b: Tn = self.0 .1 - plaintext;
168        Self(GLWE(a, b))
169    }
170}
171// plaintext multiplication
172impl Mul<Tn> for TGLWE {
173    type Output = Self;
174    fn mul(self, plaintext: Tn) -> Self {
175        debug_assert_eq!(self.0 .1.param(), plaintext.param());
176
177        let a: TR<Tn> = TR {
178            k: self.0 .0.k,
179            r: self.0 .0.r.iter().map(|r_i| r_i * &plaintext).collect(),
180        };
181        let b: Tn = self.0 .1 * plaintext;
182        Self(GLWE(a, b))
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use anyhow::Result;
189    use rand::distributions::Uniform;
190
191    use super::*;
192
193    #[test]
194    fn test_encrypt_decrypt() -> Result<()> {
195        let param = Param {
196            err_sigma: crate::ERR_SIGMA,
197            ring: RingParam { q: u64::MAX, n: 64 },
198            k: 16,
199            t: 128, // plaintext modulus
200        };
201
202        let mut rng = rand::thread_rng();
203        let msg_dist = Uniform::new(0_u64, param.t);
204
205        for _ in 0..200 {
206            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
207
208            let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
209            let p: Tn = TGLWE::encode(&param, &m);
210
211            let c = TGLWE::encrypt(&mut rng, &param, &pk, &p)?;
212            let p_recovered = c.decrypt(&sk);
213            let m_recovered = TGLWE::decode(&param, &p_recovered);
214
215            assert_eq!(m, m_recovered);
216
217            // same but using encrypt_s (with sk instead of pk))
218            let c = TGLWE::encrypt_s(&mut rng, &param, &sk, &p)?;
219            let p_recovered = c.decrypt(&sk);
220            let m_recovered = TGLWE::decode(&param, &p_recovered);
221
222            assert_eq!(m, m_recovered);
223        }
224
225        Ok(())
226    }
227
228    #[test]
229    fn test_addition() -> Result<()> {
230        let param = Param {
231            err_sigma: crate::ERR_SIGMA,
232            ring: RingParam { q: u64::MAX, n: 64 },
233            k: 16,
234            t: 128, // plaintext modulus
235        };
236
237        let mut rng = rand::thread_rng();
238        let msg_dist = Uniform::new(0_u64, param.t);
239
240        for _ in 0..200 {
241            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
242
243            let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
244            let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
245            let p1: Tn = TGLWE::encode(&param, &m1); // plaintext
246            let p2: Tn = TGLWE::encode(&param, &m2); // plaintext
247
248            let c1 = TGLWE::encrypt(&mut rng, &param, &pk, &p1)?;
249            let c2 = TGLWE::encrypt(&mut rng, &param, &pk, &p2)?;
250
251            let c3 = c1 + c2;
252
253            let p3_recovered = c3.decrypt(&sk);
254            let m3_recovered = TGLWE::decode(&param, &p3_recovered);
255
256            assert_eq!((m1 + m2).remodule(param.t), m3_recovered.remodule(param.t));
257        }
258
259        Ok(())
260    }
261
262    #[test]
263    fn test_add_plaintext() -> Result<()> {
264        let param = Param {
265            err_sigma: crate::ERR_SIGMA,
266            ring: RingParam { q: u64::MAX, n: 64 },
267            k: 16,
268            t: 128, // plaintext modulus
269        };
270
271        let mut rng = rand::thread_rng();
272        let msg_dist = Uniform::new(0_u64, param.t);
273
274        for _ in 0..200 {
275            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
276
277            let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
278            let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
279            let p1: Tn = TGLWE::encode(&param, &m1); // plaintext
280            let p2: Tn = TGLWE::encode(&param, &m2); // plaintext
281
282            let c1 = TGLWE::encrypt(&mut rng, &param, &pk, &p1)?;
283
284            let c3 = c1 + p2;
285
286            let p3_recovered = c3.decrypt(&sk);
287            let m3_recovered = TGLWE::decode(&param, &p3_recovered);
288
289            assert_eq!(m1 + m2, m3_recovered);
290        }
291
292        Ok(())
293    }
294
295    #[test]
296    fn test_mul_plaintext() -> Result<()> {
297        let param = Param {
298            err_sigma: crate::ERR_SIGMA,
299            ring: RingParam { q: u64::MAX, n: 64 },
300            k: 16,
301            t: 128, // plaintext modulus
302        };
303
304        let mut rng = rand::thread_rng();
305        let msg_dist = Uniform::new(0_u64, param.t);
306
307        for _ in 0..200 {
308            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
309
310            let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
311            let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
312            let p1: Tn = TGLWE::encode(&param, &m1);
313            // don't scale up p2, set it directly from m2
314            let p2: Tn = Tn {
315                param: param.ring,
316                coeffs: m2.coeffs().iter().map(|c_i| T64(c_i.v)).collect(),
317            };
318
319            let c1 = TGLWE::encrypt(&mut rng, &param, &pk, &p1)?;
320
321            let c3 = c1 * p2;
322
323            let p3_recovered: Tn = c3.decrypt(&sk);
324            let m3_recovered = TGLWE::decode(&param, &p3_recovered);
325            assert_eq!((m1.to_r() * m2.to_r()).to_rq(param.t), m3_recovered);
326        }
327
328        Ok(())
329    }
330
331    #[test]
332    fn test_sample_extraction() -> Result<()> {
333        let param = Param {
334            err_sigma: crate::ERR_SIGMA,
335            ring: RingParam { q: u64::MAX, n: 64 },
336            k: 16,
337            t: 128, // plaintext modulus
338        };
339
340        let mut rng = rand::thread_rng();
341        let msg_dist = Uniform::new(0_u64, param.t);
342
343        for _ in 0..20 {
344            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
345            let sk_tlwe = sk.to_tlwe(&param);
346
347            let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
348            let p: Tn = TGLWE::encode(&param, &m);
349
350            let c = TGLWE::encrypt(&mut rng, &param, &pk, &p)?;
351
352            for h in 0..param.ring.n {
353                let c_h: TLWE = c.sample_extraction(&param, h);
354
355                let p_recovered = c_h.decrypt(&sk_tlwe);
356                let m_recovered = TLWE::decode(&param, &p_recovered);
357                assert_eq!(m.coeffs()[h], m_recovered.coeffs()[0]);
358            }
359        }
360
361        Ok(())
362    }
363}