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