poulpy/core/src/gglwe_ciphertext.rs
core/src/gglwe_ciphertext.rsRaw
1use backend::{
2 Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, Module, ScalarZnx, Scratch, VecZnxAlloc, VecZnxDftAlloc, VecZnxOps,
3 ZnxInfos, ZnxZero,
4};
5use sampling::source::Source;
6
7use crate::{GLWECiphertext, GLWECiphertextFourier, GLWESecret, GetRow, Infos, ScratchCore, SetRow, div_ceil};
8
9pub struct GGLWECiphertext<C, B: Backend> {
10 pub(crate) data: MatZnxDft<C, B>,
11 pub(crate) basek: usize,
12 pub(crate) k: usize,
13 pub(crate) digits: usize,
14}
15
16impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
17 pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self {
18 Self {
19 data: module.new_mat_znx_dft(div_ceil(rows, digits), rank_in, rank_out + 1, div_ceil(basek, k)),
20 basek: basek,
21 k,
22 digits,
23 }
24 }
25
26 pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> usize {
27 module.bytes_of_mat_znx_dft(div_ceil(rows, digits), rank_in, rank_out + 1, div_ceil(basek, k))
28 }
29}
30
31impl<T, B: Backend> Infos for GGLWECiphertext<T, B> {
32 type Inner = MatZnxDft<T, B>;
33
34 fn inner(&self) -> &Self::Inner {
35 &self.data
36 }
37
38 fn basek(&self) -> usize {
39 self.basek
40 }
41
42 fn k(&self) -> usize {
43 self.k
44 }
45}
46
47impl<T, B: Backend> GGLWECiphertext<T, B> {
48 pub fn rank(&self) -> usize {
49 self.data.cols_out() - 1
50 }
51
52 pub fn digits(&self) -> usize{
53 self.digits
54 }
55
56 pub fn rank_in(&self) -> usize {
57 self.data.cols_in()
58 }
59
60 pub fn rank_out(&self) -> usize {
61 self.data.cols_out() - 1
62 }
63}
64
65impl GGLWECiphertext<Vec<u8>, FFT64> {
66 pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
67 let size = div_ceil(basek, k);
68 GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
69 + module.bytes_of_vec_znx(rank + 1, size)
70 + module.bytes_of_vec_znx(1, size)
71 + module.bytes_of_vec_znx_dft(rank + 1, size)
72 }
73
74 pub fn generate_from_pk_scratch_space(_module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
75 unimplemented!()
76 }
77}
78
79impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
80 pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
81 &mut self,
82 module: &Module<FFT64>,
83 pt: &ScalarZnx<DataPt>,
84 sk: &GLWESecret<DataSk, FFT64>,
85 source_xa: &mut Source,
86 source_xe: &mut Source,
87 sigma: f64,
88 scratch: &mut Scratch,
89 ) {
90 #[cfg(debug_assertions)]
91 {
92 assert_eq!(self.rank_in(), pt.cols());
93 assert_eq!(self.rank_out(), sk.rank());
94 assert_eq!(self.n(), module.n());
95 assert_eq!(sk.n(), module.n());
96 assert_eq!(pt.n(), module.n());
97 assert!(
98 scratch.available()
99 >= GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
100 "scratch.available: {} < GGLWECiphertext::generate_from_sk_scratch_space(module, self.rank()={}, \
101 self.size()={}): {}",
102 scratch.available(),
103 self.rank(),
104 self.size(),
105 GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank())
106 )
107 }
108
109 let rows: usize = self.rows();
110 let digits: usize = self.digits();
111 let basek: usize = self.basek();
112 let k: usize = self.k();
113 let rank_in: usize = self.rank_in();
114 let rank_out: usize = self.rank_out();
115
116 let (mut tmp_pt, scrach_1) = scratch.tmp_glwe_pt(module, basek, k);
117 let (mut tmp_ct, scrach_2) = scrach_1.tmp_glwe_ct(module, basek, k, rank_out);
118 let (mut tmp_ct_dft, scratch_3) = scrach_2.tmp_glwe_fourier(module, basek, k, rank_out);
119
120 // For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns
121 //
122 // Example for ksk rank 2 to rank 3:
123 //
124 // (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2)
125 // (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2)
126 //
127 // Example ksk rank 2 to rank 1
128 //
129 // (-(a*s) + s0, a)
130 // (-(b*s) + s1, b)
131 (0..rank_in).for_each(|col_i| {
132 (0..rows).for_each(|row_i| {
133 // Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
134 tmp_pt.data.zero(); // zeroes for next iteration
135 module.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, row_i * digits, pt, col_i);
136 module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scratch_3);
137
138 // rlwe encrypt of vec_znx_pt into vec_znx_ct
139 tmp_ct.encrypt_sk(module, &tmp_pt, sk, source_xa, source_xe, sigma, scratch_3);
140
141 // Switch vec_znx_ct into DFT domain
142 tmp_ct.dft(module, &mut tmp_ct_dft);
143
144 // Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft
145 self.set_row(module, row_i, col_i, &tmp_ct_dft);
146 });
147 });
148 }
149}
150
151impl<C: AsRef<[u8]>> GetRow<FFT64> for GGLWECiphertext<C, FFT64> {
152 fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
153 &self,
154 module: &Module<FFT64>,
155 row_i: usize,
156 col_j: usize,
157 res: &mut GLWECiphertextFourier<R, FFT64>,
158 ) {
159 module.vmp_extract_row(&mut res.data, &self.data, row_i, col_j);
160 }
161}
162
163impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GGLWECiphertext<C, FFT64> {
164 fn set_row<R: AsRef<[u8]>>(
165 &mut self,
166 module: &Module<FFT64>,
167 row_i: usize,
168 col_j: usize,
169 a: &GLWECiphertextFourier<R, FFT64>,
170 ) {
171 module.vmp_prepare_row(&mut self.data, row_i, col_j, &a.data);
172 }
173}