diff --git a/bool/src/bfhe/evaluate.rs b/bool/src/bfhe/evaluate.rs index db27eaaf..79ea3f21 100644 --- a/bool/src/bfhe/evaluate.rs +++ b/bool/src/bfhe/evaluate.rs @@ -1,9 +1,8 @@ use algebra::{NTTField, Polynomial}; use fhe_core::{ lwe_modulus_switch, lwe_modulus_switch_assign_between_modulus, lwe_modulus_switch_inplace, - BlindRotationKey, BlindRotationType, KeySwitchingKeyEnum, KeySwitchingLWEKey, - KeySwitchingRLWEKey, LWECiphertext, LWEModulusType, Parameters, ProcessType, SecretKeyPack, - Steps, + BlindRotationKey, KeySwitchingKeyEnum, KeySwitchingLWEKey, KeySwitchingRLWEKey, LWECiphertext, + LWEModulusType, Parameters, SecretKeyPack, Steps, }; /// The evaluator of the homomorphic encryption scheme. @@ -24,10 +23,29 @@ impl EvaluationKey { &self.parameters } + /// Compute the lookup table step. + #[inline] + pub fn lut_step(&self) -> usize { + let q: usize = self + .parameters + .lwe_cipher_modulus_value() + .try_into() + .ok() + .unwrap(); + let twice_ring_dim = self.parameters.ring_dimension() << 1; + + if twice_ring_dim % q == 0 { + twice_ring_dim / q + } else if q % twice_ring_dim == 0 { + 1 + } else { + unimplemented!() + } + } + /// Creates a new [`EvaluationKey`] from the given [`SecretKeyPack`]. pub fn new(secret_key_pack: &SecretKeyPack) -> Self { let parameters = secret_key_pack.parameters(); - assert_eq!(parameters.blind_rotation_type(), BlindRotationType::RLWE); let blind_rotation_key = BlindRotationKey::generate(secret_key_pack); @@ -51,23 +69,14 @@ impl EvaluationKey { /// Complete the bootstrapping operation with LWE Ciphertext *`c`* and lookup table `lut`. pub fn bootstrap(&self, mut c: LWECiphertext, lut: Polynomial) -> LWECiphertext { let parameters = self.parameters(); - let pre = parameters.process_before_blind_rotation(); - - match pre.process() { - ProcessType::ModulusSwitch => { - lwe_modulus_switch_assign_between_modulus( - &mut c, - parameters.lwe_cipher_modulus_value(), - pre.twice_ring_dimension_value(), - ); - } - ProcessType::Scale { ratio } => { - let ratio = C::as_from(ratio as u64); - c.a_mut().iter_mut().for_each(|v| *v = *v * ratio); - *c.b_mut() = c.b() * ratio; - } - ProcessType::Noop => (), - } + let twice_ring_dimension_value = C::try_from((parameters.ring_dimension() << 1) as u64) + .ok() + .unwrap(); + lwe_modulus_switch_assign_between_modulus( + &mut c, + parameters.lwe_cipher_modulus_value(), + twice_ring_dimension_value, + ); let mut acc = self.blind_rotation_key @@ -170,7 +179,7 @@ impl Evaluator { let add = c0.add_reduce_component_wise_ref(c1, lwe_modulus); - let lut = init_nand_lut(parameters.ring_dimension(), parameters.lut_step()); + let lut = init_nand_lut(parameters.ring_dimension(), self.ek.lut_step()); self.bootstrap(add, lut) } @@ -189,7 +198,7 @@ impl Evaluator { let add = c0.add_reduce_component_wise_ref(c1, lwe_modulus); let lut: Polynomial = - init_and_majority_lut(parameters.ring_dimension(), parameters.lut_step()); + init_and_majority_lut(parameters.ring_dimension(), self.ek.lut_step()); self.bootstrap(add, lut) } @@ -207,7 +216,7 @@ impl Evaluator { let add = c0.add_reduce_component_wise_ref(c1, lwe_modulus); - let lut = init_or_lut(parameters.ring_dimension(), parameters.lut_step()); + let lut = init_or_lut(parameters.ring_dimension(), self.ek.lut_step()); self.bootstrap(add, lut) } @@ -225,7 +234,7 @@ impl Evaluator { let add = c0.add_reduce_component_wise_ref(c1, lwe_modulus); - let lut = init_nor_lut(parameters.ring_dimension(), parameters.lut_step()); + let lut = init_nor_lut(parameters.ring_dimension(), self.ek.lut_step()); self.bootstrap(add, lut) } @@ -244,7 +253,7 @@ impl Evaluator { let mut sub = c0.sub_reduce_component_wise_ref(c1, lwe_modulus); sub.scalar_mul_reduce_inplace(C::TWO, lwe_modulus); - let lut = init_xor_lut(parameters.ring_dimension(), parameters.lut_step()); + let lut = init_xor_lut(parameters.ring_dimension(), self.ek.lut_step()); self.bootstrap(sub, lut) } @@ -263,7 +272,7 @@ impl Evaluator { let mut sub = c0.sub_reduce_component_wise_ref(c1, lwe_modulus); sub.scalar_mul_reduce_inplace(C::TWO, lwe_modulus); - let lut = init_xnor_lut(parameters.ring_dimension(), parameters.lut_step()); + let lut = init_xnor_lut(parameters.ring_dimension(), self.ek.lut_step()); self.bootstrap(sub, lut) } @@ -289,7 +298,7 @@ impl Evaluator { let mut add = c0.add_reduce_component_wise_ref(c1, lwe_modulus); add.add_reduce_inplace_component_wise(c2, lwe_modulus); - let lut = init_and_majority_lut(parameters.ring_dimension(), parameters.lut_step()); + let lut = init_and_majority_lut(parameters.ring_dimension(), self.ek.lut_step()); self.bootstrap(add, lut) } @@ -319,7 +328,7 @@ impl Evaluator { // (a & b) | (!a & c) t0.add_reduce_inplace_component_wise(&t1, lwe_modulus); - let lut = init_or_lut(parameters.ring_dimension(), parameters.lut_step()); + let lut = init_or_lut(parameters.ring_dimension(), self.ek.lut_step()); self.bootstrap(t0, lut) } diff --git a/bool/src/bfhe/parameters.rs b/bool/src/bfhe/parameters.rs index 8db4fac4..826f3ea8 100644 --- a/bool/src/bfhe/parameters.rs +++ b/bool/src/bfhe/parameters.rs @@ -1,7 +1,6 @@ use algebra::Field; use fhe_core::{ - BlindRotationType, ConstParameters, DefaultFieldU32, LWESecretKeyType, Parameters, - RingSecretKeyType, Steps, + ConstParameters, DefaultFieldU32, LWESecretKeyType, Parameters, RingSecretKeyType, Steps, }; use once_cell::sync::Lazy; @@ -14,7 +13,6 @@ pub static DEFAULT_TERNARY_128_BITS_PARAMETERS: Lazy = lattice::RLWE; /// NTT version RLWE Ciphertext pub type NTTRLWECiphertext = lattice::NTTRLWE; - -/// NTRU Ciphertext -pub type NTRUCiphertext = lattice::NTRU; - -/// NTT version NTRU Ciphertext -pub type NTTNTRUCiphertext = lattice::NTTNTRU; diff --git a/fhe_core/src/key_switch.rs b/fhe_core/src/key_switch/mod.rs similarity index 100% rename from fhe_core/src/key_switch.rs rename to fhe_core/src/key_switch/mod.rs diff --git a/fhe_core/src/key_switch/rlwe.rs b/fhe_core/src/key_switch/rlwe.rs index 814ecaca..24ba54c3 100644 --- a/fhe_core/src/key_switch/rlwe.rs +++ b/fhe_core/src/key_switch/rlwe.rs @@ -3,13 +3,7 @@ use std::slice::ChunksExact; use algebra::{Basis, NTTField, NTTPolynomial, Polynomial}; use lattice::{DecompositionSpace, NTTGadgetRLWE, PolynomialSpace, LWE, NTTRLWE, RLWE}; -use crate::{LWEModulusType, NTRUCiphertext, SecretKeyPack}; - -#[derive(Debug, Clone, Copy)] -enum Operation { - AddAMulS, - SubAMulS, -} +use crate::{LWEModulusType, SecretKeyPack}; /// The Key Switching Key. /// @@ -145,30 +139,7 @@ impl KeySwitchingRLWEKey { let iter = ciphertext.a_slice().chunks_exact(extended_lwe_dimension); - self.key_switch_inner(extended_lwe_dimension, init, iter, Operation::SubAMulS) - } - - /// Performs key switching operation. - pub fn key_switch_for_ntru(&self, mut ciphertext: NTRUCiphertext) -> LWE { - let extended_lwe_dimension = self.lwe_dimension.next_power_of_two(); - - // Because the lwe ciphertext extracted from a ntru ciphertext always has `b = 0`. - let init = >::zero(extended_lwe_dimension); - - if ciphertext.as_slice().len() != extended_lwe_dimension { - let a = ciphertext.as_mut_slice(); - a[0] = -a[0]; - a[1..].reverse(); - a.chunks_exact_mut(extended_lwe_dimension) - .for_each(|chunk| { - chunk[0] = -chunk[0]; - chunk[1..].reverse(); - }); - } - - let iter = ciphertext.as_slice().chunks_exact(extended_lwe_dimension); - - self.key_switch_inner(extended_lwe_dimension, init, iter, Operation::AddAMulS) + self.key_switch_inner(extended_lwe_dimension, init, iter) } /// Performs key switching operation. @@ -195,7 +166,7 @@ impl KeySwitchingRLWEKey { let iter = ciphertext.a().chunks_exact(extended_lwe_dimension); - self.key_switch_inner(extended_lwe_dimension, init, iter, Operation::SubAMulS) + self.key_switch_inner(extended_lwe_dimension, init, iter) } fn key_switch_inner( @@ -203,35 +174,19 @@ impl KeySwitchingRLWEKey { extended_lwe_dimension: usize, mut init: NTTRLWE, iter: ChunksExact, - op: Operation, ) -> LWE { let mut polynomial_space = PolynomialSpace::new(extended_lwe_dimension); let mut decompose_space = DecompositionSpace::new(extended_lwe_dimension); - match op { - Operation::AddAMulS => { - self.key.iter().zip(iter).for_each(|(k_i, a_i)| { - polynomial_space.copy_from(a_i); + self.key.iter().zip(iter).for_each(|(k_i, a_i)| { + polynomial_space.copy_from(a_i); - init.add_assign_gadget_rlwe_mul_polynomial_inplace_fast( - k_i, - &mut polynomial_space, - &mut decompose_space, - ); - }); - } - Operation::SubAMulS => { - self.key.iter().zip(iter).for_each(|(k_i, a_i)| { - polynomial_space.copy_from(a_i); - - init.sub_assign_gadget_rlwe_mul_polynomial_inplace_fast( - k_i, - &mut polynomial_space, - &mut decompose_space, - ); - }); - } - } + init.sub_assign_gadget_rlwe_mul_polynomial_inplace_fast( + k_i, + &mut polynomial_space, + &mut decompose_space, + ); + }); >::from(init).extract_partial_lwe_locally(self.lwe_dimension) } diff --git a/fhe_core/src/lib.rs b/fhe_core/src/lib.rs index 27e7e600..3ff84f9a 100644 --- a/fhe_core/src/lib.rs +++ b/fhe_core/src/lib.rs @@ -20,14 +20,9 @@ pub mod utils; pub use error::FHECoreError; -pub use parameter::{ - BlindRotationType, ConstParameters, DefaultFieldU32, DefaultQks, Parameters, - ProcessBeforeBlindRotation, ProcessType, Steps, -}; +pub use parameter::{ConstParameters, DefaultFieldU32, DefaultQks, Parameters, Steps}; -pub use ciphertext::{ - LWECiphertext, NTRUCiphertext, NTTNTRUCiphertext, NTTRLWECiphertext, RLWECiphertext, -}; +pub use ciphertext::{LWECiphertext, NTTRLWECiphertext, RLWECiphertext}; pub use plaintext::{decode, encode, LWEModulusType, LWEMsgType}; pub use secret_key::{ diff --git a/fhe_core/src/parameter.rs b/fhe_core/src/parameter.rs index f4d52e24..ed59945e 100644 --- a/fhe_core/src/parameter.rs +++ b/fhe_core/src/parameter.rs @@ -29,47 +29,6 @@ pub enum Steps { BrMs, } -/// The process type before blind rotation -#[derive(Debug, Clone, Copy)] -pub enum ProcessType { - /// Modulus Switch - ModulusSwitch, - /// Scale with the ratio. - Scale { - /// `2N/q` - ratio: usize, - }, - /// Do nothing. - Noop, -} - -/// Indicate whether to perform a `modulus switch`, `scale` or `noop` before blind rotation. -#[derive(Debug, Clone, Copy)] -pub struct ProcessBeforeBlindRotation { - process: ProcessType, - lut_step: usize, - /// `2N` - twice_ring_dimension_value: C, - twice_ring_dimension_modulus: PowOf2Modulus, -} - -impl ProcessBeforeBlindRotation { - /// Returns the `process` of this [`ProcessBeforeBlindRotation`]. - pub fn process(&self) -> ProcessType { - self.process - } - - /// Returns twice ring dimension value of this [`ProcessBeforeBlindRotation`]. - pub fn twice_ring_dimension_value(&self) -> C { - self.twice_ring_dimension_value - } - - /// Returns twice ring dimension modulus of this [`ProcessBeforeBlindRotation`]. - pub fn twice_ring_dimension_modulus(&self) -> PowOf2Modulus { - self.twice_ring_dimension_modulus - } -} - /// Parameters for LWE. #[derive(Debug, Clone, Copy)] pub struct LWEParameters { @@ -87,30 +46,19 @@ pub struct LWEParameters { pub noise_standard_deviation: f64, } -/// Use `RLWE` or `NTRU` to perform blind rotation. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BlindRotationType { - /// Use `RLWE` to perform blind rotation. - RLWE, - /// Use `NTRU` to perform blind rotation. - NTRU, -} - /// Parameters for blind rotation. #[derive(Debug, Clone, Copy)] pub struct BlindRotationParameters { - /// The dimension of the ring for rlwe or ntru, refers to **N** in the paper. + /// The dimension of the ring for rlwe, refers to **N** in the paper. pub dimension: usize, - /// The modulus of the ring for rlwe or ntru, refers to **Q** in the paper. + /// The modulus of the ring for rlwe, refers to **Q** in the paper. pub modulus: Q::Value, - /// The noise error's standard deviation for rlwe or ntru. + /// The noise error's standard deviation for rlwe. pub noise_standard_deviation: f64, /// The distribution type of the Ring Secret Key. pub secret_key_type: RingSecretKeyType, /// Decompose basis for `Q` used for bootstrapping accumulator. pub basis: Basis, - /// Use `RLWE` or `NTRU` to perform blind rotation. - pub blind_rotation_type: BlindRotationType, } /// Parameters for key switching. @@ -129,7 +77,6 @@ pub struct Parameters { lwe_params: LWEParameters, blind_rotation_params: BlindRotationParameters, key_switching_params: KeySwitchingParameters, - process_before_blind_rotation: ProcessBeforeBlindRotation, steps: Steps, } @@ -149,14 +96,11 @@ pub struct ConstParameters { /// **LWE** Secret Key distribution Type. pub lwe_secret_key_type: LWESecretKeyType, - /// Use `RLWE` or `NTRU` to perform blind rotation. - pub blind_rotation_type: BlindRotationType, - /// **Ring** polynomial dimension, refers to **N** in the paper. pub ring_dimension: usize, /// **Ring** polynomial modulus, refers to **Q** in the paper. pub ring_modulus: Q, - /// The **Ring** noise error's standard deviation for **rlwe** or **ntru**. + /// The **Ring** noise error's standard deviation for **rlwe**. pub ring_noise_standard_deviation: f64, /// The distribution type of the **Ring** Secret Key. pub ring_secret_key_type: RingSecretKeyType, @@ -184,95 +128,15 @@ impl Parameters { let steps = params.steps; let secret_key_type = params.lwe_secret_key_type; let ring_secret_key_type = params.ring_secret_key_type; - let blind_rotation_type = params.blind_rotation_type; - - match steps { - Steps::BrMsKs => { - if blind_rotation_type == BlindRotationType::NTRU { - // This method is not supporting `NTRU` now. - return Err(FHECoreError::StepsParametersNotCompatible); - } - if !(ring_secret_key_type == RingSecretKeyType::Binary - || ring_secret_key_type == RingSecretKeyType::Ternary) - { - // `RingSecretKeyType::Gaussian` is unimplemented. - return Err(FHECoreError::StepsParametersNotCompatible); - } - } - Steps::BrKsMs => { - if blind_rotation_type == BlindRotationType::NTRU - && ring_secret_key_type != RingSecretKeyType::Ternary - { - return Err(FHECoreError::StepsParametersNotCompatible); - } - } - Steps::BrMs => { - // Currently, only support RLWE Blind Rotation for this mode - if !(blind_rotation_type == BlindRotationType::RLWE - && lwe_dimension == ring_dimension - && ((secret_key_type == LWESecretKeyType::Binary - && ring_secret_key_type == RingSecretKeyType::Binary) - || (secret_key_type == LWESecretKeyType::Ternary - && ring_secret_key_type == RingSecretKeyType::Ternary))) - { - return Err(FHECoreError::StepsParametersNotCompatible); - } - } - } // N = 2^i if !ring_dimension.is_power_of_two() { return Err(FHECoreError::RingDimensionUnValid(ring_dimension)); } - let q: usize = lwe_cipher_modulus.try_into().ok().unwrap(); let twice_ring_dimension = ring_dimension << 1; assert!(twice_ring_dimension != 0, "Ring dimension is too large!"); - let twice_ring_dimension_value = C::try_from(twice_ring_dimension as u64).ok().unwrap(); - let twice_ring_dimension_modulus = twice_ring_dimension_value.to_power_of_2_modulus(); - - // `q|2N` or `2N|q` - #[allow(clippy::comparison_chain)] - let process_before_blind_rotation = if q == twice_ring_dimension { - ProcessBeforeBlindRotation { - process: ProcessType::Noop, - lut_step: 1, - twice_ring_dimension_value: lwe_cipher_modulus, - twice_ring_dimension_modulus: lwe_cipher_modulus.to_power_of_2_modulus(), - } - } else if q < twice_ring_dimension { - let ratio = twice_ring_dimension / q; - if ratio * q != twice_ring_dimension { - return Err(FHECoreError::LweModulusRingDimensionNotCompatible { - lwe_modulus: q, - ring_dimension, - }); - } - - ProcessBeforeBlindRotation { - process: ProcessType::Scale { ratio }, - lut_step: ratio, - twice_ring_dimension_value, - twice_ring_dimension_modulus, - } - } else { - let ratio = q / twice_ring_dimension; - if ratio * twice_ring_dimension != q { - return Err(FHECoreError::LweModulusRingDimensionNotCompatible { - lwe_modulus: q, - ring_dimension, - }); - } - - ProcessBeforeBlindRotation { - process: ProcessType::ModulusSwitch, - lut_step: 1, - twice_ring_dimension_value, - twice_ring_dimension_modulus, - } - }; - // 2N|(Q-1) let coeff_modulus = Into::::into(ring_modulus).try_into().unwrap(); let factor = (coeff_modulus - 1) / (twice_ring_dimension); @@ -301,7 +165,6 @@ impl Parameters { modulus: ring_modulus, noise_standard_deviation: params.ring_noise_standard_deviation, basis: Basis::::new(params.blind_rotation_basis_bits), - blind_rotation_type, secret_key_type: ring_secret_key_type, }; @@ -314,7 +177,6 @@ impl Parameters { lwe_params, blind_rotation_params, key_switching_params, - process_before_blind_rotation, steps, }) } @@ -379,12 +241,6 @@ impl Parameters { self.blind_rotation_params.secret_key_type } - /// Use `RLWE` or `NTRU` to perform blind rotation. - #[inline] - pub fn blind_rotation_type(&self) -> BlindRotationType { - self.blind_rotation_params.blind_rotation_type - } - /// Returns the gadget basis of this [`Parameters`], /// which acts as the decompose basis for `Q` used for bootstrapping accumulator. #[inline] @@ -451,18 +307,6 @@ impl Parameters { pub fn lwe_params(&self) -> LWEParameters { self.lwe_params } - - /// Returns the process before blind rotation of this [`Parameters`]. - #[inline] - pub fn process_before_blind_rotation(&self) -> ProcessBeforeBlindRotation { - self.process_before_blind_rotation - } - - /// Returns the lut step of this [`Parameters`]. - #[inline] - pub fn lut_step(&self) -> usize { - self.process_before_blind_rotation.lut_step - } } /// Default Field for Default Parameters diff --git a/fhe_core/src/plaintext.rs b/fhe_core/src/plaintext.rs index 61d0fef7..3378f940 100644 --- a/fhe_core/src/plaintext.rs +++ b/fhe_core/src/plaintext.rs @@ -1,5 +1,5 @@ use std::{ - fmt::Display, + fmt::{Debug, Display}, ops::{Shl, Shr}, }; @@ -140,6 +140,7 @@ pub trait LWEModulusType: + Send + Sync + Display + + Debug + ConstOne + ConstZero + Bits diff --git a/fhe_core/src/secret_key.rs b/fhe_core/src/secret_key.rs index 1528cde7..9869491c 100644 --- a/fhe_core/src/secret_key.rs +++ b/fhe_core/src/secret_key.rs @@ -2,11 +2,10 @@ use std::cell::RefCell; use algebra::{utils::Prg, NTTField, NTTPolynomial, Polynomial}; use lattice::{sample_binary_values, sample_ternary_values}; -use num_traits::Inv; use crate::{ - ciphertext::LWECiphertext, decode, encode, parameter::LWEParameters, BlindRotationType, - LWEModulusType, LWEMsgType, Parameters, Steps, + ciphertext::LWECiphertext, decode, encode, parameter::LWEParameters, LWEModulusType, + LWEMsgType, Parameters, Steps, }; /// The distribution type of the LWE Secret Key. @@ -53,9 +52,6 @@ pub struct SecretKeyPack { ring_secret_key: RingSecretKey, /// ntt version ring secret key ntt_ring_secret_key: NTTRingSecretKey, - /// ntt version inverse ring secret key - ntt_inv_ring_secret_key: Option>, - /// boolean fhe's parameters parameters: Parameters, @@ -81,82 +77,58 @@ impl SecretKeyPack { let ring_dimension = params.ring_dimension(); - let ring_secret_key; - let ntt_ring_secret_key; - let ntt_inv_ring_secret_key; - - match params.blind_rotation_type() { - BlindRotationType::RLWE => { - ring_secret_key = match params.steps() { - Steps::BrMsKs => match params.ring_secret_key_type() { - RingSecretKeyType::Binary => { - Polynomial::random_with_binary(ring_dimension, &mut csrng) - } - RingSecretKeyType::Ternary => { - Polynomial::random_with_ternary(ring_dimension, &mut csrng) - } - RingSecretKeyType::Gaussian => unimplemented!(), - RingSecretKeyType::Uniform => panic!(), - }, - Steps::BrKsMs => match params.ring_secret_key_type() { - RingSecretKeyType::Binary => { - Polynomial::random_with_binary(ring_dimension, &mut csrng) - } - RingSecretKeyType::Ternary => { - Polynomial::random_with_ternary(ring_dimension, &mut csrng) - } - RingSecretKeyType::Gaussian => Polynomial::random_with_gaussian( - ring_dimension, - &mut csrng, - params.ring_noise_distribution(), - ), - RingSecretKeyType::Uniform => { - Polynomial::random(ring_dimension, &mut csrng) - } - }, - Steps::BrMs => { - assert!( - params.ring_secret_key_type() == RingSecretKeyType::Binary - || params.ring_secret_key_type() == RingSecretKeyType::Ternary - ); - assert_eq!(params.lwe_dimension(), params.ring_dimension()); - // conversion - let convert = |v: &C| { - if v.is_zero() { - Q::zero() - } else if v.is_one() { - Q::one() - } else { - Q::neg_one() - } - }; - - // s = [s_0, s_1,..., s_{n-1}] - >::new(lwe_secret_key.iter().map(convert).collect()) + let ring_secret_key = match params.steps() { + Steps::BrMsKs => match params.ring_secret_key_type() { + RingSecretKeyType::Binary => { + Polynomial::random_with_binary(ring_dimension, &mut csrng) + } + RingSecretKeyType::Ternary => { + Polynomial::random_with_ternary(ring_dimension, &mut csrng) + } + RingSecretKeyType::Gaussian => unimplemented!(), + RingSecretKeyType::Uniform => panic!(), + }, + Steps::BrKsMs => match params.ring_secret_key_type() { + RingSecretKeyType::Binary => { + Polynomial::random_with_binary(ring_dimension, &mut csrng) + } + RingSecretKeyType::Ternary => { + Polynomial::random_with_ternary(ring_dimension, &mut csrng) + } + RingSecretKeyType::Gaussian => Polynomial::random_with_gaussian( + ring_dimension, + &mut csrng, + params.ring_noise_distribution(), + ), + RingSecretKeyType::Uniform => Polynomial::random(ring_dimension, &mut csrng), + }, + Steps::BrMs => { + assert!( + params.ring_secret_key_type() == RingSecretKeyType::Binary + || params.ring_secret_key_type() == RingSecretKeyType::Ternary + ); + assert_eq!(params.lwe_dimension(), params.ring_dimension()); + // conversion + let convert = |v: &C| { + if v.is_zero() { + Q::zero() + } else if v.is_one() { + Q::one() + } else { + Q::neg_one() } }; - ntt_ring_secret_key = ring_secret_key.clone().into_ntt_polynomial(); - ntt_inv_ring_secret_key = None; - } - BlindRotationType::NTRU => { - let four = Q::one() + Q::one() + Q::one() + Q::one(); - let chi = params.ring_noise_distribution(); - ring_secret_key = { - let mut ring_secret_key = - Polynomial::random_with_gaussian(ring_dimension, &mut csrng, chi); - ring_secret_key.mul_scalar_assign(four); - ring_secret_key[0] += Q::one(); - ring_secret_key - }; - ntt_ring_secret_key = ring_secret_key.clone().into_ntt_polynomial(); - ntt_inv_ring_secret_key = Some((&ntt_ring_secret_key).inv()); + + // s = [s_0, s_1,..., s_{n-1}] + >::new(lwe_secret_key.iter().map(convert).collect()) } - } + }; + let ntt_ring_secret_key = ring_secret_key.clone().into_ntt_polynomial(); + Self { lwe_secret_key, ring_secret_key, ntt_ring_secret_key, - ntt_inv_ring_secret_key, parameters: params, csrng: RefCell::new(csrng), } @@ -180,12 +152,6 @@ impl SecretKeyPack { &self.ntt_ring_secret_key } - /// Returns a reference to the ntt inv ring secret key of this [`SecretKeyPack`]. - #[inline] - pub fn ntt_inv_ring_secret_key(&self) -> Option<&NTTPolynomial> { - self.ntt_inv_ring_secret_key.as_ref() - } - /// Returns the parameters of this [`SecretKeyPack`]. #[inline] pub fn parameters(&self) -> &Parameters { diff --git a/lattice/src/gadget/mod.rs b/lattice/src/gadget/mod.rs index 30f54256..9d189303 100644 --- a/lattice/src/gadget/mod.rs +++ b/lattice/src/gadget/mod.rs @@ -1,5 +1,3 @@ -mod ntru; mod rlwe; -pub use ntru::{GadgetNTRU, NTTGadgetNTRU}; pub use rlwe::{GadgetRLWE, NTTGadgetRLWE}; diff --git a/lattice/src/gadget/ntru.rs b/lattice/src/gadget/ntru.rs deleted file mode 100644 index 5ab48a0d..00000000 --- a/lattice/src/gadget/ntru.rs +++ /dev/null @@ -1,275 +0,0 @@ -use std::slice::{Iter, IterMut}; - -use algebra::transformation::AbstractNTT; -use algebra::{Basis, FieldDiscreteGaussianSampler, NTTField, NTTPolynomial, Polynomial}; -use rand::{CryptoRng, Rng}; - -use crate::{ntru::NTRU, DecompositionSpace, PolynomialSpace, NTTNTRU}; - -/// A representation of NTRU ciphertexts with respect to different powers -/// of a base, used to control noise growth in polynomial multiplications. -/// -/// [`GadgetNTRU`] stores a sequence of [`NTRU`] ciphertexts where each [`NTRU`] instance within -/// the `data` vector represents a ciphertext of a scaled version of a message `m` by successive -/// powers of the `basis`. The first element of `data` is the ciphertext of `m`, the second is `basis * m`, -/// the third is `basis^2 * m`, and so on. This is particularly useful in cryptographic operations -/// where reducing the noise growth during the multiplication of NTRU ciphertexts with polynomials is crucial. -/// -/// The struct is generic over a type `F` that must implement the [`NTTField`] trait, which ensures that -/// the field operations are compatible with Number Theoretic Transforms, a key requirement for -/// efficient polynomial operations in NTRU-based cryptography. -#[derive(Debug, Clone)] -pub struct GadgetNTRU { - /// A vector of NTRU ciphertexts, each encrypted message with a different power of the `basis`. - data: Vec>, - /// The base with respect to which the ciphertexts are scaled. - basis: Basis, -} - -impl From> for GadgetNTRU { - #[inline] - fn from(value: NTTGadgetNTRU) -> Self { - let NTTGadgetNTRU { data, basis } = value; - let data = data.into_iter().map(From::from).collect(); - Self { data, basis } - } -} - -impl GadgetNTRU { - /// Creates a new [`GadgetNTRU`]. - #[inline] - pub fn new(data: Vec>, basis: Basis) -> Self { - debug_assert_eq!(data.len(), basis.decompose_len()); - Self { data, basis } - } - - /// Creates a new [`GadgetNTRU`] with reference. - #[inline] - pub fn from_ref(data: &[NTRU], basis: Basis) -> Self { - debug_assert_eq!(data.len(), basis.decompose_len()); - Self { - data: data.to_vec(), - basis, - } - } - - /// Creates a [`GadgetNTRU`] with all entries equal to zero. - #[inline] - pub fn zero(coeff_count: usize, basis: Basis) -> Self { - Self { - data: (0..basis.decompose_len()) - .map(|_| NTRU::zero(coeff_count)) - .collect(), - basis, - } - } - - /// Set all entries equal to zero. - #[inline] - pub fn set_zero(&mut self) { - self.data.iter_mut().for_each(|ntru| ntru.set_zero()); - } - - /// Returns a reference to the `data` of this [`GadgetNTRU`]. - #[inline] - pub fn data(&self) -> &[NTRU] { - &self.data - } - - /// Returns the basis of this [`GadgetNTRU`]. - #[inline] - pub fn basis(&self) -> Basis { - self.basis - } - - /// Returns an iterator over the `data` of this [`GadgetNTRU`]. - #[inline] - pub fn iter(&self) -> Iter<'_, NTRU> { - self.data.iter() - } - - /// Returns an iterator over the `data` of this [`GadgetNTRU`] - /// that allows modifying each value. - #[inline] - pub fn iter_mut(&mut self) -> IterMut<'_, NTRU> { - self.data.iter_mut() - } -} - -/// A representation of NTRU ciphertexts with respect to different powers -/// of a base, used to control noise growth in polynomial multiplications. -/// -/// [`NTTGadgetNTRU`] stores a sequence of [`NTTNTRU`] ciphertexts where each [`NTTNTRU`] instance within -/// the `data` vector represents a ciphertext of a scaled version of a message `m` by successive -/// powers of the `basis`. The first element of `data` is the ciphertext of `m`, the second is `basis * m`, -/// the third is `basis^2 * m`, and so on. This is particularly useful in cryptographic operations -/// where reducing the noise growth during the multiplication of RLWE ciphertexts with polynomials is crucial. -/// -/// The struct is generic over a type `F` that must implement the [`NTTField`] trait, which ensures that -/// the field operations are compatible with Number Theoretic Transforms, a key requirement for -/// efficient polynomial operations in RLWE-based cryptography. -#[derive(Debug, Clone)] -pub struct NTTGadgetNTRU { - /// A vector of NTT NTRU ciphertexts, each encrypted message with a different power of the `basis`. - data: Vec>, - /// The base with respect to which the ciphertexts are scaled. - basis: Basis, -} - -impl From> for NTTGadgetNTRU { - #[inline] - fn from(value: GadgetNTRU) -> Self { - let GadgetNTRU { data, basis } = value; - let data = data.into_iter().map(From::from).collect(); - Self { data, basis } - } -} - -impl NTTGadgetNTRU { - /// Creates a new [`NTTGadgetNTRU`]. - #[inline] - pub fn new(data: Vec>, basis: Basis) -> Self { - Self { data, basis } - } - - /// Creates a new [`NTTGadgetNTRU`] with reference. - #[inline] - pub fn from_ref(data: &[NTTNTRU], basis: Basis) -> Self { - debug_assert_eq!(data.len(), basis.decompose_len()); - Self { - data: data.to_vec(), - basis, - } - } - - /// Creates a [`NTTGadgetNTRU`] with all entries equal to zero. - #[inline] - pub fn zero(coeff_count: usize, basis: Basis) -> Self { - Self { - data: (0..basis.decompose_len()) - .map(|_| NTTNTRU::zero(coeff_count)) - .collect(), - basis, - } - } - - /// Set all entries equal to zero. - #[inline] - pub fn set_zero(&mut self) { - self.data.iter_mut().for_each(|ntru| ntru.set_zero()); - } - - /// Returns a reference to the `data` of this [`NTTGadgetNTRU`]. - #[inline] - pub fn data(&self) -> &[NTTNTRU] { - &self.data - } - - /// Returns the basis of this [`NTTGadgetNTRU`]. - #[inline] - pub fn basis(&self) -> Basis { - self.basis - } - - /// Returns an iterator over the `data` of this [`NTTGadgetNTRU`]. - #[inline] - pub fn iter(&self) -> Iter<'_, NTTNTRU> { - self.data.iter() - } - - /// Returns an iterator over the `data` of this [`NTTGadgetNTRU`] - /// that allows modifying each value. - #[inline] - pub fn iter_mut(&mut self) -> IterMut<'_, NTTNTRU> { - self.data.iter_mut() - } - - /// Perform multiplication between [`NTTGadgetNTRU`] and [`Polynomial`], - /// stores the result into `destination`. - pub fn mul_ntru_inplace( - &self, - ntru: &Polynomial, - // Pre allocate space for decomposition - decompose_space: &mut DecompositionSpace, - polynomial_space: &mut PolynomialSpace, - // Output destination - destination: &mut NTTNTRU, - ) { - let coeff_count = ntru.coeff_count(); - debug_assert!(coeff_count.is_power_of_two()); - let ntt_table = F::get_ntt_table(coeff_count.trailing_zeros()).unwrap(); - - polynomial_space.copy_from(ntru); - - destination.set_zero(); - - self.iter().for_each(|g_ntru| { - polynomial_space.decompose_lsb_bits_inplace(self.basis, decompose_space.as_mut_slice()); - ntt_table.transform_slice(decompose_space.as_mut_slice()); - destination.add_ntt_ntru_mul_ntt_polynomial_assign(g_ntru, decompose_space); - }) - } - - /// Perform `self + rhs * ntt_polynomial`, and store the result into destination. - pub fn add_ntt_gadget_ntru_mul_ntt_polynomial_inplace( - &self, - rhs: &Self, - ntt_polynomial: &NTTPolynomial, - destination: &mut Self, - ) { - destination - .iter_mut() - .zip(self.iter()) - .zip(rhs.iter()) - .for_each(|((des, l), r)| { - l.add_ntt_ntru_mul_ntt_polynomial_inplace(r, ntt_polynomial, des); - }) - } - - /// Generate a `NTTGadgetNTRU` sample which encrypts `0`. - pub fn generate_random_zero_sample( - ntru_inv_secret_key: &NTTPolynomial, - basis: Basis, - error_sampler: FieldDiscreteGaussianSampler, - rng: &mut R, - ) -> Self - where - R: Rng + CryptoRng, - { - let data = (0..basis.decompose_len()) - .map(|_| { - >::generate_random_zero_sample(ntru_inv_secret_key, error_sampler, rng) - }) - .collect(); - Self { data, basis } - } - - /// Generate a `NTTGadgetNTRU` sample which encrypts `1`. - pub fn generate_random_one_sample( - ntru_inv_secret_key: &NTTPolynomial, - basis: Basis, - error_sampler: FieldDiscreteGaussianSampler, - rng: &mut R, - ) -> Self - where - R: Rng + CryptoRng, - { - let basis_value = basis.basis(); - let mut basis_power = F::one(); - - let data: Vec> = (0..basis.decompose_len()) - .map(|_| { - let r = >::generate_random_value_sample( - ntru_inv_secret_key, - basis_power, - error_sampler, - rng, - ); - basis_power = F::lazy_new(basis_power.value() * basis_value); - r - }) - .collect(); - - Self { data, basis } - } -} diff --git a/lattice/src/lib.rs b/lattice/src/lib.rs index 481a4f78..aefbc48f 100644 --- a/lattice/src/lib.rs +++ b/lattice/src/lib.rs @@ -31,14 +31,12 @@ mod gadget; mod lwe; -mod ntru; mod rgsw; mod rlwe; mod utils; -pub use gadget::{GadgetNTRU, GadgetRLWE, NTTGadgetNTRU, NTTGadgetRLWE}; +pub use gadget::{GadgetRLWE, NTTGadgetRLWE}; pub use lwe::LWE; -pub use ntru::{NTRU, NTTNTRU}; pub use rgsw::{NTTRGSW, RGSW}; pub use rlwe::{NTTRLWE, RLWE}; pub use utils::*; diff --git a/lattice/src/ntru.rs b/lattice/src/ntru.rs deleted file mode 100644 index 0e0831ba..00000000 --- a/lattice/src/ntru.rs +++ /dev/null @@ -1,520 +0,0 @@ -use algebra::transformation::AbstractNTT; -use algebra::{ - ntt_add_mul_assign, ntt_add_mul_inplace, FieldDiscreteGaussianSampler, NTTField, NTTPolynomial, - Polynomial, -}; -use rand::{CryptoRng, Rng}; - -use crate::{DecompositionSpace, NTTGadgetNTRU, NTTNTRUSpace, PolynomialSpace, LWE}; - -/// A cryptographic structure for NTRU. -/// This structure is used in advanced cryptographic systems and protocols, particularly -/// those that require efficient homomorphic encryption properties. It consists of a [`Polynomial`] -/// over a finite field that supports Number Theoretic Transforms (NTT), which is -/// often necessary for efficient polynomial multiplication. -/// -/// The [`NTRU`] struct is generic over a type `F` which is bounded by the `NTTField` trait, ensuring -/// that the operations of addition, subtraction, and multiplication are performed in a field suitable -/// for NTT. This is crucial for the security and correctness of cryptographic operations based on NTRU. -/// -/// The fields `data` is kept private within the crate to maintain encapsulation and is -/// accessible through public API functions that enforce any necessary invariants. -#[derive(Debug, Clone)] -pub struct NTRU { - data: Polynomial, -} - -impl From> for NTRU { - #[inline] - fn from(value: NTTNTRU) -> Self { - let NTTNTRU { data } = value; - Self { - data: data.into_native_polynomial(), - } - } -} - -impl NTRU { - /// Creates a new [`NTRU`]. - #[inline] - pub fn new(data: Polynomial) -> Self { - Self { data } - } - - /// Creates a new [`NTRU`] with reference of [`Polynomial`]. - #[inline] - pub fn from_ref(data: &Polynomial) -> Self { - Self { data: data.clone() } - } - - /// Creates a new [`NTRU`] that is initialized to zero. - /// - /// # Arguments - /// - /// * `coeff_count` - The number of coefficients in the polynomial. - /// - /// # Returns - /// - /// A new [`NTRU`] whose polynomial is initialized to zero. - #[inline] - pub fn zero(coeff_count: usize) -> Self { - Self { - data: Polynomial::zero(coeff_count), - } - } - - /// Set all entries equal to zero. - #[inline] - pub fn set_zero(&mut self) { - self.data.set_zero(); - } - - /// Returns a reference to the `data` of this [`NTRU`]. - #[inline] - pub fn data(&self) -> &Polynomial { - &self.data - } - - /// Returns a mutable reference to the `data` of this [`NTRU`]. - #[inline] - pub fn data_mut(&mut self) -> &mut Polynomial { - &mut self.data - } - - /// Extracts a slice of `data` of this [`NTRU`]. - #[inline] - pub fn as_slice(&self) -> &[F] { - self.data.as_slice() - } - - /// Extracts a mutable slice of `data` of this [`NTRU`]. - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [F] { - self.data.as_mut_slice() - } - - /// Perform element-wise addition of two [`NTRU`]. - /// - /// # Attention - /// - /// In this function, `self` is a reference. - /// If your `self` is not a reference, you can use function `add_element_wise`. - #[inline] - pub fn add_element_wise_ref(&self, rhs: &Self) -> Self { - Self { - data: self.data() + rhs.data(), - } - } - - /// Perform element-wise addition of two [`NTRU`]. - /// - /// # Attention - /// - /// In this function, `self` is not a reference. - /// If your `self` is a reference, you can use function `add_element_wise_ref`. - #[inline] - pub fn add_element_wise(self, rhs: &Self) -> Self { - Self { - data: self.data + rhs.data(), - } - } - - /// Perform element-wise subtraction of two [`NTRU`]. - /// - /// # Attention - /// - /// In this function, `self` is a reference. - /// If your `self` is not a reference, you can use function `sub_element_wise`. - #[inline] - pub fn sub_element_wise_ref(&self, rhs: &Self) -> Self { - Self { - data: self.data() - rhs.data(), - } - } - - /// Perform element-wise subtraction of two [`NTRU`]. - /// - /// # Attention - /// - /// In this function, `self` is not a reference. - /// If your `self` is a reference, you can use function `sub_element_wise_ref`. - #[inline] - pub fn sub_element_wise(self, rhs: &Self) -> Self { - Self { - data: self.data - rhs.data(), - } - } - - /// Performs an in-place element-wise addition - /// on the `self` [`NTRU`] with another `rhs` [`NTRU`]. - #[inline] - pub fn add_assign_element_wise(&mut self, rhs: &Self) { - self.data += rhs.data(); - } - - /// Performs an in-place element-wise subtraction - /// on the `self` [`NTRU`] with another `rhs` [`NTRU`]. - #[inline] - pub fn sub_assign_element_wise(&mut self, rhs: &Self) { - self.data -= rhs.data(); - } - - /// Extract an LWE sample from [`NTRU`]. - #[inline] - pub fn extract_lwe(&self) -> LWE { - let mut a: Vec = self.data.as_slice().to_vec(); - a[1..].reverse(); - a[0] = -a[0]; - - LWE::::new(a, F::zero()) - } - - /// Extract an LWE sample from [`NTRU`]. - #[inline] - pub fn extract_lwe_locally(self) -> LWE { - let mut a: Vec = self.data.data(); - a[1..].reverse(); - a[0] = -a[0]; - - LWE::::new(a, F::zero()) - } - - /// Perform `self = self + rhs * Y^r` for functional bootstrapping where `Y = X^(2N/q)`. - pub fn add_assign_rhs_mul_monic_monomial( - &mut self, - rhs: &Self, - // N - ntru_dimension: usize, - r: usize, - ) { - if r <= ntru_dimension { - #[inline] - fn rotate_add( - x: &mut Polynomial, - y: &Polynomial, - r: usize, - n_sub_r: usize, - ) { - x[0..r] - .iter_mut() - .zip(y[n_sub_r..].iter()) - .for_each(|(u, v)| *u -= v); - x[r..] - .iter_mut() - .zip(y[0..n_sub_r].iter()) - .for_each(|(u, v)| *u += v); - } - let n_sub_r = ntru_dimension - r; - rotate_add(self.data_mut(), rhs.data(), r, n_sub_r); - } else { - #[inline] - fn rotate_add( - x: &mut Polynomial, - y: &Polynomial, - r: usize, - n_sub_r: usize, - ) { - x[0..r] - .iter_mut() - .zip(y[n_sub_r..].iter()) - .for_each(|(u, v)| *u += v); - x[r..] - .iter_mut() - .zip(y[0..n_sub_r].iter()) - .for_each(|(u, v)| *u -= v); - } - let r = r - ntru_dimension; - let n_sub_r = ntru_dimension.checked_sub(r).unwrap(); - rotate_add(self.data_mut(), rhs.data(), r, n_sub_r); - } - } - - /// Performs a multiplication on the `self` [`NTRU`] with another `small_ntt_gadget_ntru` [`NTTGadgetNTRU`], - /// output the [`NTRU`] result into `destination`. - /// - /// # Attention - /// The message of **`small_ntt_gadget_ntru`** is restricted to small messages `m`, typically `m = ±Xⁱ` - #[inline] - pub fn mul_small_ntt_gadget_ntru_inplace( - &self, - small_ntt_gadget_ntru: &NTTGadgetNTRU, - // Pre allocate space - decompose_space: &mut DecompositionSpace, - polynomial_space: &mut PolynomialSpace, - median: &mut NTTNTRUSpace, - // Output destination - destination: &mut NTRU, - ) { - small_ntt_gadget_ntru.mul_ntru_inplace( - self.data(), - decompose_space, - polynomial_space, - median, - ); - - median.inverse_transform_inplace(destination) - } -} - -/// A cryptographic structure for NTRU. -/// This structure is used in advanced cryptographic systems and protocols, particularly -/// those that require efficient homomorphic encryption properties. It consists of a [`NTTPolynomial`] -/// over a finite field that supports Number Theoretic Transforms (NTT), which is -/// often necessary for efficient polynomial multiplication. -/// -/// The [`NTTNTRU`] struct is generic over a type `F` which is bounded by the `NTTField` trait, ensuring -/// that the operations of addition, subtraction, and multiplication are performed in a field suitable -/// for NTT. This is crucial for the security and correctness of cryptographic operations based on RLWE. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NTTNTRU { - pub(crate) data: NTTPolynomial, -} - -impl From> for NTTNTRU { - #[inline] - fn from(value: NTRU) -> Self { - let NTRU { data } = value; - Self { - data: data.into_ntt_polynomial(), - } - } -} - -impl NTTNTRU { - /// Creates a new [`NTTNTRU`]. - #[inline] - pub fn new(data: NTTPolynomial) -> Self { - Self { data } - } - - /// Creates a new [`NTTNTRU`] with reference of [`NTTPolynomial`]. - #[inline] - pub fn from_ref(data: &NTTPolynomial) -> Self { - Self { data: data.clone() } - } - - /// Creates a [`NTTNTRU`] with all entries equal to zero. - #[inline] - pub fn zero(coeff_count: usize) -> NTTNTRU { - Self { - data: >::zero(coeff_count), - } - } - - /// Set all entries equal to zero. - #[inline] - pub fn set_zero(&mut self) { - self.data.set_zero(); - } - - /// Returns a reference to the `data` of this [`NTTNTRU`]. - #[inline] - pub fn data(&self) -> &NTTPolynomial { - self.data.as_ref() - } - - /// Returns a mutable reference to the `data` of this [`NTTNTRU`]. - #[inline] - pub fn data_mut(&mut self) -> &mut NTTPolynomial { - &mut self.data - } - - /// Extracts a slice of `data` of this [`NTTNTRU`]. - #[inline] - pub fn as_slice(&self) -> &[F] { - self.data.as_slice() - } - - /// Extracts a mutable slice of `data` of this [`NTTNTRU`]. - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [F] { - self.data.as_mut_slice() - } - - /// Perform element-wise addition of two [`NTTNTRU`]. - /// - /// # Attention - /// - /// In this function, `self` is a reference. - /// If your `self` is not a reference, you can use function `add_element_wise`. - #[inline] - pub fn add_element_wise_ref(&self, rhs: &Self) -> Self { - Self { - data: self.data() + rhs.data(), - } - } - - /// Perform element-wise addition of two [`NTTNTRU`]. - /// - /// # Attention - /// - /// In this function, `self` is not a reference. - /// If your `self` is a reference, you can use function `add_element_wise_ref`. - #[inline] - pub fn add_element_wise(self, rhs: &Self) -> Self { - Self { - data: self.data + rhs.data(), - } - } - - /// Perform element-wise subtraction of two [`NTTNTRU`]. - /// - /// # Attention - /// - /// In this function, `self` is a reference. - /// If your `self` is not a reference, you can use function `sub_element_wise`. - #[inline] - pub fn sub_element_wise_ref(&self, rhs: &Self) -> Self { - Self { - data: self.data() - rhs.data(), - } - } - - /// Perform element-wise subtraction of two [`NTTNTRU`]. - /// - /// # Attention - /// - /// In this function, `self` is not a reference. - /// If your `self` is a reference, you can use function `sub_element_wise_ref`. - #[inline] - pub fn sub_element_wise(self, rhs: &Self) -> Self { - Self { - data: self.data - rhs.data(), - } - } - - /// Performs an in-place element-wise addition - /// on the `self` [`NTTNTRU`] with another `rhs` [`NTTNTRU`]. - #[inline] - pub fn add_assign_element_wise(&mut self, rhs: &Self) { - self.data += rhs.data(); - } - - /// Performs an in-place element-wise subtraction - /// on the `self` [`NTTNTRU`] with another `rhs` [`NTTNTRU`]. - #[inline] - pub fn sub_assign_element_wise(&mut self, rhs: &Self) { - self.data -= rhs.data(); - } - - /// ntt inverse transform - pub fn inverse_transform_inplace(&self, destination: &mut NTRU) { - let coeff_count = destination.data.coeff_count(); - debug_assert!(coeff_count.is_power_of_two()); - let ntt_table = F::get_ntt_table(coeff_count.trailing_zeros()).unwrap(); - - let d = destination.as_mut_slice(); - - d.copy_from_slice(self.as_slice()); - - ntt_table.inverse_transform_slice(d); - } - - /// Performs `self = self + ntt_ntru * ntt_polynomial`. - #[inline] - pub fn add_ntt_ntru_mul_ntt_polynomial_assign( - &mut self, - ntt_ntru: &NTTNTRU, - ntt_polynomial: &NTTPolynomial, - ) { - ntt_add_mul_assign(self.data_mut(), ntt_ntru.data(), ntt_polynomial); - } - - /// Performs `destination = self + ntt_ntru * ntt_polynomial`. - #[inline] - pub fn add_ntt_ntru_mul_ntt_polynomial_inplace( - &self, - ntt_ntru: &Self, - ntt_polynomial: &NTTPolynomial, - destination: &mut Self, - ) { - ntt_add_mul_inplace( - self.data(), - ntt_ntru.data(), - ntt_polynomial, - destination.data_mut(), - ); - } - - /// Performs `self = self + gadget_ntru * polynomial`. - #[inline] - pub fn add_assign_gadget_ntru_mul_polynomial_inplace( - &mut self, - gadget_ntru: &NTTGadgetNTRU, - mut polynomial: Polynomial, - decompose_space: &mut DecompositionSpace, - ) { - let coeff_count = polynomial.coeff_count(); - debug_assert!(coeff_count.is_power_of_two()); - let ntt_table = F::get_ntt_table(coeff_count.trailing_zeros()).unwrap(); - let decompose_space = decompose_space.get_mut(); - let basis = gadget_ntru.basis(); - - gadget_ntru.iter().for_each(|g| { - polynomial.decompose_lsb_bits_inplace(basis, decompose_space.as_mut_slice()); - ntt_table.transform_slice(decompose_space.as_mut_slice()); - self.add_ntt_ntru_mul_ntt_polynomial_assign(g, decompose_space); - }) - } - - /// Performs `self = self - gadget_ntru * polynomial`. - #[inline] - pub fn sub_assign_gadget_ntru_mul_polynomial_inplace( - &mut self, - gadget_ntru: &NTTGadgetNTRU, - polynomial: Polynomial, - decompose_space: &mut DecompositionSpace, - ) { - let coeff_count = polynomial.coeff_count(); - debug_assert!(coeff_count.is_power_of_two()); - let ntt_table = F::get_ntt_table(coeff_count.trailing_zeros()).unwrap(); - let decompose_space = decompose_space.get_mut(); - let basis = gadget_ntru.basis(); - - let mut polynomial = -polynomial; - - gadget_ntru.iter().for_each(|g| { - polynomial.decompose_lsb_bits_inplace(basis, decompose_space.as_mut_slice()); - ntt_table.transform_slice(decompose_space.as_mut_slice()); - self.add_ntt_ntru_mul_ntt_polynomial_assign(g, decompose_space); - }) - } - - /// Generate a `NTTNTRU` sample which encrypts `0`. - pub fn generate_random_zero_sample( - ntru_inv_secret_key: &NTTPolynomial, - error_sampler: FieldDiscreteGaussianSampler, - rng: &mut R, - ) -> Self - where - R: Rng + CryptoRng, - { - let ntru_dimension = ntru_inv_secret_key.coeff_count(); - - let mut data = >::random_with_gaussian(ntru_dimension, rng, error_sampler) - .into_ntt_polynomial(); - data *= ntru_inv_secret_key; - - Self { data } - } - - /// Generate a `NTTNTRU` sample which encrypts `value`. - pub fn generate_random_value_sample( - ntru_inv_secret_key: &NTTPolynomial, - value: F, - error_sampler: FieldDiscreteGaussianSampler, - rng: &mut R, - ) -> Self - where - R: Rng + CryptoRng, - { - let ntru_dimension = ntru_inv_secret_key.coeff_count(); - - let mut data = >::random_with_gaussian(ntru_dimension, rng, error_sampler) - .into_ntt_polynomial(); - data *= ntru_inv_secret_key; - data.iter_mut().for_each(|v| *v += value); - - Self { data } - } -} diff --git a/lattice/src/utils/space.rs b/lattice/src/utils/space.rs index 055ec9c8..92794396 100644 --- a/lattice/src/utils/space.rs +++ b/lattice/src/utils/space.rs @@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut}; use algebra::{Basis, NTTField, NTTPolynomial, Polynomial}; -use crate::{NTTGadgetNTRU, NTRU, NTTNTRU, NTTRGSW, NTTRLWE, RLWE}; +use crate::{NTTRGSW, NTTRLWE, RLWE}; /// Pre allocated space for inplace decomposition. #[derive(Debug)] @@ -267,135 +267,3 @@ impl NTTRGSWSpace { &mut self.space } } - -/// Pre allocated space. -#[derive(Debug)] -pub struct NTTNTRUSpace { - space: NTTNTRU, -} - -impl Deref for NTTNTRUSpace { - type Target = NTTNTRU; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.space - } -} - -impl DerefMut for NTTNTRUSpace { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.space - } -} - -impl NTTNTRUSpace { - /// Creates a new [`NTTNTRUSpace`]. - #[inline] - pub fn new(coeff_count: usize) -> Self { - Self { - space: NTTNTRU::zero(coeff_count), - } - } - - /// Gets the pre allocated space. - #[inline] - pub fn get(&self) -> &NTTNTRU { - &self.space - } - - /// Gets the mutable pre allocated space. - #[inline] - pub fn get_mut(&mut self) -> &mut NTTNTRU { - &mut self.space - } -} - -/// Pre allocated space. -#[derive(Debug)] -pub struct NTRUSpace { - space: NTRU, -} - -impl Deref for NTRUSpace { - type Target = NTRU; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.space - } -} - -impl DerefMut for NTRUSpace { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.space - } -} - -impl NTRUSpace { - /// Creates a new [`NTRUSpace`]. - #[inline] - pub fn new(coeff_count: usize) -> Self { - Self { - space: NTRU::zero(coeff_count), - } - } - - /// Gets the pre allocated space. - #[inline] - pub fn get(&self) -> &NTRU { - &self.space - } - - /// Gets the mutable pre allocated space. - #[inline] - pub fn get_mut(&mut self) -> &mut NTRU { - &mut self.space - } -} - -/// Pre allocated space. -#[derive(Debug)] -pub struct NTTGadgetNTRUSpace { - space: NTTGadgetNTRU, -} - -impl Deref for NTTGadgetNTRUSpace { - type Target = NTTGadgetNTRU; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.space - } -} - -impl DerefMut for NTTGadgetNTRUSpace { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.space - } -} - -impl NTTGadgetNTRUSpace { - /// Creates a new [`NTTGadgetNTRUSpace`]. - #[inline] - pub fn new(coeff_count: usize, basis: Basis) -> Self { - Self { - space: NTTGadgetNTRU::zero(coeff_count, basis), - } - } - - /// Gets the pre allocated space. - #[inline] - pub fn get(&self) -> &NTTGadgetNTRU { - &self.space - } - - /// Gets the mutable pre allocated space. - #[inline] - pub fn get_mut(&mut self) -> &mut NTTGadgetNTRU { - &mut self.space - } -} diff --git a/lattice/tests/lattice_test.rs b/lattice/tests/lattice_test.rs index 01000a3b..32333537 100644 --- a/lattice/tests/lattice_test.rs +++ b/lattice/tests/lattice_test.rs @@ -6,7 +6,6 @@ use algebra::{ ModulusConfig, Polynomial, }; use lattice::*; -use num_traits::{Inv, One}; use rand::prelude::*; use rand_distr::{Standard, Uniform}; @@ -209,52 +208,6 @@ fn test_rlwe_he() { assert_eq!(decrypted_add, v_add); } -#[test] -fn test_ntru_he() { - let mut rng = rand::thread_rng(); - let chi = FieldDiscreteGaussianSampler::new(0., 3.2).unwrap(); - let dis = Uniform::new(0, FT); - - let v0: Vec = dis.sample_iter(&mut rng).take(N).collect(); - let v1: Vec = dis.sample_iter(&mut rng).take(N).collect(); - - let v_add: Vec = v0 - .iter() - .zip(v1.iter()) - .map(|(a, b)| (*a + b) % FT) - .collect(); - - let v0 = PolyFF::new(v0.into_iter().map(encode).collect()); - let v1 = PolyFF::new(v1.into_iter().map(encode).collect()); - - let mut s = PolyFF::random_with_ternary(N, &mut rng); - s.mul_scalar_assign(Fp32(4)); - s[0] += FF::one(); - - let inv_s = (&s).inv(); - - let ntru0 = { - let e = PolyFF::random_with_distribution(N, &mut rng, chi); - let a = e * &inv_s + v0; - NTRU::new(a) - }; - - let ntru1 = { - let e = PolyFF::random_with_distribution(N, &mut rng, chi); - let a = e * &inv_s + v1; - NTRU::new(a) - }; - - let ntru_add = ntru0.add_element_wise(&ntru1); - - let decrypted_add = (ntru_add.data() * &s) - .into_iter() - .map(decode) - .collect::>(); - - assert_eq!(decrypted_add, v_add); -} - #[test] fn extract_lwe_test() { let mut rng = thread_rng(); @@ -487,64 +440,3 @@ fn test_rgsw_mul_rgsw() { let decoded_decrypt_neg_sm0m1: Vec = decrypted_neg_sm0m1.into_iter().map(decode).collect(); assert_eq!(decoded_neg_sm0m1, decoded_decrypt_neg_sm0m1); } - -#[test] -fn test_gadget_ntru_mul_ntru() { - let mut rng = rand::thread_rng(); - let chi = FieldDiscreteGaussianSampler::new(0., 3.2).unwrap(); - - let dis = Uniform::new(0, FT); - - let v0: Vec = dis.sample_iter(&mut rng).take(N).collect(); - - let m0 = PolyFF::new(v0.into_iter().map(encode).collect()); - let m1 = PolyFF::random_with_distribution(N, &mut rng, FieldTernarySampler); - - let m0m1 = &m0 * &m1; - - let mut s = PolyFF::random_with_ternary(N, &mut rng); - s.mul_scalar_assign(Fp32(4)); - s[0] += FF::one(); - - let inv_s = (&s).inv(); - - let basis = >::new(BITS); - - let gadget_ntru = { - let data = (0..basis.decompose_len()) - .map(|i| { - let e = PolyFF::random_with_distribution(N, &mut rng, chi); - let a = e * &inv_s + m1.mul_scalar(Fp32::new(B.pow(i as u32) as Inner)); - NTRU::new(a) - }) - .collect::>>(); - - GadgetNTRU::new(data, basis) - }; - - let (ntru, _e) = { - let e = PolyFF::random_with_distribution(N, &mut rng, chi); - let a = &e * &inv_s + m0; - - (NTRU::new(a), e) - }; - - let mut decompose_space = DecompositionSpace::new(N); - let mut polynomial_space = PolynomialSpace::new(N); - let mut median = NTTNTRUSpace::new(N); - let mut destination = NTRUSpace::new(N); - - let ntt_gadget_ntru = gadget_ntru.into(); - ntru.mul_small_ntt_gadget_ntru_inplace( - &ntt_gadget_ntru, - &mut decompose_space, - &mut polynomial_space, - &mut median, - &mut destination, - ); - let decrypt_mul = destination.data() * &s; - - let decoded_m0m1: Vec = m0m1.into_iter().map(decode).collect(); - let decoded_decrypt: Vec = decrypt_mul.into_iter().map(decode).collect(); - assert_eq!(decoded_m0m1, decoded_decrypt); -}