fhe-study/tfhe/src/tlwe.rs
tfhe/src/tlwe.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
13const ERR_SIGMA: f64 = 3.2;
14
15#[derive(Clone, Debug)]
16pub struct SecretKey<const K: usize>(glwe::SecretKey<Tn<1>, K>);
17#[derive(Clone, Debug)]
18pub struct PublicKey<const K: usize>(glwe::PublicKey<Tn<1>, K>);
19
20#[derive(Clone, Debug)]
21pub struct TLWE<const K: usize>(pub GLWE<Tn<1>, K>);
22
23impl<const K: usize> TLWE<K> {
24 pub fn zero() -> Self {
25 Self(GLWE::<Tn<1>, K>::zero())
26 }
27
28 pub fn new_key(rng: impl Rng) -> Result<(SecretKey<K>, PublicKey<K>)> {
29 let (sk, pk) = GLWE::new_key(rng)?;
30 Ok((SecretKey(sk), PublicKey(pk)))
31 }
32
33 pub fn encode<const P: u64>(m: &Rq<P, 1>) -> Tn<1> {
34 let delta = u64::MAX / P; // floored
35 let coeffs = m.coeffs();
36 Tn(array::from_fn(|i| T64(coeffs[i].0 * delta)))
37 }
38 pub fn decode<const P: u64>(p: &Tn<1>) -> Rq<P, 1> {
39 let p = p.mul_div_round(P, u64::MAX);
40 Rq::<P, 1>::from_vec_u64(p.coeffs().iter().map(|c| c.0).collect())
41 }
42
43 // encrypts with the given SecretKey (instead of PublicKey)
44 pub fn encrypt_s(rng: impl Rng, sk: &SecretKey<K>, p: &Tn<1>) -> Result<Self> {
45 let glwe = GLWE::encrypt_s(rng, &sk.0, p)?;
46 Ok(Self(glwe))
47 }
48 pub fn encrypt(rng: impl Rng, pk: &PublicKey<K>, p: &Tn<1>) -> Result<Self> {
49 let glwe = GLWE::encrypt(rng, &pk.0, p)?;
50 Ok(Self(glwe))
51 }
52 pub fn decrypt(&self, sk: &SecretKey<K>) -> Tn<1> {
53 self.0.decrypt(&sk.0)
54 }
55}
56
57impl<const K: usize> Add<TLWE<K>> for TLWE<K> {
58 type Output = Self;
59 fn add(self, other: Self) -> Self {
60 Self(self.0 + other.0)
61 }
62}
63impl<const K: usize> AddAssign for TLWE<K> {
64 fn add_assign(&mut self, rhs: Self) {
65 self.0 += rhs.0
66 }
67}
68impl<const K: usize> Sum<TLWE<K>> for TLWE<K> {
69 fn sum<I>(iter: I) -> Self
70 where
71 I: Iterator<Item = Self>,
72 {
73 let mut acc = TLWE::<K>::zero();
74 for e in iter {
75 acc += e;
76 }
77 acc
78 }
79}
80
81impl<const K: usize> Sub<TLWE<K>> for TLWE<K> {
82 type Output = Self;
83 fn sub(self, other: Self) -> Self {
84 Self(self.0 - other.0)
85 }
86}
87
88// plaintext addition
89impl<const K: usize> Add<Tn<1>> for TLWE<K> {
90 type Output = Self;
91 fn add(self, plaintext: Tn<1>) -> Self {
92 let a: TR<Tn<1>, K> = self.0 .0;
93 let b: Tn<1> = self.0 .1 + plaintext;
94 Self(GLWE(a, b))
95 }
96}
97// plaintext substraction
98impl<const K: usize> Sub<Tn<1>> for TLWE<K> {
99 type Output = Self;
100 fn sub(self, plaintext: Tn<1>) -> Self {
101 let a: TR<Tn<1>, K> = self.0 .0;
102 let b: Tn<1> = self.0 .1 - plaintext;
103 Self(GLWE(a, b))
104 }
105}
106// plaintext multiplication
107impl<const K: usize> Mul<Tn<1>> for TLWE<K> {
108 type Output = Self;
109 fn mul(self, plaintext: Tn<1>) -> Self {
110 let a: TR<Tn<1>, K> = TR(self.0 .0 .0.iter().map(|r_i| *r_i * plaintext).collect());
111 let b: Tn<1> = self.0 .1 * plaintext;
112 Self(GLWE(a, b))
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use anyhow::Result;
119 use rand::distributions::Uniform;
120
121 use super::*;
122
123 #[test]
124 fn test_encrypt_decrypt() -> Result<()> {
125 const T: u64 = 128; // msg space (msg modulus)
126 const K: usize = 16;
127 type S = TLWE<K>;
128
129 let mut rng = rand::thread_rng();
130 let msg_dist = Uniform::new(0_u64, T);
131
132 for _ in 0..200 {
133 let (sk, pk) = S::new_key(&mut rng)?;
134
135 let m = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
136 dbg!(&m);
137 let p: Tn<1> = S::encode::<T>(&m);
138 dbg!(&p);
139
140 let c = S::encrypt(&mut rng, &pk, &p)?;
141 let p_recovered = c.decrypt(&sk);
142 let m_recovered = S::decode::<T>(&p_recovered);
143
144 assert_eq!(m, m_recovered);
145
146 // same but using encrypt_s (with sk instead of pk))
147 let c = S::encrypt_s(&mut rng, &sk, &p)?;
148 let p_recovered = c.decrypt(&sk);
149 let m_recovered = S::decode::<T>(&p_recovered);
150
151 assert_eq!(m, m_recovered);
152 }
153
154 Ok(())
155 }
156
157 #[test]
158 fn test_addition() -> Result<()> {
159 const T: u64 = 128;
160 const K: usize = 16;
161 type S = TLWE<K>;
162
163 let mut rng = rand::thread_rng();
164 let msg_dist = Uniform::new(0_u64, T);
165
166 for _ in 0..200 {
167 let (sk, pk) = S::new_key(&mut rng)?;
168
169 let m1 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
170 let m2 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
171 let p1: Tn<1> = S::encode::<T>(&m1); // plaintext
172 let p2: Tn<1> = S::encode::<T>(&m2); // plaintext
173
174 let c1 = S::encrypt(&mut rng, &pk, &p1)?;
175 let c2 = S::encrypt(&mut rng, &pk, &p2)?;
176
177 let c3 = c1 + c2;
178
179 let p3_recovered = c3.decrypt(&sk);
180 let m3_recovered = S::decode::<T>(&p3_recovered);
181
182 assert_eq!((m1 + m2).remodule::<T>(), m3_recovered.remodule::<T>());
183 }
184
185 Ok(())
186 }
187
188 #[test]
189 fn test_add_plaintext() -> Result<()> {
190 const T: u64 = 128;
191 const K: usize = 16;
192 type S = TLWE<K>;
193
194 let mut rng = rand::thread_rng();
195 let msg_dist = Uniform::new(0_u64, T);
196
197 for _ in 0..200 {
198 let (sk, pk) = S::new_key(&mut rng)?;
199
200 let m1 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
201 let m2 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
202 let p1: Tn<1> = S::encode::<T>(&m1); // plaintext
203 let p2: Tn<1> = S::encode::<T>(&m2); // plaintext
204
205 let c1 = S::encrypt(&mut rng, &pk, &p1)?;
206
207 let c3 = c1 + p2;
208
209 let p3_recovered = c3.decrypt(&sk);
210 let m3_recovered = S::decode::<T>(&p3_recovered);
211
212 assert_eq!((m1 + m2).remodule::<T>(), m3_recovered);
213 }
214
215 Ok(())
216 }
217
218 #[test]
219 fn test_mul_plaintext() -> Result<()> {
220 const T: u64 = 128;
221 const K: usize = 16;
222 type S = TLWE<K>;
223
224 let mut rng = rand::thread_rng();
225 let msg_dist = Uniform::new(0_u64, T);
226
227 for _ in 0..200 {
228 let (sk, pk) = S::new_key(&mut rng)?;
229
230 let m1 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
231 let m2 = Rq::<T, 1>::rand_u64(&mut rng, msg_dist)?;
232 let p1: Tn<1> = S::encode::<T>(&m1);
233 // don't scale up p2, set it directly from m2
234 let p2: Tn<1> = Tn(array::from_fn(|i| T64(m2.coeffs()[i].0)));
235
236 let c1 = S::encrypt(&mut rng, &pk, &p1)?;
237
238 let c3 = c1 * p2;
239
240 let p3_recovered: Tn<1> = c3.decrypt(&sk);
241 let m3_recovered = S::decode::<T>(&p3_recovered);
242 assert_eq!((m1.to_r() * m2.to_r()).to_rq::<T>(), m3_recovered);
243 }
244
245 Ok(())
246 }
247}