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