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 elem::Infos,
11 gglwe_ciphertext::GGLWECiphertext,
12 ggsw_ciphertext::GGSWCiphertext,
13 glwe_ciphertext_fourier::GLWECiphertextFourier,
14 glwe_plaintext::GLWEPlaintext,
15 keys::{GLWEPublicKey, SecretDistribution, SecretKeyFourier},
16 keyswitch_key::GLWESwitchingKey,
17 utils::derive_size,
18 vec_glwe_product::{VecGLWEProduct, VecGLWEProductScratchSpace},
19};
20
21pub struct GLWECiphertext<C> {
22 pub data: VecZnx<C>,
23 pub basek: usize,
24 pub k: usize,
25}
26
27impl GLWECiphertext<Vec<u8>> {
28 pub fn new<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
29 Self {
30 data: module.new_vec_znx(rank + 1, derive_size(basek, k)),
31 basek,
32 k,
33 }
34 }
35}
36
37impl<T> Infos for GLWECiphertext<T> {
38 type Inner = VecZnx<T>;
39
40 fn inner(&self) -> &Self::Inner {
41 &self.data
42 }
43
44 fn basek(&self) -> usize {
45 self.basek
46 }
47
48 fn k(&self) -> usize {
49 self.k
50 }
51}
52
53impl<T> GLWECiphertext<T> {
54 pub fn rank(&self) -> usize {
55 self.cols() - 1
56 }
57}
58
59impl<C> VecZnxToMut for GLWECiphertext<C>
60where
61 VecZnx<C>: VecZnxToMut,
62{
63 fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
64 self.data.to_mut()
65 }
66}
67
68impl<C> VecZnxToRef for GLWECiphertext<C>
69where
70 VecZnx<C>: VecZnxToRef,
71{
72 fn to_ref(&self) -> VecZnx<&[u8]> {
73 self.data.to_ref()
74 }
75}
76
77impl<C> GLWECiphertext<C>
78where
79 VecZnx<C>: VecZnxToRef,
80{
81 #[allow(dead_code)]
82 pub(crate) fn dft<R>(&self, module: &Module<FFT64>, res: &mut GLWECiphertextFourier<R, FFT64>)
83 where
84 VecZnxDft<R, FFT64>: VecZnxDftToMut<FFT64> + ZnxInfos,
85 {
86 #[cfg(debug_assertions)]
87 {
88 assert_eq!(self.rank(), res.rank());
89 assert_eq!(self.basek(), res.basek())
90 }
91
92 (0..self.rank() + 1).for_each(|i| {
93 module.vec_znx_dft(res, i, self, i);
94 })
95 }
96}
97
98impl GLWECiphertext<Vec<u8>> {
99 pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, _rank: usize, ct_size: usize) -> usize {
100 module.vec_znx_big_normalize_tmp_bytes()
101 + module.bytes_of_vec_znx_dft(1, ct_size)
102 + module.bytes_of_vec_znx_big(1, ct_size)
103 }
104 pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, _rank: usize, pk_size: usize) -> usize {
105 ((module.bytes_of_vec_znx_dft(1, pk_size) + module.bytes_of_vec_znx_big(1, pk_size)) | module.bytes_of_scalar_znx(1))
106 + module.bytes_of_scalar_znx_dft(1)
107 + module.vec_znx_big_normalize_tmp_bytes()
108 }
109
110 pub fn decrypt_scratch_space(module: &Module<FFT64>, ct_size: usize) -> usize {
111 (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, ct_size))
112 + module.bytes_of_vec_znx_big(1, ct_size)
113 }
114
115 pub fn keyswitch_scratch_space(module: &Module<FFT64>, res_size: usize, lhs: usize, rhs: usize) -> usize {
116 <GGLWECiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_scratch_space(module, res_size, lhs, rhs)
117 }
118
119 pub fn keyswitch_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize) -> usize {
120 <GGLWECiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space(
121 module, res_size, rhs,
122 )
123 }
124
125 pub fn external_product_scratch_space(module: &Module<FFT64>, res_size: usize, lhs: usize, rhs: usize) -> usize {
126 <GGSWCiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_scratch_space(module, res_size, lhs, rhs)
127 }
128
129 pub fn external_product_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize) -> usize {
130 <GGSWCiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space(
131 module, res_size, rhs,
132 )
133 }
134}
135
136impl<DataSelf> GLWECiphertext<DataSelf>
137where
138 VecZnx<DataSelf>: VecZnxToMut + VecZnxToRef,
139{
140 pub fn encrypt_sk<DataPt, DataSk>(
141 &mut self,
142 module: &Module<FFT64>,
143 pt: &GLWEPlaintext<DataPt>,
144 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
145 source_xa: &mut Source,
146 source_xe: &mut Source,
147 sigma: f64,
148 bound: f64,
149 scratch: &mut Scratch,
150 ) where
151 VecZnx<DataPt>: VecZnxToRef,
152 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
153 {
154 self.encrypt_sk_private(
155 module,
156 Some((pt, 0)),
157 sk_dft,
158 source_xa,
159 source_xe,
160 sigma,
161 bound,
162 scratch,
163 );
164 }
165
166 pub fn encrypt_zero_sk<DataSk>(
167 &mut self,
168 module: &Module<FFT64>,
169 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
170 source_xa: &mut Source,
171 source_xe: &mut Source,
172 sigma: f64,
173 bound: f64,
174 scratch: &mut Scratch,
175 ) where
176 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
177 {
178 self.encrypt_sk_private(
179 module, None, sk_dft, source_xa, source_xe, sigma, bound, scratch,
180 );
181 }
182
183 pub fn encrypt_pk<DataPt, DataPk>(
184 &mut self,
185 module: &Module<FFT64>,
186 pt: &GLWEPlaintext<DataPt>,
187 pk: &GLWEPublicKey<DataPk, FFT64>,
188 source_xu: &mut Source,
189 source_xe: &mut Source,
190 sigma: f64,
191 bound: f64,
192 scratch: &mut Scratch,
193 ) where
194 VecZnx<DataPt>: VecZnxToRef,
195 VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
196 {
197 self.encrypt_pk_private(
198 module,
199 Some((pt, 0)),
200 pk,
201 source_xu,
202 source_xe,
203 sigma,
204 bound,
205 scratch,
206 );
207 }
208
209 pub fn encrypt_zero_pk<DataPk>(
210 &mut self,
211 module: &Module<FFT64>,
212 pk: &GLWEPublicKey<DataPk, FFT64>,
213 source_xu: &mut Source,
214 source_xe: &mut Source,
215 sigma: f64,
216 bound: f64,
217 scratch: &mut Scratch,
218 ) where
219 VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
220 {
221 self.encrypt_pk_private(
222 module, None, pk, source_xu, source_xe, sigma, bound, scratch,
223 );
224 }
225
226 pub fn keyswitch<DataLhs, DataRhs>(
227 &mut self,
228 module: &Module<FFT64>,
229 lhs: &GLWECiphertext<DataLhs>,
230 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
231 scratch: &mut Scratch,
232 ) where
233 VecZnx<DataLhs>: VecZnxToRef,
234 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
235 {
236 rhs.0.prod_with_glwe(module, self, lhs, scratch);
237 }
238
239 pub fn keyswitch_inplace<DataRhs>(
240 &mut self,
241 module: &Module<FFT64>,
242 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
243 scratch: &mut Scratch,
244 ) where
245 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
246 {
247 rhs.0.prod_with_glwe_inplace(module, self, scratch);
248 }
249
250 pub fn external_product<DataLhs, DataRhs>(
251 &mut self,
252 module: &Module<FFT64>,
253 lhs: &GLWECiphertext<DataLhs>,
254 rhs: &GGSWCiphertext<DataRhs, FFT64>,
255 scratch: &mut Scratch,
256 ) where
257 VecZnx<DataLhs>: VecZnxToRef,
258 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
259 {
260 rhs.prod_with_glwe(module, self, lhs, scratch);
261 }
262
263 pub fn external_product_inplace<DataRhs>(
264 &mut self,
265 module: &Module<FFT64>,
266 rhs: &GGSWCiphertext<DataRhs, FFT64>,
267 scratch: &mut Scratch,
268 ) where
269 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
270 {
271 rhs.prod_with_glwe_inplace(module, self, scratch);
272 }
273
274 pub(crate) fn encrypt_sk_private<DataPt, DataSk>(
275 &mut self,
276 module: &Module<FFT64>,
277 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
278 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
279 source_xa: &mut Source,
280 source_xe: &mut Source,
281 sigma: f64,
282 bound: f64,
283 scratch: &mut Scratch,
284 ) where
285 VecZnx<DataPt>: VecZnxToRef,
286 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
287 {
288 #[cfg(debug_assertions)]
289 {
290 assert_eq!(self.rank(), sk_dft.rank());
291 assert_eq!(sk_dft.n(), module.n());
292 assert_eq!(self.n(), module.n());
293 if let Some((pt, col)) = pt {
294 assert_eq!(pt.n(), module.n());
295 assert!(col < self.rank() + 1);
296 }
297 }
298
299 let log_base2k: usize = self.basek();
300 let log_k: usize = self.k();
301 let size: usize = self.size();
302 let cols: usize = self.rank() + 1;
303
304 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
305 c0_big.zero();
306
307 {
308 // c[i] = uniform
309 // c[0] -= c[i] * s[i],
310 (1..cols).for_each(|i| {
311 let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
312
313 // c[i] = uniform
314 self.data.fill_uniform(log_base2k, i, size, source_xa);
315
316 // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
317 module.vec_znx_dft(&mut ci_dft, 0, self, i);
318 module.svp_apply_inplace(&mut ci_dft, 0, sk_dft, i - 1);
319 let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
320
321 // use c[0] as buffer, which is overwritten later by the normalization step
322 module.vec_znx_big_normalize(log_base2k, self, 0, &ci_big, 0, scratch_2);
323
324 // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
325 module.vec_znx_sub_ab_inplace(&mut c0_big, 0, self, 0);
326
327 // c[i] += m if col = i
328 if let Some((pt, col)) = pt {
329 if i == col {
330 module.vec_znx_add_inplace(self, i, pt, 0);
331 module.vec_znx_normalize_inplace(log_base2k, self, i, scratch_2);
332 }
333 }
334 });
335 }
336
337 // c[0] += e
338 c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, bound);
339
340 // c[0] += m if col = 0
341 if let Some((pt, col)) = pt {
342 if col == 0 {
343 module.vec_znx_add_inplace(&mut c0_big, 0, pt, 0);
344 }
345 }
346
347 // c[0] = norm(c[0])
348 module.vec_znx_normalize(log_base2k, self, 0, &c0_big, 0, scratch_1);
349 }
350
351 pub(crate) fn encrypt_pk_private<DataPt, DataPk>(
352 &mut self,
353 module: &Module<FFT64>,
354 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
355 pk: &GLWEPublicKey<DataPk, FFT64>,
356 source_xu: &mut Source,
357 source_xe: &mut Source,
358 sigma: f64,
359 bound: 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, bound);
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 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
439
440 {
441 let (mut c0_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
442 module.vec_znx_dft(&mut c0_dft, 0, self, 1);
443
444 // c0_dft = DFT(a) * DFT(s)
445 module.svp_apply_inplace(&mut c0_dft, 0, sk_dft, 0);
446
447 // c0_big = IDFT(c0_dft)
448 module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut c0_dft, 0);
449 }
450
451 // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
452 module.vec_znx_big_add_small_inplace(&mut c0_big, 0, self, 0);
453
454 // pt = norm(BIG(m + e))
455 module.vec_znx_big_normalize(self.basek(), pt, 0, &mut c0_big, 0, scratch_1);
456
457 pt.basek = self.basek();
458 pt.k = pt.k().min(self.k());
459 }
460}