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, ¶m.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(¶m.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, ¶m)?;
221
222 let m = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
223 let p: Tn = TGLWE::encode(¶m, &m);
224
225 let c = TGLWE::encrypt(&mut rng, ¶m, &pk, &p)?;
226 let p_recovered = c.decrypt(&sk);
227 let m_recovered = TGLWE::decode(¶m, &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, ¶m, &sk, &p)?;
233 let p_recovered = c.decrypt(&sk);
234 let m_recovered = TGLWE::decode(¶m, &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, ¶m)?;
255
256 let m1 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
257 let m2 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
258 let p1: Tn = TGLWE::encode(¶m, &m1); // plaintext
259 let p2: Tn = TGLWE::encode(¶m, &m2); // plaintext
260
261 let c1 = TGLWE::encrypt(&mut rng, ¶m, &pk, &p1)?;
262 let c2 = TGLWE::encrypt(&mut rng, ¶m, &pk, &p2)?;
263
264 let c3 = c1 + c2;
265
266 let p3_recovered = c3.decrypt(&sk);
267 let m3_recovered = TGLWE::decode(¶m, &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, ¶m)?;
288
289 let m1 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
290 let m2 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
291 let p1: Tn = TGLWE::encode(¶m, &m1); // plaintext
292 let p2: Tn = TGLWE::encode(¶m, &m2); // plaintext
293
294 let c1 = TGLWE::encrypt(&mut rng, ¶m, &pk, &p1)?;
295
296 let c3 = c1 + p2;
297
298 let p3_recovered = c3.decrypt(&sk);
299 let m3_recovered = TGLWE::decode(¶m, &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, ¶m)?;
320
321 let m1 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
322 let m2 = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
323 let p1: Tn = TGLWE::encode(¶m, &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, ¶m, &pk, &p1)?;
331
332 let c3 = c1 * p2;
333
334 let p3_recovered: Tn = c3.decrypt(&sk);
335 let m3_recovered = TGLWE::decode(¶m, &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, ¶m)?;
355 let sk_tlwe = sk.to_tlwe(¶m);
356
357 let m = Rq::rand_u64(&mut rng, msg_dist, ¶m.pt())?;
358 let p: Tn = TGLWE::encode(¶m, &m);
359
360 let c = TGLWE::encrypt(&mut rng, ¶m, &pk, &p)?;
361
362 for h in 0..param.ring.n {
363 let c_h: TLWE = c.sample_extraction(¶m, h);
364
365 let p_recovered = c_h.decrypt(&sk_tlwe);
366 let m_recovered = TLWE::decode(¶m, &p_recovered);
367 assert_eq!(m.coeffs()[h], m_recovered.coeffs()[0]);
368 }
369 }
370
371 Ok(())
372 }
373}