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