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