poulpy/core/src/blind_rotation/key.rs
core/src/blind_rotation/key.rsRaw
1use backend::hal::{
2 api::{
3 FillUniform, Reset, ScratchAvailable, SvpPPolAlloc, SvpPrepare, TakeVecZnx, TakeVecZnxDft, VecZnxAddScalarInplace,
4 ZnxInfos, ZnxView, ZnxViewMut,
5 },
6 layouts::{Backend, Data, DataMut, DataRef, Module, ReaderFrom, ScalarZnx, ScalarZnxToRef, Scratch, SvpPPol, WriterTo},
7};
8use sampling::source::Source;
9
10use crate::{
11 Distribution, GGSWCiphertext, GGSWCiphertextExec, GGSWEncryptSkFamily, GGSWLayoutFamily, GLWESecretExec, Infos, LWESecret,
12};
13use std::fmt;
14
15#[derive(Clone)]
16pub struct BlindRotationKeyCGGI<D: Data> {
17 pub(crate) keys: Vec<GGSWCiphertext<D>>,
18 pub(crate) dist: Distribution,
19}
20
21impl<D: DataRef> fmt::Debug for BlindRotationKeyCGGI<D> {
22 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23 write!(f, "{}", self)
24 }
25}
26
27impl<D: Data> PartialEq for BlindRotationKeyCGGI<D> {
28 fn eq(&self, other: &Self) -> bool {
29 if self.keys.len() != other.keys.len() {
30 return false;
31 }
32 for (a, b) in self.keys.iter().zip(other.keys.iter()) {
33 if a != b {
34 return false;
35 }
36 }
37 self.dist == other.dist
38 }
39}
40
41impl<D: Data> Eq for BlindRotationKeyCGGI<D> {}
42
43impl<D: DataRef> fmt::Display for BlindRotationKeyCGGI<D> {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 for (i, key) in self.keys.iter().enumerate() {
46 write!(f, "key[{}]: {}", i, key)?;
47 }
48 writeln!(f, "{:?}", self.dist)
49 }
50}
51
52impl<D: DataMut> Reset for BlindRotationKeyCGGI<D> {
53 fn reset(&mut self) {
54 self.keys.iter_mut().for_each(|key| key.reset());
55 self.dist = Distribution::NONE;
56 }
57}
58
59impl<D: DataMut> FillUniform for BlindRotationKeyCGGI<D> {
60 fn fill_uniform(&mut self, source: &mut sampling::source::Source) {
61 self.keys
62 .iter_mut()
63 .for_each(|key| key.fill_uniform(source));
64 }
65}
66
67use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
68
69impl<D: DataMut> ReaderFrom for BlindRotationKeyCGGI<D> {
70 fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
71 match Distribution::read_from(reader) {
72 Ok(dist) => self.dist = dist,
73 Err(e) => return Err(e),
74 }
75 let len: usize = reader.read_u64::<LittleEndian>()? as usize;
76 if self.keys.len() != len {
77 return Err(std::io::Error::new(
78 std::io::ErrorKind::InvalidData,
79 format!("self.keys.len()={} != read len={}", self.keys.len(), len),
80 ));
81 }
82 for key in &mut self.keys {
83 key.read_from(reader)?;
84 }
85 Ok(())
86 }
87}
88
89impl<D: DataRef> WriterTo for BlindRotationKeyCGGI<D> {
90 fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
91 match self.dist.write_to(writer) {
92 Ok(()) => {}
93 Err(e) => return Err(e),
94 }
95 writer.write_u64::<LittleEndian>(self.keys.len() as u64)?;
96 for key in &self.keys {
97 key.write_to(writer)?;
98 }
99 Ok(())
100 }
101}
102
103impl BlindRotationKeyCGGI<Vec<u8>> {
104 pub fn alloc(n_gglwe: usize, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self {
105 let mut data: Vec<GGSWCiphertext<Vec<u8>>> = Vec::with_capacity(n_lwe);
106 (0..n_lwe).for_each(|_| data.push(GGSWCiphertext::alloc(n_gglwe, basek, k, rows, 1, rank)));
107 Self {
108 keys: data,
109 dist: Distribution::NONE,
110 }
111 }
112
113 pub fn generate_from_sk_scratch_space<B: Backend>(module: &Module<B>, n: usize, basek: usize, k: usize, rank: usize) -> usize
114 where
115 Module<B>: GGSWEncryptSkFamily<B>,
116 {
117 GGSWCiphertext::encrypt_sk_scratch_space(module, n, basek, k, rank)
118 }
119}
120
121impl<D: DataRef> BlindRotationKeyCGGI<D> {
122 #[allow(dead_code)]
123 pub(crate) fn n(&self) -> usize {
124 self.keys[0].n()
125 }
126
127 #[allow(dead_code)]
128 pub(crate) fn rows(&self) -> usize {
129 self.keys[0].rows()
130 }
131
132 #[allow(dead_code)]
133 pub(crate) fn k(&self) -> usize {
134 self.keys[0].k()
135 }
136
137 #[allow(dead_code)]
138 pub(crate) fn size(&self) -> usize {
139 self.keys[0].size()
140 }
141
142 #[allow(dead_code)]
143 pub(crate) fn rank(&self) -> usize {
144 self.keys[0].rank()
145 }
146
147 pub(crate) fn basek(&self) -> usize {
148 self.keys[0].basek()
149 }
150
151 #[allow(dead_code)]
152 pub(crate) fn block_size(&self) -> usize {
153 match self.dist {
154 Distribution::BinaryBlock(value) => value,
155 _ => 1,
156 }
157 }
158}
159
160impl<D: DataMut> BlindRotationKeyCGGI<D> {
161 pub fn generate_from_sk<DataSkGLWE, DataSkLWE, B: Backend>(
162 &mut self,
163 module: &Module<B>,
164 sk_glwe: &GLWESecretExec<DataSkGLWE, B>,
165 sk_lwe: &LWESecret<DataSkLWE>,
166 source_xa: &mut Source,
167 source_xe: &mut Source,
168 sigma: f64,
169 scratch: &mut Scratch<B>,
170 ) where
171 DataSkGLWE: DataRef,
172 DataSkLWE: DataRef,
173 Module<B>: GGSWEncryptSkFamily<B> + VecZnxAddScalarInplace,
174 Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
175 {
176 #[cfg(debug_assertions)]
177 {
178 assert_eq!(self.keys.len(), sk_lwe.n());
179 assert!(sk_glwe.n() <= module.n());
180 assert_eq!(sk_glwe.rank(), self.keys[0].rank());
181 match sk_lwe.dist {
182 Distribution::BinaryBlock(_)
183 | Distribution::BinaryFixed(_)
184 | Distribution::BinaryProb(_)
185 | Distribution::ZERO => {}
186 _ => panic!(
187 "invalid GLWESecret distribution: must be BinaryBlock, BinaryFixed or BinaryProb (or ZERO for debugging)"
188 ),
189 }
190 }
191
192 self.dist = sk_lwe.dist;
193
194 let mut pt: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(sk_glwe.n(), 1);
195 let sk_ref: ScalarZnx<&[u8]> = sk_lwe.data.to_ref();
196
197 self.keys.iter_mut().enumerate().for_each(|(i, ggsw)| {
198 pt.at_mut(0, 0)[0] = sk_ref.at(0, 0)[i];
199 ggsw.encrypt_sk(module, &pt, sk_glwe, source_xa, source_xe, sigma, scratch);
200 });
201 }
202}
203
204#[derive(PartialEq, Eq)]
205pub struct BlindRotationKeyCGGIExec<D: Data, B: Backend> {
206 pub(crate) data: Vec<GGSWCiphertextExec<D, B>>,
207 pub(crate) dist: Distribution,
208 pub(crate) x_pow_a: Option<Vec<SvpPPol<Vec<u8>, B>>>,
209}
210
211impl<D: Data, B: Backend> BlindRotationKeyCGGIExec<D, B> {
212 #[allow(dead_code)]
213 pub(crate) fn n(&self) -> usize {
214 self.data[0].n()
215 }
216
217 #[allow(dead_code)]
218 pub(crate) fn rows(&self) -> usize {
219 self.data[0].rows()
220 }
221
222 #[allow(dead_code)]
223 pub(crate) fn k(&self) -> usize {
224 self.data[0].k()
225 }
226
227 #[allow(dead_code)]
228 pub(crate) fn size(&self) -> usize {
229 self.data[0].size()
230 }
231
232 #[allow(dead_code)]
233 pub(crate) fn rank(&self) -> usize {
234 self.data[0].rank()
235 }
236
237 pub(crate) fn basek(&self) -> usize {
238 self.data[0].basek()
239 }
240
241 pub(crate) fn block_size(&self) -> usize {
242 match self.dist {
243 Distribution::BinaryBlock(value) => value,
244 _ => 1,
245 }
246 }
247}
248
249pub trait BlindRotationKeyCGGIExecLayoutFamily<B: Backend> = GGSWLayoutFamily<B> + SvpPPolAlloc<B> + SvpPrepare<B>;
250
251impl<B: Backend> BlindRotationKeyCGGIExec<Vec<u8>, B> {
252 pub fn alloc(module: &Module<B>, n_glwe: usize, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self
253 where
254 Module<B>: BlindRotationKeyCGGIExecLayoutFamily<B>,
255 {
256 let mut data: Vec<GGSWCiphertextExec<Vec<u8>, B>> = Vec::with_capacity(n_lwe);
257 (0..n_lwe).for_each(|_| {
258 data.push(GGSWCiphertextExec::alloc(
259 module, n_glwe, basek, k, rows, 1, rank,
260 ))
261 });
262 Self {
263 data,
264 dist: Distribution::NONE,
265 x_pow_a: None,
266 }
267 }
268
269 pub fn from<DataOther>(module: &Module<B>, other: &BlindRotationKeyCGGI<DataOther>, scratch: &mut Scratch<B>) -> Self
270 where
271 DataOther: DataRef,
272 Module<B>: BlindRotationKeyCGGIExecLayoutFamily<B>,
273 {
274 let mut brk: BlindRotationKeyCGGIExec<Vec<u8>, B> = Self::alloc(
275 module,
276 other.n(),
277 other.keys.len(),
278 other.basek(),
279 other.k(),
280 other.rows(),
281 other.rank(),
282 );
283 brk.prepare(module, other, scratch);
284 brk
285 }
286}
287
288impl<D: DataMut, B: Backend> BlindRotationKeyCGGIExec<D, B> {
289 pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &BlindRotationKeyCGGI<DataOther>, scratch: &mut Scratch<B>)
290 where
291 DataOther: DataRef,
292 Module<B>: BlindRotationKeyCGGIExecLayoutFamily<B>,
293 {
294 #[cfg(debug_assertions)]
295 {
296 assert_eq!(self.data.len(), other.keys.len());
297 }
298
299 let n: usize = other.n();
300
301 self.data
302 .iter_mut()
303 .zip(other.keys.iter())
304 .for_each(|(ggsw_exec, other)| {
305 ggsw_exec.prepare(module, other, scratch);
306 });
307
308 self.dist = other.dist;
309
310 match other.dist {
311 Distribution::BinaryBlock(_) => {
312 let mut x_pow_a: Vec<SvpPPol<Vec<u8>, B>> = Vec::with_capacity(n << 1);
313 let mut buf: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n, 1);
314 (0..n << 1).for_each(|i| {
315 let mut res: SvpPPol<Vec<u8>, B> = module.svp_ppol_alloc(n, 1);
316 set_xai_plus_y(module, i, 0, &mut res, &mut buf);
317 x_pow_a.push(res);
318 });
319 self.x_pow_a = Some(x_pow_a);
320 }
321 _ => {}
322 }
323 }
324}
325
326pub fn set_xai_plus_y<A, C, B: Backend>(module: &Module<B>, ai: usize, y: i64, res: &mut SvpPPol<A, B>, buf: &mut ScalarZnx<C>)
327where
328 A: DataMut,
329 C: DataMut,
330 Module<B>: SvpPrepare<B>,
331{
332 let n: usize = res.n();
333
334 {
335 let raw: &mut [i64] = buf.at_mut(0, 0);
336 if ai < n {
337 raw[ai] = 1;
338 } else {
339 raw[(ai - n) & (n - 1)] = -1;
340 }
341 raw[0] += y;
342 }
343
344 module.svp_prepare(res, 0, buf, 0);
345
346 {
347 let raw: &mut [i64] = buf.at_mut(0, 0);
348
349 if ai < n {
350 raw[ai] = 0;
351 } else {
352 raw[(ai - n) & (n - 1)] = 0;
353 }
354 raw[0] = 0;
355 }
356}