arnaucube
arnaucube/poulpyPublic Self-hosted repository
wip on RLWE
Jean-Philippe Bossuat jeanphilippe.bossuat@gmail.com
Mon, 10 Feb 2025 23:43:01 +0100
commit

ec6968d52a25ac675dea2ce72cfcf27fe9138ae0

parent

0519510667f6211b7b96dece0b68f6812d9d349f

M Cargo.lock → Cargo.lock

@@ -638,6 +638,7 @@ version = "0.1.0"

dependencies = [ "base2k", "rand_distr", + "sampling", ] [[package]]
M rlwe/Cargo.toml → rlwe/Cargo.toml

@@ -7,4 +7,5 @@ edition = "2024"

[dependencies] base2k = {path="../base2k"} +sampling = {path="../sampling"} rand_distr = {workspace = true}
M rlwe/src/ciphertext.rs → rlwe/src/ciphertext.rs

@@ -1,16 +1,50 @@

use crate::elem::Elem; -use crate::parameters::Parameters; use crate::plaintext::Plaintext; +use base2k::VecZnx; pub struct Ciphertext(pub Elem); +/* impl Parameters { - pub fn new_ciphertext(&self, degree: usize, log_q: usize) -> Ciphertext { - Ciphertext(self.new_elem(degree, log_q)) + pub fn new_ciphertext(&self, degree: usize, log_base2k: usize, log_q: usize) -> Ciphertext { + Ciphertext(self.new_elem(degree, log_base2k, log_q)) } } + */ impl Ciphertext { + pub fn new(n: usize, log_base2k: usize, log_q: usize, degree: usize) -> Self { + Self(Elem::new(n, log_base2k, log_q, degree)) + } + + pub fn n(&self) -> usize { + self.0.n() + } + + pub fn degree(&self) -> usize { + self.0.degree() + } + + pub fn log_q(&self) -> usize { + self.0.log_q() + } + + pub fn limbs(&self) -> usize { + self.0.limbs() + } + + pub fn at(&self, i: usize) -> &VecZnx { + self.0.at(i) + } + + pub fn at_mut(&mut self, i: usize) -> &mut VecZnx { + self.0.at_mut(i) + } + + pub fn log_base2k(&self) -> usize { + self.0.log_base2k() + } + pub fn as_plaintext(&self) -> Plaintext { unsafe { Plaintext(std::ptr::read(&self.0)) } }
M rlwe/src/elem.rs → rlwe/src/elem.rs

@@ -1,18 +1,51 @@

use crate::parameters::Parameters; -use base2k::VecZnx; +use base2k::{Infos, VecZnx}; pub struct Elem { pub value: Vec<VecZnx>, - pub log_scale: usize, + pub log_base2k: usize, + pub log_q: usize, } -impl Parameters { - pub fn new_elem(&self, degree: usize, log_q: usize) -> Elem { +impl Elem { + pub fn new(n: usize, log_base2k: usize, log_q: usize, degree: usize) -> Self { + let limbs: usize = (log_q + log_base2k - 1) / log_base2k; let mut value: Vec<VecZnx> = Vec::new(); - (0..degree + 1).for_each(|_| value.push(VecZnx::new(self.n(), log_q))); - Elem { - value: value, - log_scale: self.log_scale(), + (0..degree + 1).for_each(|_| value.push(VecZnx::new(n, limbs))); + Self { + value, + log_base2k, + log_q, } + } + + pub fn n(&self) -> usize { + self.value[0].n() + } + + pub fn degree(&self) -> usize { + self.value.len() + } + + pub fn limbs(&self) -> usize { + self.value[0].limbs() + } + + pub fn log_base2k(&self) -> usize { + self.log_base2k + } + + pub fn log_q(&self) -> usize { + self.log_q + } + + pub fn at(&self, i: usize) -> &VecZnx { + assert!(i <= self.degree()); + &self.value[i] + } + + pub fn at_mut(&mut self, i: usize) -> &mut VecZnx { + assert!(i <= self.degree()); + &mut self.value[i] } }
M rlwe/src/encryptor.rs → rlwe/src/encryptor.rs

@@ -1,1 +1,114 @@

+use crate::ciphertext::Ciphertext; +use crate::elem::Elem; +use crate::keys::SecretKey; +use crate::parameters::Parameters; +use crate::plaintext::Plaintext; +use base2k::sampling::Sampling; +use base2k::{Module, SvpPPol, SvpPPolOps, VecZnx, VecZnxBig, VecZnxDft}; +use sampling::source::Source; +pub struct EncryptorSk { + pub sk: SvpPPol, +} + +impl EncryptorSk { + pub fn new(params: &Parameters, sk: &SecretKey) -> Self { + let mut sk_svp_ppol: SvpPPol = params.module().svp_new_ppol(); + params.module().svp_prepare(&mut sk_svp_ppol, &sk.0); + Self { sk: sk_svp_ppol } + } + + pub fn encrypt_rlwe_sk( + &self, + params: &Parameters, + ct: &mut Ciphertext, + pt: Option<&Plaintext>, + xa_source: &mut Source, + xe_source: &mut Source, + tmp_bytes: &mut [u8], + ) { + params.encrypt_rlwe_sk(ct, pt, &self.sk, xa_source, xe_source, tmp_bytes); + } +} + +impl Parameters { + pub fn encrypt_rlwe_sk_tmp_bytes(&self, limbs: usize) -> usize { + encrypt_rlwe_sk_tmp_bytes(self.module(), limbs) + } + + pub fn encrypt_rlwe_sk( + &self, + ct: &mut Ciphertext, + pt: Option<&Plaintext>, + sk: &SvpPPol, + xa_source: &mut Source, + xe_source: &mut Source, + tmp_bytes: &mut [u8], + ) { + encrypt_rlwe_sk( + self.module(), + &mut ct.0, + pt.map(|pt| &pt.0), + sk, + xa_source, + xe_source, + self.xe(), + tmp_bytes, + ) + } +} + +pub fn encrypt_rlwe_sk_tmp_bytes(module: &Module, limbs: usize) -> usize { + module.bytes_of_vec_znx_dft(limbs) + module.vec_znx_big_normalize_tmp_bytes() +} + +pub fn encrypt_rlwe_sk( + module: &Module, + ct: &mut Elem, + pt: Option<&Elem>, + sk: &SvpPPol, + xa_source: &mut Source, + xe_source: &mut Source, + sigma: f64, + tmp_bytes: &mut [u8], +) { + let limbs: usize = ct.limbs(); + + assert!( + tmp_bytes.len() >= encrypt_rlwe_sk_tmp_bytes(module, limbs), + "invalid tmp_bytes: tmp_bytes={} < encrypt_rlwe_sk_tmp_bytes={}", + tmp_bytes.len(), + encrypt_rlwe_sk_tmp_bytes(module, limbs) + ); + + let log_q: usize = ct.log_q(); + let log_base2k: usize = ct.log_base2k(); + let c1: &mut VecZnx = ct.at_mut(1); + + // c1 <- Z_{2^prec}[X]/(X^{N}+1) + c1.fill_uniform(limbs, log_base2k, xa_source); + + let bytes_of_vec_znx_dft = module.bytes_of_vec_znx_dft(limbs); + + // Scratch space for DFT values + let mut buf_dft: VecZnxDft = + VecZnxDft::from_bytes(limbs, &mut tmp_bytes[..bytes_of_vec_znx_dft]); + + // Applies buf_dft <- s * c1 + module.svp_apply_dft(&mut buf_dft, sk, c1); + + // Alias scratch space + let mut buf_big: VecZnxBig = buf_dft.as_vec_znx_big(); + + if let Some(pt) = pt { + // buf_big <- m - buf_big + module.vec_znx_big_sub_small_a_inplace(&mut buf_big, pt.at(0)); + }; + + let carry: &mut [u8] = &mut tmp_bytes[bytes_of_vec_znx_dft..]; + + // c0 <- normalize(buf_big) + e + let c0: &mut VecZnx = ct.at_mut(0); + module.vec_znx_big_normalize(log_base2k, c0, &buf_big, carry); + c0.add_normal(log_base2k, log_q, xe_source, sigma, (sigma * 6.0).ceil()); +}
A rlwe/src/key_generator.rs

@@ -0,0 +1,1 @@

+pub struct KeyGenerator {}
A rlwe/src/keys.rs

@@ -0,0 +1,44 @@

+use crate::elem::Elem; +use crate::encryptor::{encrypt_rlwe_sk, encrypt_rlwe_sk_tmp_bytes}; +use crate::parameters::Parameters; +use base2k::{Module, Sampling, Scalar, SvpPPol, SvpPPolOps, VecZnx}; +use sampling::source::Source; + +pub struct SecretKey(pub Scalar); + +impl SecretKey { + pub fn new_ternary_prob(module: &Module, limbs: usize, prob: f64, source: &mut Source) -> Self { + let mut sk: Scalar = Scalar::new(module.n()); + sk.fill_ternary_prob(prob, source); + SecretKey(sk) + } +} + +pub struct PublicKey(pub Elem); + +impl PublicKey { + pub fn new( + params: &Parameters, + sk: &SvpPPol, + xa_source: &mut Source, + xe_source: &mut Source, + tmp_bytes: &mut [u8], + ) -> Self { + let mut pk: Elem = Elem::new(params.n(), params.log_base2k(), params.log_qp(), 1); + encrypt_rlwe_sk( + params.module(), + &mut pk, + None, + sk, + xa_source, + xe_source, + params.xe(), + tmp_bytes, + ); + PublicKey(pk) + } + + pub fn new_tmp_bytes(params: &Parameters) -> usize { + encrypt_rlwe_sk_tmp_bytes(params.module(), params.limbs_qp()) + } +}
M rlwe/src/lib.rs → rlwe/src/lib.rs

@@ -1,5 +1,7 @@

pub mod ciphertext; pub mod elem; pub mod encryptor; +pub mod key_generator; +pub mod keys; pub mod parameters; pub mod plaintext;
M rlwe/src/parameters.rs → rlwe/src/parameters.rs

@@ -55,7 +55,31 @@ pub fn log_p(&self) -> usize {

self.log_p } + pub fn log_qp(&self) -> usize { + self.log_q + self.log_p + } + + pub fn limbs_q(&self) -> usize { + (self.log_q + self.log_base2k - 1) / self.log_base2k + } + + pub fn limbs_qp(&self) -> usize { + (self.log_q + self.log_p + self.log_base2k - 1) / self.log_base2k + } + pub fn log_base2k(&self) -> usize { self.log_base2k + } + + pub fn module(&self) -> &Module { + &self.module + } + + pub fn xe(&self) -> f64 { + self.xe + } + + pub fn xs(&self) -> usize { + self.xs } }
M rlwe/src/plaintext.rs → rlwe/src/plaintext.rs

@@ -1,16 +1,50 @@

use crate::ciphertext::Ciphertext; use crate::elem::Elem; -use crate::parameters::Parameters; +use base2k::VecZnx; pub struct Plaintext(pub Elem); +/* impl Parameters { pub fn new_plaintext(&self, log_q: usize) -> Plaintext { Plaintext(self.new_elem(0, log_q)) } } +*/ impl Plaintext { + pub fn new(n: usize, log_base2k: usize, log_q: usize) -> Self { + Self(Elem::new(n, log_base2k, log_q, 0)) + } + + pub fn n(&self) -> usize { + self.0.n() + } + + pub fn degree(&self) -> usize { + self.0.degree() + } + + pub fn log_q(&self) -> usize { + self.0.log_q() + } + + pub fn limbs(&self) -> usize { + self.0.limbs() + } + + pub fn at(&self, i: usize) -> &VecZnx { + self.0.at(i) + } + + pub fn at_mut(&mut self, i: usize) -> &mut VecZnx { + self.0.at_mut(i) + } + + pub fn log_base2k(&self) -> usize { + self.0.log_base2k() + } + pub fn as_ciphertext(&self) -> Ciphertext { unsafe { Ciphertext(std::ptr::read(&self.0)) } }
M sampling/src/source.rs → sampling/src/source.rs

@@ -1,6 +1,6 @@

use rand_chacha::rand_core::SeedableRng; use rand_chacha::ChaCha8Rng; -use rand_core::RngCore; +use rand_core::{OsRng, RngCore}; const MAXF64: f64 = 9007199254740992.0;

@@ -8,6 +8,12 @@ pub struct Source {

source: ChaCha8Rng, } +pub fn new_seed() -> [u8; 32] { + let mut seed = [0u8; 32]; + OsRng.fill_bytes(&mut seed); + seed +} + impl Source { pub fn new(seed: [u8; 32]) -> Source { Source {

@@ -19,6 +25,10 @@ pub fn new_seed(&mut self) -> [u8; 32] {

let mut seed: [u8; 32] = [0u8; 32]; self.source.fill_bytes(&mut seed); seed + } + + pub fn branch(&mut self) -> Self { + Source::new(self.new_seed()) } #[inline(always)]