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