poulpy/core/src/gglwe/encryption.rs
core/src/gglwe/encryption.rsRaw
1use backend::{
2 FFT64, Module, ScalarZnx, ScalarZnxDftOps, ScalarZnxOps, Scratch, VecZnxAlloc, VecZnxDftAlloc, VecZnxOps, ZnxInfos, ZnxZero,
3};
4use sampling::source::Source;
5
6use crate::{
7 FourierGLWESecret, GGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, GLWETensorKey, Infos,
8 ScratchCore, SetRow, div_ceil,
9};
10
11impl GGLWECiphertext<Vec<u8>, FFT64> {
12 pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
13 let size = div_ceil(k, basek);
14 GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
15 + module.bytes_of_vec_znx(rank + 1, size)
16 + module.bytes_of_vec_znx(1, size)
17 + module.bytes_of_vec_znx_dft(rank + 1, size)
18 }
19
20 pub fn generate_from_pk_scratch_space(_module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
21 unimplemented!()
22 }
23}
24
25impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
26 pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
27 &mut self,
28 module: &Module<FFT64>,
29 pt: &ScalarZnx<DataPt>,
30 sk: &FourierGLWESecret<DataSk, FFT64>,
31 source_xa: &mut Source,
32 source_xe: &mut Source,
33 sigma: f64,
34 scratch: &mut Scratch,
35 ) {
36 #[cfg(debug_assertions)]
37 {
38 assert_eq!(self.rank_in(), pt.cols());
39 assert_eq!(self.rank_out(), sk.rank());
40 assert_eq!(self.n(), module.n());
41 assert_eq!(sk.n(), module.n());
42 assert_eq!(pt.n(), module.n());
43 assert!(
44 scratch.available()
45 >= GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
46 "scratch.available: {} < GGLWECiphertext::generate_from_sk_scratch_space(module, self.rank()={}, \
47 self.size()={}): {}",
48 scratch.available(),
49 self.rank(),
50 self.size(),
51 GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank())
52 );
53 assert!(
54 self.rows() * self.digits() * self.basek() <= self.k(),
55 "self.rows() : {} * self.digits() : {} * self.basek() : {} = {} >= self.k() = {}",
56 self.rows(),
57 self.digits(),
58 self.basek(),
59 self.rows() * self.digits() * self.basek(),
60 self.k()
61 );
62 }
63
64 let rows: usize = self.rows();
65 let digits: usize = self.digits();
66 let basek: usize = self.basek();
67 let k: usize = self.k();
68 let rank_in: usize = self.rank_in();
69 let rank_out: usize = self.rank_out();
70
71 let (mut tmp_pt, scrach_1) = scratch.tmp_glwe_pt(module, basek, k);
72 let (mut tmp_ct, scrach_2) = scrach_1.tmp_glwe_ct(module, basek, k, rank_out);
73 let (mut tmp_ct_dft, scratch_3) = scrach_2.tmp_glwe_fourier(module, basek, k, rank_out);
74
75 // For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns
76 //
77 // Example for ksk rank 2 to rank 3:
78 //
79 // (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2)
80 // (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2)
81 //
82 // Example ksk rank 2 to rank 1
83 //
84 // (-(a*s) + s0, a)
85 // (-(b*s) + s1, b)
86 (0..rank_in).for_each(|col_i| {
87 (0..rows).for_each(|row_i| {
88 // Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
89 tmp_pt.data.zero(); // zeroes for next iteration
90 module.vec_znx_add_scalar_inplace(
91 &mut tmp_pt.data,
92 0,
93 (digits - 1) + row_i * digits,
94 pt,
95 col_i,
96 );
97 module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scratch_3);
98
99 // rlwe encrypt of vec_znx_pt into vec_znx_ct
100 tmp_ct.encrypt_sk(module, &tmp_pt, sk, source_xa, source_xe, sigma, scratch_3);
101
102 // Switch vec_znx_ct into DFT domain
103 tmp_ct.dft(module, &mut tmp_ct_dft);
104
105 // Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft
106 self.set_row(module, row_i, col_i, &tmp_ct_dft);
107 });
108 });
109 }
110}
111
112impl GLWESwitchingKey<Vec<u8>, FFT64> {
113 pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
114 GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank)
115 }
116
117 pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
118 GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank)
119 }
120}
121
122impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
123 pub fn generate_from_sk<DataSkIn: AsRef<[u8]>, DataSkOut: AsRef<[u8]>>(
124 &mut self,
125 module: &Module<FFT64>,
126 sk_in: &GLWESecret<DataSkIn>,
127 sk_out: &FourierGLWESecret<DataSkOut, FFT64>,
128 source_xa: &mut Source,
129 source_xe: &mut Source,
130 sigma: f64,
131 scratch: &mut Scratch,
132 ) {
133 self.0.encrypt_sk(
134 module,
135 &sk_in.data,
136 sk_out,
137 source_xa,
138 source_xe,
139 sigma,
140 scratch,
141 );
142 }
143}
144
145impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
146 pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
147 GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank) + GLWESecret::bytes_of(module, rank)
148 }
149
150 pub fn generate_from_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
151 GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank)
152 }
153}
154
155impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
156 pub fn generate_from_sk<DataSk: AsRef<[u8]>>(
157 &mut self,
158 module: &Module<FFT64>,
159 p: i64,
160 sk: &GLWESecret<DataSk>,
161 source_xa: &mut Source,
162 source_xe: &mut Source,
163 sigma: f64,
164 scratch: &mut Scratch,
165 ) {
166 #[cfg(debug_assertions)]
167 {
168 assert_eq!(self.n(), module.n());
169 assert_eq!(sk.n(), module.n());
170 assert_eq!(self.rank_out(), self.rank_in());
171 assert_eq!(sk.rank(), self.rank());
172 assert!(
173 scratch.available()
174 >= GLWEAutomorphismKey::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
175 "scratch.available(): {} < AutomorphismKey::generate_from_sk_scratch_space(module, self.rank()={}, \
176 self.size()={}): {}",
177 scratch.available(),
178 self.rank(),
179 self.size(),
180 GLWEAutomorphismKey::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank())
181 )
182 }
183
184 let (mut sk_out_dft, scratch_1) = scratch.tmp_fourier_sk(module, sk.rank());
185
186 {
187 let (mut sk_out, _) = scratch_1.tmp_sk(module, sk.rank());
188 (0..self.rank()).for_each(|i| {
189 module.scalar_znx_automorphism(
190 module.galois_element_inv(p),
191 &mut sk_out.data,
192 i,
193 &sk.data,
194 i,
195 );
196 });
197 sk_out_dft.set(module, &sk_out);
198 }
199
200 self.key.generate_from_sk(
201 module,
202 &sk,
203 &sk_out_dft,
204 source_xa,
205 source_xe,
206 sigma,
207 scratch_1,
208 );
209
210 self.p = p;
211 }
212}
213
214impl GLWETensorKey<Vec<u8>, FFT64> {
215 pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
216 GLWESecret::bytes_of(module, 1)
217 + FourierGLWESecret::bytes_of(module, 1)
218 + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank)
219 }
220}
221
222impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
223 pub fn generate_from_sk<DataSk: AsRef<[u8]>>(
224 &mut self,
225 module: &Module<FFT64>,
226 sk: &FourierGLWESecret<DataSk, FFT64>,
227 source_xa: &mut Source,
228 source_xe: &mut Source,
229 sigma: f64,
230 scratch: &mut Scratch,
231 ) {
232 #[cfg(debug_assertions)]
233 {
234 assert_eq!(self.rank(), sk.rank());
235 assert_eq!(self.n(), module.n());
236 assert_eq!(sk.n(), module.n());
237 }
238
239 let rank: usize = self.rank();
240
241 let (mut sk_ij, scratch1) = scratch.tmp_sk(module, 1);
242 let (mut sk_ij_dft, scratch2) = scratch1.tmp_fourier_sk(module, 1);
243
244 (0..rank).for_each(|i| {
245 (i..rank).for_each(|j| {
246 module.svp_apply(&mut sk_ij_dft.data, 0, &sk.data, i, &sk.data, j);
247 module.scalar_znx_idft(&mut sk_ij.data, 0, &sk_ij_dft.data, 0, scratch2);
248 self.at_mut(i, j)
249 .generate_from_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch2);
250 });
251 })
252 }
253}