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    /// encodes the given message as a TGLWE constant/public value, for using it
 65    /// in ct-pt-multiplication.
 66    pub fn new_const(param: &Param, m: &Rq) -> Tn {
 67        debug_assert_eq!(param.t, m.param.q);
 68        // don't scale up m, set the Tn element directly from m's coefficients
 69        Tn {
 70            param: param.ring,
 71            coeffs: m.coeffs().iter().map(|c_i| T64(c_i.v)).collect(),
 72        }
 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 first = iter.next().unwrap();
145        iter.fold(first, |acc, e| acc + e)
146    }
147}
148
149impl Sub<TGLWE> for TGLWE {
150    type Output = Self;
151    fn sub(self, other: Self) -> Self {
152        debug_assert_eq!(self.0 .0.k, other.0 .0.k);
153        debug_assert_eq!(self.0 .1.param(), other.0 .1.param());
154
155        Self(self.0 - other.0)
156    }
157}
158
159// plaintext addition
160impl Add<Tn> for TGLWE {
161    type Output = Self;
162    fn add(self, plaintext: Tn) -> Self {
163        debug_assert_eq!(self.0 .1.param(), plaintext.param());
164
165        let a: TR<Tn> = self.0 .0;
166        let b: Tn = self.0 .1 + plaintext;
167        Self(GLWE(a, b))
168    }
169}
170// plaintext substraction
171impl Sub<Tn> for TGLWE {
172    type Output = Self;
173    fn sub(self, plaintext: Tn) -> Self {
174        debug_assert_eq!(self.0 .1.param(), plaintext.param());
175
176        let a: TR<Tn> = self.0 .0;
177        let b: Tn = self.0 .1 - plaintext;
178        Self(GLWE(a, b))
179    }
180}
181// plaintext multiplication
182impl Mul<Tn> for TGLWE {
183    type Output = Self;
184    fn mul(self, plaintext: Tn) -> Self {
185        debug_assert_eq!(self.0 .1.param(), plaintext.param());
186
187        let a: TR<Tn> = TR {
188            k: self.0 .0.k,
189            r: self.0 .0.r.iter().map(|r_i| r_i * &plaintext).collect(),
190        };
191        let b: Tn = self.0 .1 * plaintext;
192        Self(GLWE(a, b))
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use anyhow::Result;
199    use rand::distr::Uniform;
200
201    use super::*;
202
203    #[test]
204    fn test_encrypt_decrypt() -> Result<()> {
205        let param = Param {
206            err_sigma: crate::ERR_SIGMA,
207            ring: RingParam { q: u64::MAX, n: 64 },
208            k: 16,
209            t: 128, // plaintext modulus
210        };
211
212        let mut rng = rand::rng();
213        let msg_dist = Uniform::new(0_u64, param.t)?;
214
215        for _ in 0..200 {
216            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
217
218            let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
219            let p: Tn = TGLWE::encode(&param, &m);
220
221            let c = TGLWE::encrypt(&mut rng, &param, &pk, &p)?;
222            let p_recovered = c.decrypt(&sk);
223            let m_recovered = TGLWE::decode(&param, &p_recovered);
224
225            assert_eq!(m, m_recovered);
226
227            // same but using encrypt_s (with sk instead of pk))
228            let c = TGLWE::encrypt_s(&mut rng, &param, &sk, &p)?;
229            let p_recovered = c.decrypt(&sk);
230            let m_recovered = TGLWE::decode(&param, &p_recovered);
231
232            assert_eq!(m, m_recovered);
233        }
234
235        Ok(())
236    }
237
238    #[test]
239    fn test_addition() -> Result<()> {
240        let param = Param {
241            err_sigma: crate::ERR_SIGMA,
242            ring: RingParam { q: u64::MAX, n: 64 },
243            k: 16,
244            t: 128, // plaintext modulus
245        };
246
247        let mut rng = rand::rng();
248        let msg_dist = Uniform::new(0_u64, param.t)?;
249
250        for _ in 0..200 {
251            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
252
253            let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
254            let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
255            let p1: Tn = TGLWE::encode(&param, &m1); // plaintext
256            let p2: Tn = TGLWE::encode(&param, &m2); // plaintext
257
258            let c1 = TGLWE::encrypt(&mut rng, &param, &pk, &p1)?;
259            let c2 = TGLWE::encrypt(&mut rng, &param, &pk, &p2)?;
260
261            let c3 = c1 + c2;
262
263            let p3_recovered = c3.decrypt(&sk);
264            let m3_recovered = TGLWE::decode(&param, &p3_recovered);
265
266            assert_eq!((m1 + m2).remodule(param.t), m3_recovered.remodule(param.t));
267        }
268
269        Ok(())
270    }
271
272    #[test]
273    fn test_add_plaintext() -> Result<()> {
274        let param = Param {
275            err_sigma: crate::ERR_SIGMA,
276            ring: RingParam { q: u64::MAX, n: 64 },
277            k: 16,
278            t: 128, // plaintext modulus
279        };
280
281        let mut rng = rand::rng();
282        let msg_dist = Uniform::new(0_u64, param.t)?;
283
284        for _ in 0..200 {
285            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
286
287            let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
288            let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
289            let p1: Tn = TGLWE::encode(&param, &m1); // plaintext
290            let p2: Tn = TGLWE::encode(&param, &m2); // plaintext
291
292            let c1 = TGLWE::encrypt(&mut rng, &param, &pk, &p1)?;
293
294            let c3 = c1 + p2;
295
296            let p3_recovered = c3.decrypt(&sk);
297            let m3_recovered = TGLWE::decode(&param, &p3_recovered);
298
299            assert_eq!(m1 + m2, m3_recovered);
300        }
301
302        Ok(())
303    }
304
305    #[test]
306    fn test_mul_plaintext() -> Result<()> {
307        let param = Param {
308            err_sigma: crate::ERR_SIGMA,
309            ring: RingParam { q: u64::MAX, n: 64 },
310            k: 16,
311            t: 128, // plaintext modulus
312        };
313
314        let mut rng = rand::rng();
315        let msg_dist = Uniform::new(0_u64, param.t)?;
316
317        for _ in 0..200 {
318            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
319
320            let m1 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
321            let m2 = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
322            let p1: Tn = TGLWE::encode(&param, &m1);
323            let p2: Tn = TGLWE::new_const(&param, &m2); // as constant/public value
324
325            let c1 = TGLWE::encrypt(&mut rng, &param, &pk, &p1)?;
326
327            let c3 = c1 * p2;
328
329            let p3_recovered: Tn = c3.decrypt(&sk);
330            let m3_recovered = TGLWE::decode(&param, &p3_recovered);
331            assert_eq!((m1.to_r() * m2.to_r()).to_rq(param.t), m3_recovered);
332        }
333
334        Ok(())
335    }
336
337    #[test]
338    fn test_sample_extraction() -> Result<()> {
339        let param = Param {
340            err_sigma: crate::ERR_SIGMA,
341            ring: RingParam { q: u64::MAX, n: 64 },
342            k: 16,
343            t: 128, // plaintext modulus
344        };
345
346        let mut rng = rand::rng();
347        let msg_dist = Uniform::new(0_u64, param.t)?;
348
349        for _ in 0..20 {
350            let (sk, pk) = TGLWE::new_key(&mut rng, &param)?;
351            let sk_tlwe = sk.to_tlwe(&param);
352
353            let m = Rq::rand_u64(&mut rng, msg_dist, &param.pt())?;
354            let p: Tn = TGLWE::encode(&param, &m);
355
356            let c = TGLWE::encrypt(&mut rng, &param, &pk, &p)?;
357
358            for h in 0..param.ring.n {
359                let c_h: TLWE = c.sample_extraction(&param, h);
360
361                let p_recovered = c_h.decrypt(&sk_tlwe);
362                let m_recovered = TLWE::decode(&param, &p_recovered);
363                assert_eq!(m.coeffs()[h], m_recovered.coeffs()[0]);
364            }
365        }
366
367        Ok(())
368    }
369}