arnaucube
arnaucube/poulpyPublic Self-hosted repository
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}