fhe-study/generalized-fhe/src/glwe.rs
generalized-fhe/src/glwe.rsRaw
1use anyhow::Result;
2use rand::Rng;
3use rand_distr::{Normal, Uniform};
4use std::ops::{Add, Mul};
5
6use arith::{Ring, Rq, TR};
7
8const ERR_SIGMA: f64 = 3.2;
9
10pub struct GLWE<const Q: u64, const N: usize, const K: usize>(TR<Rq<Q, N>, K>, Rq<Q, N>);
11
12#[derive(Clone, Debug)]
13pub struct SecretKey<const Q: u64, const N: usize, const K: usize>(TR<Rq<Q, N>, K>);
14#[derive(Clone, Debug)]
15pub struct PublicKey<const Q: u64, const N: usize, const K: usize>(Rq<Q, N>, TR<Rq<Q, N>, K>);
16
17impl<const Q: u64, const N: usize, const K: usize> GLWE<Q, N, K> {
18 pub fn new_key(mut rng: impl Rng) -> Result<(SecretKey<Q, N, K>, PublicKey<Q, N, K>)> {
19 let Xi_key = Uniform::new(0_f64, 2_f64);
20 let Xi_err = Normal::new(0_f64, ERR_SIGMA)?;
21
22 let s: TR<Rq<Q, N>, K> = TR::rand(&mut rng, Xi_key);
23 let a: TR<Rq<Q, N>, K> = TR::rand(&mut rng, Uniform::new(0_f64, Q as f64));
24 let e = Rq::<Q, N>::rand(&mut rng, Xi_err);
25
26 let pk: PublicKey<Q, N, K> = PublicKey((&a * &s) + e, a);
27 Ok((SecretKey(s), pk))
28 }
29
30 // TODO delta not as input
31 pub fn encrypt_s<const T: u64>(
32 mut rng: impl Rng,
33 sk: &SecretKey<Q, N, K>,
34 m: &Rq<T, N>,
35 delta: u64,
36 ) -> Result<Self> {
37 let m: Rq<Q, N> = m.remodule::<Q>();
38
39 let Xi_key = Uniform::new(0_f64, 2_f64);
40 let Xi_err = Normal::new(0_f64, ERR_SIGMA)?;
41
42 let a: TR<Rq<Q, N>, K> = TR::rand(&mut rng, Xi_key);
43 let e = Rq::<Q, N>::rand(&mut rng, Xi_err);
44
45 let b: Rq<Q, N> = (&a * &sk.0) + m * delta + e;
46 Ok(Self(a, b))
47 }
48 pub fn encrypt<const T: u64>(
49 mut rng: impl Rng,
50 pk: &PublicKey<Q, N, K>,
51 m: &Rq<T, N>,
52 delta: u64,
53 ) -> Result<Self> {
54 let m: Rq<Q, N> = m.remodule::<Q>();
55
56 let Xi_key = Uniform::new(0_f64, 2_f64);
57 let Xi_err = Normal::new(0_f64, ERR_SIGMA)?;
58
59 let u: Rq<Q, N> = Rq::rand(&mut rng, Xi_key);
60
61 let e0 = Rq::<Q, N>::rand(&mut rng, Xi_err);
62 let e1 = TR::<Rq<Q, N>, K>::rand(&mut rng, Xi_err);
63
64 let b: Rq<Q, N> = pk.0 * u + m * delta + e0;
65 let d: TR<Rq<Q, N>, K> = &pk.1 * &u + e1;
66
67 Ok(Self(d, b))
68 }
69 pub fn decrypt<const T: u64>(&self, sk: &SecretKey<Q, N, K>, delta: u64) -> Rq<T, N> {
70 let (d, b): (TR<Rq<Q, N>, K>, Rq<Q, N>) = (self.0.clone(), self.1);
71 let r: Rq<Q, N> = b - &d * &sk.0;
72 let r_scaled: Vec<f64> = r
73 .coeffs()
74 .iter()
75 .map(|e| (e.0 as f64 / delta as f64).round())
76 .collect();
77 let r = Rq::<Q, N>::from_vec_f64(r_scaled);
78 r.remodule::<T>()
79 }
80}
81
82impl<const Q: u64, const N: usize, const K: usize> Add<GLWE<Q, N, K>> for GLWE<Q, N, K> {
83 type Output = Self;
84 fn add(self, other: Self) -> Self {
85 let a: TR<Rq<Q, N>, K> = self.0 + other.0;
86 let b: Rq<Q, N> = self.1 + other.1;
87 Self(a, b)
88 }
89}
90
91impl<const Q: u64, const N: usize, const K: usize> Add<Rq<Q, N>> for GLWE<Q, N, K> {
92 type Output = Self;
93 fn add(self, plaintext: Rq<Q, N>) -> Self {
94 let a: TR<Rq<Q, N>, K> = self.0;
95 let b: Rq<Q, N> = self.1 + plaintext;
96 Self(a, b)
97 }
98}
99impl<const Q: u64, const N: usize, const K: usize> Mul<Rq<Q, N>> for GLWE<Q, N, K> {
100 type Output = Self;
101 fn mul(self, plaintext: Rq<Q, N>) -> Self {
102 // first compute the NTT for plaintext, to avoid computing it at each
103 // iteration, speeding up the multiplications
104 let mut plaintext = plaintext.clone();
105 plaintext.compute_evals();
106
107 let a: TR<Rq<Q, N>, K> = TR(self.0 .0.iter().map(|r_i| *r_i * plaintext).collect());
108 let b: Rq<Q, N> = self.1 * plaintext;
109 Self(a, b)
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use anyhow::Result;
116 use rand::distributions::Uniform;
117
118 use super::*;
119
120 #[test]
121 fn test_encrypt_decrypt() -> Result<()> {
122 const Q: u64 = 2u64.pow(16) + 1;
123 const N: usize = 128;
124 const T: u64 = 32; // plaintext modulus
125 const K: usize = 16;
126 type S = GLWE<Q, N, K>;
127
128 let delta: u64 = Q / T; // floored
129 let mut rng = rand::thread_rng();
130
131 for _ in 0..200 {
132 let (sk, pk) = S::new_key(&mut rng)?;
133
134 let msg_dist = Uniform::new(0_u64, T);
135 let m = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
136
137 let c = S::encrypt(&mut rng, &pk, &m, delta)?;
138 let m_recovered = c.decrypt(&sk, delta);
139
140 assert_eq!(m, m_recovered);
141 }
142
143 Ok(())
144 }
145
146 #[test]
147 fn test_addition() -> Result<()> {
148 const Q: u64 = 2u64.pow(16) + 1;
149 const N: usize = 128;
150 const T: u64 = 20;
151 const K: usize = 16;
152 type S = GLWE<Q, N, K>;
153
154 let delta: u64 = Q / T; // floored
155 let mut rng = rand::thread_rng();
156
157 for _ in 0..200 {
158 let (sk, pk) = S::new_key(&mut rng)?;
159
160 let msg_dist = Uniform::new(0_u64, T);
161 let m1 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
162 let m2 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
163
164 let c1 = S::encrypt(&mut rng, &pk, &m1, delta)?;
165 let c2 = S::encrypt(&mut rng, &pk, &m2, delta)?;
166
167 let c3 = c1 + c2;
168
169 let m3_recovered = c3.decrypt(&sk, delta);
170
171 assert_eq!(m1 + m2, m3_recovered);
172 }
173
174 Ok(())
175 }
176
177 #[test]
178 fn test_add_plaintext() -> Result<()> {
179 const Q: u64 = 2u64.pow(16) + 1;
180 const N: usize = 128;
181 const T: u64 = 32;
182 const K: usize = 16;
183 type S = GLWE<Q, N, K>;
184
185 let delta: u64 = Q / T; // floored
186 let mut rng = rand::thread_rng();
187
188 for _ in 0..200 {
189 let (sk, pk) = S::new_key(&mut rng)?;
190
191 let msg_dist = Uniform::new(0_u64, T);
192 let m1 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
193 let m2 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
194 let m2_scaled: Rq<Q, N> = m2.remodule::<Q>() * delta;
195
196 let c1 = S::encrypt(&mut rng, &pk, &m1, delta)?;
197
198 let c3 = c1 + m2_scaled;
199
200 let m3_recovered = c3.decrypt(&sk, delta);
201
202 assert_eq!(m1 + m2, m3_recovered);
203 }
204
205 Ok(())
206 }
207
208 #[test]
209 fn test_mul_plaintext() -> Result<()> {
210 const Q: u64 = 2u64.pow(16) + 1;
211 const N: usize = 16;
212 const T: u64 = 4;
213 const K: usize = 16;
214 type S = GLWE<Q, N, K>;
215
216 let delta: u64 = Q / T; // floored
217 let mut rng = rand::thread_rng();
218
219 for _ in 0..200 {
220 let (sk, pk) = S::new_key(&mut rng)?;
221
222 let msg_dist = Uniform::new(0_u64, T);
223 let m1 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
224 let m2 = Rq::<T, N>::rand_u64(&mut rng, msg_dist)?;
225 let m2: Rq<Q, N> = m2.remodule::<Q>();
226 let c1 = S::encrypt(&mut rng, &pk, &m1, delta)?;
227
228 let c3 = c1 * m2;
229
230 let m3_recovered: Rq<T, N> = c3.decrypt(&sk, delta);
231 assert_eq!((m1.to_r() * m2.to_r()).to_rq::<T>(), m3_recovered);
232 }
233
234 Ok(())
235 }
236}