use num_traits::{ConstZero, FromPrimitive, PrimInt}; use crate::{ backend::Modulus, decomposer::{Decomposer, NumInfo}, utils::log2, }; pub(crate) trait DoubleDecomposerCount { type Count; fn a(&self) -> Self::Count; fn b(&self) -> Self::Count; } pub(crate) trait DoubleDecomposerParams { type Base; type Count; fn decomposition_base(&self) -> Self::Base; fn decomposition_count_a(&self) -> Self::Count; fn decomposition_count_b(&self) -> Self::Count; } pub(crate) trait SingleDecomposerParams { type Base; type Count; // fn new(base: Self::Base, count: Self::Count) -> Self; fn decomposition_base(&self) -> Self::Base; fn decomposition_count(&self) -> Self::Count; } impl DoubleDecomposerParams for ( DecompostionLogBase, // Assume (Decomposition count for A, Decomposition count for B) (DecompositionCount, DecompositionCount), ) { type Base = DecompostionLogBase; type Count = DecompositionCount; // fn new( // base: DecompostionLogBase, // count_a: DecompositionCount, // count_b: DecompositionCount, // ) -> Self { // (base, (count_a, count_b)) // } fn decomposition_base(&self) -> Self::Base { self.0 } fn decomposition_count_a(&self) -> Self::Count { self.1 .0 } fn decomposition_count_b(&self) -> Self::Count { self.1 .1 } } impl SingleDecomposerParams for (DecompostionLogBase, DecompositionCount) { type Base = DecompostionLogBase; type Count = DecompositionCount; // fn new(base: DecompostionLogBase, count: DecompositionCount) -> Self { // (base, count) // } fn decomposition_base(&self) -> Self::Base { self.0 } fn decomposition_count(&self) -> Self::Count { self.1 } } #[derive(Clone, PartialEq, Debug)] pub(crate) enum ParameterVariant { SingleParty, MultiParty, NonInteractiveMultiParty, } #[derive(Clone, PartialEq)] pub struct BoolParameters { /// RLWE ciphertext modulus Q rlwe_q: CiphertextModulus, /// LWE ciphertext modulus q (usually referred to as Q_{ks}) lwe_q: CiphertextModulus, /// Blind rotation modulus. It is the modulus to which we switch before /// blind rotation. /// /// Since blind rotation decrypts LWE ciphertext in the exponent of a ring /// polynomial, which is a ring mod 2N, blind rotation modulus is /// always <= 2N. br_q: usize, /// Ring dimension `N` for 2N^{th} cyclotomic polynomial ring rlwe_n: PolynomialSize, /// LWE dimension `n` lwe_n: LweDimension, /// LWE key switch decompositon params lwe_decomposer_params: (DecompostionLogBase, DecompositionCount), /// Decompostion parameters for RLWE x RGSW. /// /// We restrict decomposition for RLWE'(-sm) and RLWE'(m) to have same base /// but can have different decomposition count. We refer to this /// DoubleDecomposer / RlweDecomposer /// /// Decomposition count `d_a` (i.e. for SignedDecompose(RLWE_A(m)) x /// RLWE'(-sm)) and `d_b` (i.e. for SignedDecompose(RLWE_B(m)) x RLWE'(m)) /// are always stored as `(d_a, d_b)` rlrg_decomposer_params: ( DecompostionLogBase, (DecompositionCount, DecompositionCount), ), /// Decomposition parameters for RLWE automorphism auto_decomposer_params: (DecompostionLogBase, DecompositionCount), /// Decomposition parameters for RGSW0 x RGSW1 /// /// `0` and `1` indicate that RGSW0 and RGSW1 may not use same decomposition /// parameters. /// /// In RGSW0 x RGSW1, decomposition parameters for RGSW1 are required. /// Hence, the parameters we store are decomposition parameters of RGSW1. /// /// Like RLWE x RGSW decomposition parameters (1) we restrict to same base /// but can have different decomposition counts `d_a` and `d_b` and (2) /// decomposition count `d_a` and `d_b` are always stored as `(d_a, d_b)` /// /// RGSW0 x RGSW1 are optional because they only necessary to be supplied in /// multi-party setting. rgrg_decomposer_params: Option<( DecompostionLogBase, (DecompositionCount, DecompositionCount), )>, /// Decomposition parameters for non-interactive key switching from u_j to /// s, hwere u_j is RLWE secret `u` of party `j` and `s` is the ideal RLWE /// secret key. /// /// Decomposition parameters for non-interactive key switching are optional /// and must be supplied only for non-interactive multi-party non_interactive_ui_to_s_key_switch_decomposer: Option<(DecompostionLogBase, DecompositionCount)>, /// Group generator for Z^*_{2N} g: usize, /// Window size parameter for LMKC++ blind rotation w: usize, /// Parameter variant variant: ParameterVariant, } impl BoolParameters { pub(crate) fn rlwe_q(&self) -> &CiphertextModulus { &self.rlwe_q } pub(crate) fn lwe_q(&self) -> &CiphertextModulus { &self.lwe_q } pub(crate) fn br_q(&self) -> &usize { &self.br_q } pub(crate) fn rlwe_n(&self) -> &PolynomialSize { &self.rlwe_n } pub(crate) fn lwe_n(&self) -> &LweDimension { &self.lwe_n } pub(crate) fn g(&self) -> usize { self.g } pub(crate) fn w(&self) -> usize { self.w } pub(crate) fn rlwe_by_rgsw_decomposition_params( &self, ) -> &( DecompostionLogBase, (DecompositionCount, DecompositionCount), ) { &self.rlrg_decomposer_params } pub(crate) fn rgsw_by_rgsw_decomposition_params( &self, ) -> ( DecompostionLogBase, (DecompositionCount, DecompositionCount), ) { self.rgrg_decomposer_params.expect(&format!( "Parameter variant {:?} does not support RGSWxRGSW", self.variant )) } pub(crate) fn rlwe_rgsw_decomposition_base(&self) -> DecompostionLogBase { self.rlrg_decomposer_params.0 } pub(crate) fn rlwe_rgsw_decomposition_count(&self) -> (DecompositionCount, DecompositionCount) { self.rlrg_decomposer_params.1 } pub(crate) fn rgsw_rgsw_decomposition_count(&self) -> (DecompositionCount, DecompositionCount) { let params = self.rgrg_decomposer_params.expect(&format!( "Parameter variant {:?} does not support RGSW x RGSW", self.variant )); params.1 } pub(crate) fn auto_decomposition_param(&self) -> &(DecompostionLogBase, DecompositionCount) { &self.auto_decomposer_params } pub(crate) fn auto_decomposition_base(&self) -> DecompostionLogBase { self.auto_decomposer_params.decomposition_base() } pub(crate) fn auto_decomposition_count(&self) -> DecompositionCount { self.auto_decomposer_params.decomposition_count() } pub(crate) fn lwe_decomposition_base(&self) -> DecompostionLogBase { self.lwe_decomposer_params.decomposition_base() } pub(crate) fn lwe_decomposition_count(&self) -> DecompositionCount { self.lwe_decomposer_params.decomposition_count() } pub(crate) fn non_interactive_ui_to_s_key_switch_decomposition_count( &self, ) -> DecompositionCount { let params = self .non_interactive_ui_to_s_key_switch_decomposer .expect(&format!( "Parameter variant {:?} does not support non-interactive", self.variant )); params.decomposition_count() } pub(crate) fn rgsw_rgsw_decomposer>(&self) -> (D, D) where El: Copy, { let params = self.rgrg_decomposer_params.expect(&format!( "Parameter variant {:?} does not support RGSW x RGSW", self.variant )); ( // A D::new( self.rlwe_q.0, params.decomposition_base().0, params.decomposition_count_a().0, ), // B D::new( self.rlwe_q.0, params.decomposition_base().0, params.decomposition_count_b().0, ), ) } pub(crate) fn auto_decomposer>(&self) -> D where El: Copy, { D::new( self.rlwe_q.0, self.auto_decomposer_params.decomposition_base().0, self.auto_decomposer_params.decomposition_count().0, ) } pub(crate) fn lwe_decomposer>(&self) -> D where El: Copy, { D::new( self.lwe_q.0, self.lwe_decomposer_params.decomposition_base().0, self.lwe_decomposer_params.decomposition_count().0, ) } pub(crate) fn rlwe_rgsw_decomposer>(&self) -> (D, D) where El: Copy, { ( // A D::new( self.rlwe_q.0, self.rlrg_decomposer_params.decomposition_base().0, self.rlrg_decomposer_params.decomposition_count_a().0, ), // B D::new( self.rlwe_q.0, self.rlrg_decomposer_params.decomposition_base().0, self.rlrg_decomposer_params.decomposition_count_b().0, ), ) } pub(crate) fn non_interactive_ui_to_s_key_switch_decomposer>( &self, ) -> D where El: Copy, { let params = self .non_interactive_ui_to_s_key_switch_decomposer .expect(&format!( "Parameter variant {:?} does not support non-interactive", self.variant )); D::new( self.rlwe_q.0, params.decomposition_base().0, params.decomposition_count().0, ) } /// Returns dlogs of `g` for which auto keys are required as /// per the parameter. Given that autos are required for [-g, g, g^2, ..., /// g^w] function returns the following [0, 1, 2, ..., w] where `w` is /// the window size. Note that although g^0 = 1, we use 0 for -g. pub(crate) fn auto_element_dlogs(&self) -> Vec { let mut els = vec![0]; (1..self.w + 1).into_iter().for_each(|e| { els.push(e); }); els } pub(crate) fn variant(&self) -> &ParameterVariant { &self.variant } } #[derive(Clone, Copy, PartialEq)] pub(crate) struct DecompostionLogBase(pub(crate) usize); impl AsRef for DecompostionLogBase { fn as_ref(&self) -> &usize { &self.0 } } #[derive(Clone, Copy, PartialEq)] pub(crate) struct DecompositionCount(pub(crate) usize); impl AsRef for DecompositionCount { fn as_ref(&self) -> &usize { &self.0 } } #[derive(Clone, Copy, PartialEq)] pub(crate) struct LweDimension(pub(crate) usize); #[derive(Clone, Copy, PartialEq)] pub(crate) struct PolynomialSize(pub(crate) usize); #[derive(Clone, Copy, PartialEq, Debug)] /// T equals modulus when modulus is non-native. Otherwise T equals 0. bool is /// true when modulus is native, false otherwise. pub struct CiphertextModulus(T, bool); impl CiphertextModulus { const fn new_native() -> Self { // T::zero is stored only for convenience. It has no use when modulus // is native. That is, either u128,u64,u32,u16 Self(T::ZERO, true) } const fn new_non_native(q: T) -> Self { Self(q, false) } } impl CiphertextModulus where T: PrimInt + NumInfo, { fn _bits() -> usize { T::BITS as usize } fn _native(&self) -> bool { self.1 } fn _half_q(&self) -> T { if self._native() { T::one() << (Self::_bits() - 1) } else { self.0 >> 1 } } fn _q(&self) -> Option { if self._native() { None } else { Some(self.0) } } } impl Modulus for CiphertextModulus where T: PrimInt + FromPrimitive + NumInfo, { type Element = T; fn is_native(&self) -> bool { self._native() } fn largest_unsigned_value(&self) -> Self::Element { if self._native() { T::max_value() } else { self.0 - T::one() } } fn neg_one(&self) -> Self::Element { if self._native() { T::max_value() } else { self.0 - T::one() } } // fn signed_max(&self) -> Self::Element {} // fn signed_min(&self) -> Self::Element {} fn smallest_unsigned_value(&self) -> Self::Element { T::zero() } fn map_element_to_i64(&self, v: &Self::Element) -> i64 { assert!(*v <= self.largest_unsigned_value()); if *v > self._half_q() { -((self.largest_unsigned_value() - *v) + T::one()) .to_i64() .unwrap() } else { v.to_i64().unwrap() } } fn map_element_from_f64(&self, v: f64) -> Self::Element { let v = v.round(); let v_el = T::from_f64(v.abs()).unwrap(); assert!(v_el <= self.largest_unsigned_value()); if v < 0.0 { self.largest_unsigned_value() - v_el + T::one() } else { v_el } } fn map_element_from_i64(&self, v: i64) -> Self::Element { let v_el = T::from_i64(v.abs()).unwrap(); assert!(v_el <= self.largest_unsigned_value()); if v < 0 { self.largest_unsigned_value() - v_el + T::one() } else { v_el } } fn q(&self) -> Option { self._q() } fn q_as_f64(&self) -> Option { if self._native() { Some(T::max_value().to_f64().unwrap() + 1.0) } else { self.0.to_f64() } } fn log_q(&self) -> usize { if self.is_native() { Self::_bits() } else { log2(&self.q().unwrap()) } } } pub(crate) const MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(1152921504606830593), lwe_q: CiphertextModulus::new_non_native(1 << 20), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(500), lwe_decomposer_params: (DecompostionLogBase(4), DecompositionCount(5)), rlrg_decomposer_params: ( DecompostionLogBase(12), (DecompositionCount(5), DecompositionCount(5)), ), rgrg_decomposer_params: Some(( DecompostionLogBase(12), (DecompositionCount(5), DecompositionCount(5)), )), auto_decomposer_params: (DecompostionLogBase(12), DecompositionCount(5)), non_interactive_ui_to_s_key_switch_decomposer: None, g: 5, w: 10, variant: ParameterVariant::MultiParty, }; pub(crate) const SMALL_MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(36028797018820609), lwe_q: CiphertextModulus::new_non_native(1 << 20), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(600), lwe_decomposer_params: (DecompostionLogBase(4), DecompositionCount(5)), rlrg_decomposer_params: ( DecompostionLogBase(11), (DecompositionCount(2), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( DecompostionLogBase(11), (DecompositionCount(5), DecompositionCount(4)), )), auto_decomposer_params: (DecompostionLogBase(11), DecompositionCount(2)), non_interactive_ui_to_s_key_switch_decomposer: None, g: 5, w: 10, variant: ParameterVariant::MultiParty, }; pub(crate) const I_2P: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(18014398509404161), lwe_q: CiphertextModulus::new_non_native(1 << 15), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(480), lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(11)), rlrg_decomposer_params: ( DecompostionLogBase(16), (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( DecompostionLogBase(8), (DecompositionCount(6), DecompositionCount(6)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), non_interactive_ui_to_s_key_switch_decomposer: None, g: 5, w: 10, variant: ParameterVariant::MultiParty, }; pub(crate) const NI_2P: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(18014398509404161), lwe_q: CiphertextModulus::new_non_native(1 << 15), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(480), lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(12)), rlrg_decomposer_params: ( DecompostionLogBase(16), (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( DecompostionLogBase(6), (DecompositionCount(7), DecompositionCount(7)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), non_interactive_ui_to_s_key_switch_decomposer: Some(( DecompostionLogBase(1), DecompositionCount(50), )), g: 5, w: 10, variant: ParameterVariant::NonInteractiveMultiParty, }; pub(crate) const NI_4P: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(18014398509404161), lwe_q: CiphertextModulus::new_non_native(1 << 16), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(510), lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(12)), rlrg_decomposer_params: ( DecompostionLogBase(17), (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( DecompostionLogBase(4), (DecompositionCount(10), DecompositionCount(9)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), non_interactive_ui_to_s_key_switch_decomposer: Some(( DecompostionLogBase(1), DecompositionCount(50), )), g: 5, w: 10, variant: ParameterVariant::NonInteractiveMultiParty, }; #[cfg(test)] pub(crate) const SP_TEST_BOOL_PARAMS: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(268369921u64), lwe_q: CiphertextModulus::new_non_native(1 << 16), br_q: 1 << 9, rlwe_n: PolynomialSize(1 << 9), lwe_n: LweDimension(100), lwe_decomposer_params: (DecompostionLogBase(4), DecompositionCount(4)), rlrg_decomposer_params: ( DecompostionLogBase(7), (DecompositionCount(4), DecompositionCount(4)), ), rgrg_decomposer_params: None, auto_decomposer_params: (DecompostionLogBase(7), DecompositionCount(4)), non_interactive_ui_to_s_key_switch_decomposer: None, g: 5, w: 5, variant: ParameterVariant::SingleParty, }; #[cfg(test)] mod tests { impl BoolParameters { pub(crate) fn default_rlwe_modop(&self) -> ModularOpsU64> { ModularOpsU64::new(self.rlwe_q) } pub(crate) fn default_rlwe_nttop(&self) -> NttBackendU64 { NttBackendU64::new(&self.rlwe_q, self.rlwe_n.0) } } use crate::{utils::generate_prime, ModInit, ModularOpsU64, Ntt, NttBackendU64, NttInit}; use super::{BoolParameters, CiphertextModulus}; #[test] fn find_prime() { let bits = 60; let ring_size = 1 << 11; let prime = generate_prime(bits, ring_size * 2, 1 << bits).unwrap(); dbg!(prime); } }