fhe-study/tfhe/src/tlwe.rs
tfhe/src/tlwe.rsRaw
1use anyhow::Result;
2use itertools::zip_eq;
3use rand::Rng;
4use std::iter::Sum;
5use std::ops::{Add, AddAssign, Mul, Sub};
6
7use arith::{Ring, Rq, Tn, Zq, T64, TR};
8use gfhe::{glwe, GLWE};
9
10use crate::tggsw::TGGSW;
11use crate::tlev::TLev;
12use crate::{tglwe, tglwe::TGLWE};
13
14pub struct SecretKey<const K: usize>(pub glwe::SecretKey<T64, K>);
15
16impl<const KN: usize> SecretKey<KN> {
17 /// from TFHE [2018-421] paper: A TLWE key k \in B^n, can be interpreted as a
18 /// TRLWE key K \in B_N[X]^k having the same sequence of coefficients and
19 /// vice-versa.
20 pub fn to_tglwe<const N: usize, const K: usize>(self) -> crate::tglwe::SecretKey<N, K> {
21 let s: TR<T64, KN> = self.0 .0;
22 // split into K vectors, and interpret each of them as a T_N[X]/(X^N+1)
23 // polynomial
24 let r: Vec<Tn<N>> =
25 s.0.chunks(N)
26 .map(|v| Tn::<N>::from_vec(v.to_vec()))
27 .collect();
28 crate::tglwe::SecretKey(glwe::SecretKey::<Tn<N>, K>(TR(r)))
29 }
30}
31
32pub type PublicKey<const K: usize> = glwe::PublicKey<T64, K>;
33
34#[derive(Clone, Debug)]
35pub struct KSK<const K: usize>(Vec<TLev<K>>);
36
37#[derive(Clone, Debug)]
38pub struct TLWE<const K: usize>(pub GLWE<T64, K>);
39
40impl<const K: usize> TLWE<K> {
41 pub fn zero() -> Self {
42 Self(GLWE::<T64, K>::zero())
43 }
44
45 pub fn new_key(rng: impl Rng) -> Result<(SecretKey<K>, PublicKey<K>)> {
46 let (sk, pk): (glwe::SecretKey<T64, K>, glwe::PublicKey<T64, K>) = GLWE::new_key(rng)?;
47 Ok((SecretKey(sk), pk))
48 }
49
50 pub fn encode<const P: u64>(m: &Rq<P, 1>) -> T64 {
51 let delta = u64::MAX / P; // floored
52 let coeffs = m.coeffs();
53 T64(coeffs[0].0 * delta)
54 }
55 pub fn decode<const P: u64>(p: &T64) -> Rq<P, 1> {
56 let p = p.mul_div_round(P, u64::MAX);
57 Rq::<P, 1>::from_vec_u64(p.coeffs().iter().map(|c| c.0).collect())
58 }
59
60 // encrypts with the given SecretKey (instead of PublicKey)
61 pub fn encrypt_s(rng: impl Rng, sk: &SecretKey<K>, p: &T64) -> Result<Self> {
62 let glwe = GLWE::encrypt_s(rng, &sk.0, p)?;
63 Ok(Self(glwe))
64 }
65 pub fn encrypt(rng: impl Rng, pk: &PublicKey<K>, p: &T64) -> Result<Self> {
66 let glwe = GLWE::encrypt(rng, &pk, p)?;
67 Ok(Self(glwe))
68 }
69 pub fn decrypt(&self, sk: &SecretKey<K>) -> T64 {
70 self.0.decrypt(&sk.0)
71 }
72
73 pub fn new_ksk(
74 mut rng: impl Rng,
75 beta: u32,
76 l: u32,
77 sk: &SecretKey<K>,
78 new_sk: &SecretKey<K>,
79 ) -> Result<KSK<K>> {
80 let r: Vec<TLev<K>> = (0..K)
81 .into_iter()
82 .map(|i|
83 // treat sk_i as the msg being encrypted
84 TLev::<K>::encrypt_s(&mut rng, beta, l, &new_sk, &sk.0.0 .0[i]))
85 .collect::<Result<Vec<_>>>()?;
86
87 Ok(KSK(r))
88 }
89 pub fn key_switch(&self, beta: u32, l: u32, ksk: &KSK<K>) -> Self {
90 let (a, b): (TR<T64, K>, T64) = (self.0 .0.clone(), self.0 .1);
91
92 let lhs: TLWE<K> = TLWE(GLWE(TR::zero(), b));
93
94 // K iterations, ksk.0 contains K times GLev
95 let rhs: TLWE<K> = zip_eq(a.0, ksk.0.clone())
96 .map(|(a_i, ksk_i)| ksk_i * a_i.decompose(beta, l)) // dot_product
97 .sum();
98
99 lhs - rhs
100 }
101 // modulus switch from Q (2^64) to Q2 (in blind_rotation Q2=K*N)
102 pub fn mod_switch<const Q2: u64>(&self) -> Self {
103 let a: TR<T64, K> = self.0 .0.mod_switch::<Q2>();
104 let b: T64 = self.0 .1.mod_switch::<Q2>();
105 Self(GLWE(a, b))
106 }
107}
108// NOTE: the ugly const generics are temporary
109pub fn blind_rotation<const N: usize, const K: usize, const KN: usize, const KN2: u64>(
110 c: TLWE<KN>,
111 btk: BootstrappingKey<N, K, KN>,
112 table: TGLWE<N, K>,
113) -> TGLWE<N, K> {
114 let c_kn: TLWE<KN> = c.mod_switch::<KN2>();
115 let (a, b): (TR<T64, KN>, T64) = (c_kn.0 .0, c_kn.0 .1);
116 // two main parts: rotate by a known power of X, rotate by a secret
117 // power of X (using the C gate)
118
119 // table * X^-b, ie. left rotate
120 let v_xb: TGLWE<N, K> = table.left_rotate(b.0 as usize);
121
122 // rotate by a secret power of X using the cmux gate
123 let mut c_j: TGLWE<N, K> = v_xb.clone();
124 let _ = (1..K).map(|j| {
125 c_j = TGGSW::<N, K>::cmux(
126 btk.0[j].clone(),
127 c_j.clone(),
128 c_j.clone().left_rotate(a.0[j].0 as usize),
129 );
130 dbg!(&c_j);
131 });
132 c_j
133}
134
135#[derive(Clone, Debug)]
136pub struct BootstrappingKey<const N: usize, const K: usize, const KN: usize>(pub Vec<TGGSW<N, K>>);
137impl<const N: usize, const K: usize, const KN: usize> BootstrappingKey<N, K, KN> {
138 pub fn from_sk(mut rng: impl Rng, sk: &tglwe::SecretKey<N, K>) -> Result<Self> {
139 let (beta, l) = (2u32, 64u32); // TMP
140 //
141 let s: TR<Tn<N>, K> = sk.0 .0.clone();
142
143 // each btk_j = TGGSW_sk(s_i)
144 let btk: Vec<TGGSW<N, K>> = s
145 .iter()
146 .map(|s_i| TGGSW::<N, K>::encrypt_s(&mut rng, beta, l, sk, s_i))
147 .collect::<Result<Vec<_>>>()?;
148
149 Ok(Self(btk))
150 }
151}
152
153pub fn compute_lookup_table<const T: u64, const K: usize, const N: usize>() -> TGLWE<N, K> {
154 // from 2021-1402:
155 // v(x) = \sum_j^{N-1} [(p_j / 2N mod p)/p] X^j
156
157 // matrix of coefficients with size K*N = delta x T
158 let delta: usize = N / T as usize;
159 let values: Vec<Zq<T>> = (0..T).map(|v| Zq::<T>::from_u64(v)).collect();
160 let coeffs: Vec<Zq<T>> = (0..T as usize)
161 .flat_map(|i| vec![values[i]; delta])
162 .collect();
163 let table = Rq::<T, N>::from_vec(coeffs);
164
165 // encode the table as plaintext
166 let v: Tn<N> = TGLWE::<N, K>::encode::<T>(&table);
167
168 // encode the table as TGLWE ciphertext
169 let v: TGLWE<N, K> = TGLWE::<N, K>::from_plaintext(v);
170 v
171}
172
173impl<const K: usize> Add<TLWE<K>> for TLWE<K> {
174 type Output = Self;
175 fn add(self, other: Self) -> Self {
176 Self(self.0 + other.0)
177 }
178}
179impl<const K: usize> AddAssign for TLWE<K> {
180 fn add_assign(&mut self, rhs: Self) {
181 self.0 += rhs.0
182 }
183}
184impl<const K: usize> Sum<TLWE<K>> for TLWE<K> {
185 fn sum<I>(iter: I) -> Self
186 where
187 I: Iterator<Item = Self>,
188 {
189 let mut acc = TLWE::<K>::zero();
190 for e in iter {
191 acc += e;
192 }
193 acc
194 }
195}
196
197impl<const K: usize> Sub<TLWE<K>> for TLWE<K> {
198 type Output = Self;
199 fn sub(self, other: Self) -> Self {
200 Self(self.0 - other.0)
201 }
202}
203
204// plaintext addition
205impl<const K: usize> Add<T64> for TLWE<K> {
206 type Output = Self;
207 fn add(self, plaintext: T64) -> Self {
208 let a: TR<T64, K> = self.0 .0;
209 let b: T64 = self.0 .1 + plaintext;
210 Self(GLWE(a, b))
211 }
212}
213// plaintext substraction
214impl<const K: usize> Sub<T64> for TLWE<K> {
215 type Output = Self;
216 fn sub(self, plaintext: T64) -> Self {
217 let a: TR<T64, K> = self.0 .0;
218 let b: T64 = self.0 .1 - plaintext;
219 Self(GLWE(a, b))
220 }
221}
222// plaintext multiplication
223impl<const K: usize> Mul<T64> for TLWE<K> {
224 type Output = Self;
225 fn mul(self, plaintext: T64) -> Self {
226 let a: TR<T64, K> = TR(self.0 .0 .0.iter().map(|r_i| *r_i * plaintext).collect());
227 let b: T64 = self.0 .1 * plaintext;
228 Self(GLWE(a, b))
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use anyhow::Result;
235 use rand::distributions::Uniform;
236 use std::time::Instant;
237
238 use super::*;
239
240 #[test]
241 fn test_encrypt_decrypt() -> Result<()> {
242 const T: u64 = 128; // msg space (msg modulus)
243 const K: usize = 16;
244 type S = TLWE<K>;
245
246 let mut rng = rand::thread_rng();
247 let msg_dist = Uniform::new(0_u64, T);
248
249 for _ in 0..200 {
250 let (sk, pk) = S::new_key(&mut rng)?;
251
252 let m = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
253 let p: T64 = S::encode::<T>(&m);
254
255 let c = S::encrypt(&mut rng, &pk, &p)?;
256 let p_recovered = c.decrypt(&sk);
257 let m_recovered = S::decode::<T>(&p_recovered);
258
259 assert_eq!(m, m_recovered);
260
261 // same but using encrypt_s (with sk instead of pk))
262 let c = S::encrypt_s(&mut rng, &sk, &p)?;
263 let p_recovered = c.decrypt(&sk);
264 let m_recovered = S::decode::<T>(&p_recovered);
265
266 assert_eq!(m, m_recovered);
267 }
268
269 Ok(())
270 }
271
272 #[test]
273 fn test_addition() -> Result<()> {
274 const T: u64 = 128;
275 const K: usize = 16;
276 type S = TLWE<K>;
277
278 let mut rng = rand::thread_rng();
279 let msg_dist = Uniform::new(0_u64, T);
280
281 for _ in 0..200 {
282 let (sk, pk) = S::new_key(&mut rng)?;
283
284 let m1 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
285 let m2 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
286 let p1: T64 = S::encode::<T>(&m1); // plaintext
287 let p2: T64 = S::encode::<T>(&m2); // plaintext
288
289 let c1 = S::encrypt(&mut rng, &pk, &p1)?;
290 let c2 = S::encrypt(&mut rng, &pk, &p2)?;
291
292 let c3 = c1 + c2;
293
294 let p3_recovered = c3.decrypt(&sk);
295 let m3_recovered = S::decode::<T>(&p3_recovered);
296
297 assert_eq!((m1 + m2).remodule::<T>(), m3_recovered.remodule::<T>());
298 }
299
300 Ok(())
301 }
302
303 #[test]
304 fn test_add_plaintext() -> Result<()> {
305 const T: u64 = 128;
306 const K: usize = 16;
307 type S = TLWE<K>;
308
309 let mut rng = rand::thread_rng();
310 let msg_dist = Uniform::new(0_u64, T);
311
312 for _ in 0..200 {
313 let (sk, pk) = S::new_key(&mut rng)?;
314
315 let m1 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
316 let m2 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
317 let p1: T64 = S::encode::<T>(&m1); // plaintext
318 let p2: T64 = S::encode::<T>(&m2); // plaintext
319
320 let c1 = S::encrypt(&mut rng, &pk, &p1)?;
321
322 let c3 = c1 + p2;
323
324 let p3_recovered = c3.decrypt(&sk);
325 let m3_recovered = S::decode::<T>(&p3_recovered);
326
327 assert_eq!(m1 + m2, m3_recovered);
328 }
329
330 Ok(())
331 }
332
333 #[test]
334 fn test_mul_plaintext() -> Result<()> {
335 const T: u64 = 128;
336 const K: usize = 16;
337 type S = TLWE<K>;
338
339 let mut rng = rand::thread_rng();
340 let msg_dist = Uniform::new(0_u64, T);
341
342 for _ in 0..200 {
343 let (sk, pk) = S::new_key(&mut rng)?;
344
345 let m1 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
346 let m2 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
347 let p1: T64 = S::encode::<T>(&m1);
348 // don't scale up p2, set it directly from m2
349 // let p2: T64 = Tn(array::from_fn(|i| T64(m2.coeffs()[i].0)));
350 let p2: T64 = T64(m2.coeffs()[0].0);
351
352 let c1 = S::encrypt(&mut rng, &pk, &p1)?;
353
354 let c3 = c1 * p2;
355
356 let p3_recovered: T64 = c3.decrypt(&sk);
357 let m3_recovered = S::decode::<T>(&p3_recovered);
358 assert_eq!((m1.to_r() * m2.to_r()).to_rq::<T>(), m3_recovered);
359 }
360
361 Ok(())
362 }
363
364 #[test]
365 fn test_key_switch() -> Result<()> {
366 const T: u64 = 128; // plaintext modulus
367 const K: usize = 16;
368 type S = TLWE<K>;
369
370 let beta: u32 = 2;
371 let l: u32 = 64;
372
373 let mut rng = rand::thread_rng();
374
375 let (sk, pk) = S::new_key(&mut rng)?;
376 let (sk2, _) = S::new_key(&mut rng)?;
377 // ksk to switch from sk to sk2
378 let ksk = S::new_ksk(&mut rng, beta, l, &sk, &sk2)?;
379
380 let msg_dist = Uniform::new(0_u64, T);
381 let m = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
382 let p = S::encode::<T>(&m); // plaintext
383 //
384 let c = S::encrypt_s(&mut rng, &sk, &p)?;
385
386 let c2 = c.key_switch(beta, l, &ksk);
387
388 // decrypt with the 2nd secret key
389 let p_recovered = c2.decrypt(&sk2);
390 let m_recovered = S::decode::<T>(&p_recovered);
391 assert_eq!(m.remodule::<T>(), m_recovered.remodule::<T>());
392
393 // do the same but now encrypting with pk
394 let c = S::encrypt(&mut rng, &pk, &p)?;
395 let c2 = c.key_switch(beta, l, &ksk);
396 let p_recovered = c2.decrypt(&sk2);
397 let m_recovered = S::decode::<T>(&p_recovered);
398 assert_eq!(m, m_recovered);
399
400 Ok(())
401 }
402}