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}