diff --git a/Cargo.lock b/Cargo.lock index c2c3c7c..7bbc290 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -638,6 +638,7 @@ version = "0.1.0" dependencies = [ "base2k", "rand_distr", + "sampling", ] [[package]] diff --git a/rlwe/Cargo.toml b/rlwe/Cargo.toml index 227ed3e..a5d94c8 100644 --- a/rlwe/Cargo.toml +++ b/rlwe/Cargo.toml @@ -7,4 +7,5 @@ edition = "2024" [dependencies] base2k = {path="../base2k"} +sampling = {path="../sampling"} rand_distr = {workspace = true} diff --git a/rlwe/src/ciphertext.rs b/rlwe/src/ciphertext.rs index 09ea19c..1870f4b 100644 --- a/rlwe/src/ciphertext.rs +++ b/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)) } } diff --git a/rlwe/src/elem.rs b/rlwe/src/elem.rs index 97e8b93..bb8eb74 100644 --- a/rlwe/src/elem.rs +++ b/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, - 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 = 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] + } } diff --git a/rlwe/src/encryptor.rs b/rlwe/src/encryptor.rs index 8b13789..efb4c59 100644 --- a/rlwe/src/encryptor.rs +++ b/rlwe/src/encryptor.rs @@ -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()); +} diff --git a/rlwe/src/key_generator.rs b/rlwe/src/key_generator.rs new file mode 100644 index 0000000..1cfaf1f --- /dev/null +++ b/rlwe/src/key_generator.rs @@ -0,0 +1 @@ +pub struct KeyGenerator {} diff --git a/rlwe/src/keys.rs b/rlwe/src/keys.rs new file mode 100644 index 0000000..a7a6bbe --- /dev/null +++ b/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()) + } +} diff --git a/rlwe/src/lib.rs b/rlwe/src/lib.rs index b2c2498..5bb7cc0 100644 --- a/rlwe/src/lib.rs +++ b/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; diff --git a/rlwe/src/parameters.rs b/rlwe/src/parameters.rs index 1d698d1..86946ec 100644 --- a/rlwe/src/parameters.rs +++ b/rlwe/src/parameters.rs @@ -55,7 +55,31 @@ impl Parameters { 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 + } } diff --git a/rlwe/src/plaintext.rs b/rlwe/src/plaintext.rs index 55800d6..aeec2bd 100644 --- a/rlwe/src/plaintext.rs +++ b/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)) } } diff --git a/sampling/src/source.rs b/sampling/src/source.rs index 95db528..eec868d 100644 --- a/sampling/src/source.rs +++ b/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 { @@ -21,6 +27,10 @@ impl Source { seed } + pub fn branch(&mut self) -> Self { + Source::new(self.new_seed()) + } + #[inline(always)] pub fn next_u64n(&mut self, max: u64, mask: u64) -> u64 { let mut x: u64 = self.next_u64() & mask;