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