From d5dc9e690201ef58e2e1bbe413d3fd69d25d59f3 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bossuat Date: Thu, 12 Jun 2025 10:54:23 +0200 Subject: [PATCH] Fixed block binary secret generation & added CGGI blind rotation key generation --- backend/src/scalar_znx.rs | 12 ++++--- core/src/blind_rotation/ccgi.rs | 0 core/src/blind_rotation/key.rs | 60 +++++++++++++++++++++++++++++++++ core/src/blind_rotation/mod.rs | 2 ++ core/src/lib.rs | 1 + 5 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 core/src/blind_rotation/ccgi.rs create mode 100644 core/src/blind_rotation/key.rs create mode 100644 core/src/blind_rotation/mod.rs diff --git a/backend/src/scalar_znx.rs b/backend/src/scalar_znx.rs index 4c45f36..e252a9b 100644 --- a/backend/src/scalar_znx.rs +++ b/backend/src/scalar_znx.rs @@ -91,10 +91,14 @@ impl + AsRef<[u8]>> ScalarZnx { } pub fn fill_binary_block(&mut self, col: usize, block_size: usize, source: &mut Source) { - assert!(self.n() % block_size == 0); - for chunk in self.at_mut(col, 0).chunks_mut(block_size) { - chunk[0] = 1; - chunk.shuffle(source); + assert!(block_size & (block_size - 1) == 0); + let max_idx: u64 = (block_size + 1) as u64; + let mask_idx: u64 = (2 * block_size - 1) as u64; + for block in self.at_mut(col, 0).chunks_mut(block_size) { + let idx: usize = source.next_u64n(max_idx, mask_idx) as usize; + if idx != block_size { + block[idx] = 1; + } } } } diff --git a/core/src/blind_rotation/ccgi.rs b/core/src/blind_rotation/ccgi.rs new file mode 100644 index 0000000..e69de29 diff --git a/core/src/blind_rotation/key.rs b/core/src/blind_rotation/key.rs new file mode 100644 index 0000000..4076e14 --- /dev/null +++ b/core/src/blind_rotation/key.rs @@ -0,0 +1,60 @@ +use backend::{Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToRef, Scratch, ZnxView, ZnxViewMut}; +use sampling::source::Source; + +use crate::{AutomorphismKey, GGSWCiphertext, GLWESecret, SecretDistribution}; + +pub struct BlindRotationKeyCGGI { + pub(crate) data: Vec, B>>, + pub(crate) dist: SecretDistribution, +} + +pub struct BlindRotationKeyFHEW { + pub(crate) data: Vec, B>>, + pub(crate) auto: Vec, B>>, +} + +impl BlindRotationKeyCGGI { + pub fn allocate(module: &Module, lwe_degree: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self { + let mut data: Vec, FFT64>> = Vec::with_capacity(lwe_degree); + (0..lwe_degree).for_each(|_| data.push(GGSWCiphertext::alloc(module, basek, k, rows, 1, rank))); + Self { + data, + dist: SecretDistribution::NONE, + } + } + + pub fn generate_from_sk( + &mut self, + module: &Module, + sk_glwe: &GLWESecret, + sk_lwe: &GLWESecret, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) where + DataSkGLWE: AsRef<[u8]>, + DataSkLWE: AsRef<[u8]>, + { + #[cfg(debug_assertions)] + { + assert_eq!(self.data.len(), sk_lwe.n()); + assert_eq!(sk_glwe.n(), module.n()); + assert_eq!(sk_glwe.rank(), self.data[0].rank()); + match sk_lwe.dist { + SecretDistribution::BinaryBlock(_) | SecretDistribution::BinaryFixed(_) | SecretDistribution::BinaryProb(_) => {} + _ => panic!("invalid GLWESecret distribution: must be BinaryBlock, BinaryFixed or BinaryProb"), + } + } + + self.dist = sk_lwe.dist; + + let mut pt: ScalarZnx> = module.new_scalar_znx(1); + let sk_ref: ScalarZnx<&[u8]> = sk_lwe.data.to_ref(); + + self.data.iter_mut().enumerate().for_each(|(i, ggsw)| { + pt.at_mut(0, 0)[0] = sk_ref.at(0, 0)[i]; + ggsw.encrypt_sk(module, &pt, sk_glwe, source_xa, source_xe, sigma, scratch); + }) + } +} diff --git a/core/src/blind_rotation/mod.rs b/core/src/blind_rotation/mod.rs new file mode 100644 index 0000000..c531781 --- /dev/null +++ b/core/src/blind_rotation/mod.rs @@ -0,0 +1,2 @@ +// pub mod cggi; +pub mod key; diff --git a/core/src/lib.rs b/core/src/lib.rs index 69eb045..3aa05ea 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,4 +1,5 @@ pub mod automorphism; +pub mod blind_rotation; pub mod elem; pub mod gglwe_ciphertext; pub mod ggsw_ciphertext;