poulpy/core/src/glwe_ciphertext.rs
core/src/glwe_ciphertext.rsRaw
1use base2k::{
2 AddNormal, Backend, FFT64, FillUniform, MatZnxDft, MatZnxDftToRef, Module, ScalarZnxAlloc, ScalarZnxDft, ScalarZnxDftAlloc,
3 ScalarZnxDftOps, ScalarZnxDftToRef, Scratch, VecZnx, VecZnxAlloc, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch,
4 VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxDftToMut, VecZnxDftToRef, VecZnxOps, VecZnxToMut, VecZnxToRef, ZnxInfos,
5 ZnxZero,
6};
7use sampling::source::Source;
8
9use crate::{
10 SIX_SIGMA,
11 elem::Infos,
12 gglwe_ciphertext::GGLWECiphertext,
13 ggsw_ciphertext::GGSWCiphertext,
14 glwe_ciphertext_fourier::GLWECiphertextFourier,
15 glwe_plaintext::GLWEPlaintext,
16 keys::{GLWEPublicKey, SecretDistribution, SecretKeyFourier},
17 keyswitch_key::GLWESwitchingKey,
18 utils::derive_size,
19 vec_glwe_product::{VecGLWEProduct, VecGLWEProductScratchSpace},
20};
21
22pub struct GLWECiphertext<C> {
23 pub data: VecZnx<C>,
24 pub basek: usize,
25 pub k: usize,
26}
27
28impl GLWECiphertext<Vec<u8>> {
29 pub fn new<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
30 Self {
31 data: module.new_vec_znx(rank + 1, derive_size(basek, k)),
32 basek,
33 k,
34 }
35 }
36}
37
38impl<T> Infos for GLWECiphertext<T> {
39 type Inner = VecZnx<T>;
40
41 fn inner(&self) -> &Self::Inner {
42 &self.data
43 }
44
45 fn basek(&self) -> usize {
46 self.basek
47 }
48
49 fn k(&self) -> usize {
50 self.k
51 }
52}
53
54impl<T> GLWECiphertext<T> {
55 pub fn rank(&self) -> usize {
56 self.cols() - 1
57 }
58}
59
60impl<C> VecZnxToMut for GLWECiphertext<C>
61where
62 VecZnx<C>: VecZnxToMut,
63{
64 fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
65 self.data.to_mut()
66 }
67}
68
69impl<C> VecZnxToRef for GLWECiphertext<C>
70where
71 VecZnx<C>: VecZnxToRef,
72{
73 fn to_ref(&self) -> VecZnx<&[u8]> {
74 self.data.to_ref()
75 }
76}
77
78impl<C> GLWECiphertext<C>
79where
80 VecZnx<C>: VecZnxToRef,
81{
82 #[allow(dead_code)]
83 pub(crate) fn dft<R>(&self, module: &Module<FFT64>, res: &mut GLWECiphertextFourier<R, FFT64>)
84 where
85 VecZnxDft<R, FFT64>: VecZnxDftToMut<FFT64> + ZnxInfos,
86 {
87 #[cfg(debug_assertions)]
88 {
89 assert_eq!(self.rank(), res.rank());
90 assert_eq!(self.basek(), res.basek())
91 }
92
93 (0..self.rank() + 1).for_each(|i| {
94 module.vec_znx_dft(res, i, self, i);
95 })
96 }
97}
98
99impl GLWECiphertext<Vec<u8>> {
100 pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, ct_size: usize) -> usize {
101 module.vec_znx_big_normalize_tmp_bytes()
102 + module.bytes_of_vec_znx_dft(1, ct_size)
103 + module.bytes_of_vec_znx_big(1, ct_size)
104 }
105 pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, pk_size: usize) -> usize {
106 ((module.bytes_of_vec_znx_dft(1, pk_size) + module.bytes_of_vec_znx_big(1, pk_size)) | module.bytes_of_scalar_znx(1))
107 + module.bytes_of_scalar_znx_dft(1)
108 + module.vec_znx_big_normalize_tmp_bytes()
109 }
110
111 pub fn decrypt_scratch_space(module: &Module<FFT64>, ct_size: usize) -> usize {
112 (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, ct_size))
113 + module.bytes_of_vec_znx_big(1, ct_size)
114 }
115
116 pub fn keyswitch_scratch_space(
117 module: &Module<FFT64>,
118 res_size: usize,
119 lhs: usize,
120 rhs: usize,
121 rank_in: usize,
122 rank_out: usize,
123 ) -> usize {
124 <GGLWECiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_scratch_space(
125 module, res_size, lhs, rhs, rank_in, rank_out,
126 )
127 }
128
129 pub fn keyswitch_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize, rank: usize) -> usize {
130 <GGLWECiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space(
131 module, res_size, rhs, rank,
132 )
133 }
134
135 pub fn external_product_scratch_space(module: &Module<FFT64>, res_size: usize, lhs: usize, rhs: usize, rank: usize) -> usize {
136 <GGSWCiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_scratch_space(
137 module, res_size, lhs, rhs, rank, rank,
138 )
139 }
140
141 pub fn external_product_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize, rank: usize) -> usize {
142 <GGSWCiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space(
143 module, res_size, rhs, rank,
144 )
145 }
146}
147
148impl<DataSelf> GLWECiphertext<DataSelf>
149where
150 VecZnx<DataSelf>: VecZnxToMut + VecZnxToRef,
151{
152 pub fn encrypt_sk<DataPt, DataSk>(
153 &mut self,
154 module: &Module<FFT64>,
155 pt: &GLWEPlaintext<DataPt>,
156 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
157 source_xa: &mut Source,
158 source_xe: &mut Source,
159 sigma: f64,
160 scratch: &mut Scratch,
161 ) where
162 VecZnx<DataPt>: VecZnxToRef,
163 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
164 {
165 self.encrypt_sk_private(
166 module,
167 Some((pt, 0)),
168 sk_dft,
169 source_xa,
170 source_xe,
171 sigma,
172 scratch,
173 );
174 }
175
176 pub fn encrypt_zero_sk<DataSk>(
177 &mut self,
178 module: &Module<FFT64>,
179 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
180 source_xa: &mut Source,
181 source_xe: &mut Source,
182 sigma: f64,
183 scratch: &mut Scratch,
184 ) where
185 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
186 {
187 self.encrypt_sk_private(module, None, sk_dft, source_xa, source_xe, sigma, scratch);
188 }
189
190 pub fn encrypt_pk<DataPt, DataPk>(
191 &mut self,
192 module: &Module<FFT64>,
193 pt: &GLWEPlaintext<DataPt>,
194 pk: &GLWEPublicKey<DataPk, FFT64>,
195 source_xu: &mut Source,
196 source_xe: &mut Source,
197 sigma: f64,
198 scratch: &mut Scratch,
199 ) where
200 VecZnx<DataPt>: VecZnxToRef,
201 VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
202 {
203 self.encrypt_pk_private(
204 module,
205 Some((pt, 0)),
206 pk,
207 source_xu,
208 source_xe,
209 sigma,
210 scratch,
211 );
212 }
213
214 pub fn encrypt_zero_pk<DataPk>(
215 &mut self,
216 module: &Module<FFT64>,
217 pk: &GLWEPublicKey<DataPk, FFT64>,
218 source_xu: &mut Source,
219 source_xe: &mut Source,
220 sigma: f64,
221 scratch: &mut Scratch,
222 ) where
223 VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
224 {
225 self.encrypt_pk_private(module, None, pk, source_xu, source_xe, sigma, scratch);
226 }
227
228 pub fn keyswitch<DataLhs, DataRhs>(
229 &mut self,
230 module: &Module<FFT64>,
231 lhs: &GLWECiphertext<DataLhs>,
232 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
233 scratch: &mut Scratch,
234 ) where
235 VecZnx<DataLhs>: VecZnxToRef,
236 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
237 {
238 rhs.0.prod_with_glwe(module, self, lhs, scratch);
239 }
240
241 pub fn keyswitch_inplace<DataRhs>(
242 &mut self,
243 module: &Module<FFT64>,
244 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
245 scratch: &mut Scratch,
246 ) where
247 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
248 {
249 rhs.0.prod_with_glwe_inplace(module, self, scratch);
250 }
251
252 pub fn external_product<DataLhs, DataRhs>(
253 &mut self,
254 module: &Module<FFT64>,
255 lhs: &GLWECiphertext<DataLhs>,
256 rhs: &GGSWCiphertext<DataRhs, FFT64>,
257 scratch: &mut Scratch,
258 ) where
259 VecZnx<DataLhs>: VecZnxToRef,
260 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
261 {
262 rhs.prod_with_glwe(module, self, lhs, scratch);
263 }
264
265 pub fn external_product_inplace<DataRhs>(
266 &mut self,
267 module: &Module<FFT64>,
268 rhs: &GGSWCiphertext<DataRhs, FFT64>,
269 scratch: &mut Scratch,
270 ) where
271 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
272 {
273 rhs.prod_with_glwe_inplace(module, self, scratch);
274 }
275
276 pub(crate) fn encrypt_sk_private<DataPt, DataSk>(
277 &mut self,
278 module: &Module<FFT64>,
279 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
280 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
281 source_xa: &mut Source,
282 source_xe: &mut Source,
283 sigma: f64,
284 scratch: &mut Scratch,
285 ) where
286 VecZnx<DataPt>: VecZnxToRef,
287 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
288 {
289 #[cfg(debug_assertions)]
290 {
291 assert_eq!(self.rank(), sk_dft.rank());
292 assert_eq!(sk_dft.n(), module.n());
293 assert_eq!(self.n(), module.n());
294 if let Some((pt, col)) = pt {
295 assert_eq!(pt.n(), module.n());
296 assert!(col < self.rank() + 1);
297 }
298 }
299
300 let log_base2k: usize = self.basek();
301 let log_k: usize = self.k();
302 let size: usize = self.size();
303 let cols: usize = self.rank() + 1;
304
305 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
306 c0_big.zero();
307
308 {
309 // c[i] = uniform
310 // c[0] -= c[i] * s[i],
311 (1..cols).for_each(|i| {
312 let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
313
314 // c[i] = uniform
315 self.data.fill_uniform(log_base2k, i, size, source_xa);
316
317 // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
318 module.vec_znx_dft(&mut ci_dft, 0, self, i);
319 module.svp_apply_inplace(&mut ci_dft, 0, sk_dft, i - 1);
320 let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
321
322 // use c[0] as buffer, which is overwritten later by the normalization step
323 module.vec_znx_big_normalize(log_base2k, self, 0, &ci_big, 0, scratch_2);
324
325 // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
326 module.vec_znx_sub_ab_inplace(&mut c0_big, 0, self, 0);
327
328 // c[i] += m if col = i
329 if let Some((pt, col)) = pt {
330 if i == col {
331 module.vec_znx_add_inplace(self, i, pt, 0);
332 module.vec_znx_normalize_inplace(log_base2k, self, i, scratch_2);
333 }
334 }
335 });
336 }
337
338 // c[0] += e
339 c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, sigma * SIX_SIGMA);
340
341 // c[0] += m if col = 0
342 if let Some((pt, col)) = pt {
343 if col == 0 {
344 module.vec_znx_add_inplace(&mut c0_big, 0, pt, 0);
345 }
346 }
347
348 // c[0] = norm(c[0])
349 module.vec_znx_normalize(log_base2k, self, 0, &c0_big, 0, scratch_1);
350 }
351
352 pub(crate) fn encrypt_pk_private<DataPt, DataPk>(
353 &mut self,
354 module: &Module<FFT64>,
355 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
356 pk: &GLWEPublicKey<DataPk, FFT64>,
357 source_xu: &mut Source,
358 source_xe: &mut Source,
359 sigma: f64,
360 scratch: &mut Scratch,
361 ) where
362 VecZnx<DataPt>: VecZnxToRef,
363 VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
364 {
365 #[cfg(debug_assertions)]
366 {
367 assert_eq!(self.basek(), pk.basek());
368 assert_eq!(self.n(), module.n());
369 assert_eq!(pk.n(), module.n());
370 assert_eq!(self.rank(), pk.rank());
371 if let Some((pt, _)) = pt {
372 assert_eq!(pt.basek(), pk.basek());
373 assert_eq!(pt.n(), module.n());
374 }
375 }
376
377 let log_base2k: usize = pk.basek();
378 let size_pk: usize = pk.size();
379 let cols: usize = self.rank() + 1;
380
381 // Generates u according to the underlying secret distribution.
382 let (mut u_dft, scratch_1) = scratch.tmp_scalar_znx_dft(module, 1);
383
384 {
385 let (mut u, _) = scratch_1.tmp_scalar_znx(module, 1);
386 match pk.dist {
387 SecretDistribution::NONE => panic!(
388 "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
389 Self::generate"
390 ),
391 SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
392 SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
393 SecretDistribution::ZERO => {}
394 }
395
396 module.svp_prepare(&mut u_dft, 0, &u, 0);
397 }
398
399 // ct[i] = pk[i] * u + ei (+ m if col = i)
400 (0..cols).for_each(|i| {
401 let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size_pk);
402 // ci_dft = DFT(u) * DFT(pk[i])
403 module.svp_apply(&mut ci_dft, 0, &u_dft, 0, pk, i);
404
405 // ci_big = u * p[i]
406 let mut ci_big = module.vec_znx_idft_consume(ci_dft);
407
408 // ci_big = u * pk[i] + e
409 ci_big.add_normal(log_base2k, 0, pk.k(), source_xe, sigma, sigma * SIX_SIGMA);
410
411 // ci_big = u * pk[i] + e + m (if col = i)
412 if let Some((pt, col)) = pt {
413 if col == i {
414 module.vec_znx_big_add_small_inplace(&mut ci_big, 0, pt, 0);
415 }
416 }
417
418 // ct[i] = norm(ci_big)
419 module.vec_znx_big_normalize(log_base2k, self, i, &ci_big, 0, scratch_2);
420 });
421 }
422}
423
424impl<DataSelf> GLWECiphertext<DataSelf>
425where
426 VecZnx<DataSelf>: VecZnxToRef,
427{
428 pub fn decrypt<DataPt, DataSk>(
429 &self,
430 module: &Module<FFT64>,
431 pt: &mut GLWEPlaintext<DataPt>,
432 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
433 scratch: &mut Scratch,
434 ) where
435 VecZnx<DataPt>: VecZnxToMut,
436 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
437 {
438 #[cfg(debug_assertions)]
439 {
440 assert_eq!(self.rank(), sk_dft.rank());
441 assert_eq!(self.n(), module.n());
442 assert_eq!(pt.n(), module.n());
443 assert_eq!(sk_dft.n(), module.n());
444 }
445
446 let cols: usize = self.rank() + 1;
447
448 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
449 c0_big.zero();
450
451 {
452 (1..cols).for_each(|i| {
453 // ci_dft = DFT(a[i]) * DFT(s[i])
454 let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
455 module.vec_znx_dft(&mut ci_dft, 0, self, i);
456 module.svp_apply_inplace(&mut ci_dft, 0, sk_dft, i - 1);
457 let ci_big = module.vec_znx_idft_consume(ci_dft);
458
459 // c0_big += a[i] * s[i]
460 module.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
461 });
462 }
463
464 // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
465 module.vec_znx_big_add_small_inplace(&mut c0_big, 0, self, 0);
466
467 // pt = norm(BIG(m + e))
468 module.vec_znx_big_normalize(self.basek(), pt, 0, &mut c0_big, 0, scratch_1);
469
470 pt.basek = self.basek();
471 pt.k = pt.k().min(self.k());
472 }
473}