diff --git a/examples/interactive_fheuint8.rs b/examples/interactive_fheuint8.rs index 26ea2ea..c50816b 100644 --- a/examples/interactive_fheuint8.rs +++ b/examples/interactive_fheuint8.rs @@ -11,8 +11,8 @@ fn fhe_circuit(fhe_a: &FheUint8, fhe_b: &FheUint8, fhe_c: &FheUint8) -> FheUint8 } fn main() { - set_parameter_set(ParameterSelector::MultiPartyLessThanOrEqualTo16); - let no_of_parties = 8; + set_parameter_set(ParameterSelector::InteractiveLTE2Party); + let no_of_parties = 2; let client_keys = (0..no_of_parties) .into_iter() .map(|_| gen_client_key()) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 332f28f..d3c0721 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,6 +1,6 @@ use num_traits::ToPrimitive; -use crate::Row; +use crate::{utils::log2, Row}; mod modulus_u64; mod power_of_2; @@ -13,6 +13,8 @@ pub trait Modulus { type Element; /// Modulus value if it fits in Element fn q(&self) -> Option; + /// Log2 of `q` + fn log_q(&self) -> usize; /// Modulus value as f64 if it fits in f64 fn q_as_f64(&self) -> Option; /// Is modulus native? @@ -35,7 +37,7 @@ pub trait Modulus { impl Modulus for u64 { type Element = u64; fn is_native(&self) -> bool { - // q of size u64 can never be a naitve modulus + // q that fits in u64 can never be a native modulus false } fn largest_unsigned_value(&self) -> Self::Element { @@ -56,21 +58,22 @@ impl Modulus for u64 { } } fn map_element_from_f64(&self, v: f64) -> Self::Element { - //FIXME (Jay): Before I check whether v is smaller than 0 with `let is_neg = - // o.is_sign_negative() && o != 0.0; I'm ocnfused why didn't I simply check < - // 0.0? let v = v.round(); + let v_u64 = v.abs().to_u64().unwrap(); + assert!(v_u64 <= self.largest_unsigned_value()); if v < 0.0 { - self - v.abs().to_u64().unwrap() + self - v_u64 } else { - v.to_u64().unwrap() + v_u64 } } fn map_element_from_i64(&self, v: i64) -> Self::Element { + let v_u64 = v.abs().to_u64().unwrap(); + assert!(v_u64 <= self.largest_unsigned_value()); if v < 0 { - self - v.abs().to_u64().unwrap() + self - v_u64 } else { - v.to_u64().unwrap() + v_u64 } } fn q(&self) -> Option { @@ -79,6 +82,9 @@ impl Modulus for u64 { fn q_as_f64(&self) -> Option { self.to_f64() } + fn log_q(&self) -> usize { + log2(&self.q().unwrap()) + } } pub trait ModInit { diff --git a/src/bool/evaluator.rs b/src/bool/evaluator.rs index c1197ef..0c100e5 100644 --- a/src/bool/evaluator.rs +++ b/src/bool/evaluator.rs @@ -6,7 +6,7 @@ use std::{ }; use itertools::{izip, Itertools}; -use num_traits::{FromPrimitive, One, PrimInt, ToPrimitive, WrappingAdd, WrappingSub, Zero}; +use num_traits::{FromPrimitive, Num, One, PrimInt, ToPrimitive, WrappingAdd, WrappingSub, Zero}; use rand_distr::uniform::SampleUniform; use crate::{ @@ -18,21 +18,20 @@ use crate::{ non_interactive_ksk_gen, non_interactive_ksk_zero_encryptions_for_other_party_i, public_key_share, }, - ntt::{self, Ntt, NttBackendU64, NttInit}, + ntt::{Ntt, NttInit}, pbs::{pbs, sample_extract, PbsInfo, PbsKey, WithShoupRepr}, random::{ DefaultSecureRng, NewWithSeed, RandomFill, RandomFillGaussianInModulus, RandomFillUniformInModulus, RandomGaussianElementInModulus, }, rgsw::{ - decrypt_rlwe, generate_auto_map, public_key_encrypt_rgsw, rgsw_by_rgsw_inplace, - rgsw_x_rgsw_scratch_rows, rlwe_auto, rlwe_auto_scratch_rows, rlwe_x_rgsw_scratch_rows, - secret_key_encrypt_rgsw, seeded_auto_key_gen, RgswCiphertext, RgswCiphertextMutRef, - RgswCiphertextRef, RuntimeScratchMutRef, + generate_auto_map, public_key_encrypt_rgsw, rgsw_by_rgsw_inplace, rgsw_x_rgsw_scratch_rows, + rlwe_auto_scratch_rows, rlwe_x_rgsw_scratch_rows, secret_key_encrypt_rgsw, + seeded_auto_key_gen, RgswCiphertextMutRef, RgswCiphertextRef, RuntimeScratchMutRef, }, utils::{ encode_x_pow_si_with_emebedding_factor, fill_random_ternary_secret_with_hamming_weight, - generate_prime, mod_exponent, puncture_p_rng, Global, TryConvertFrom1, WithLocal, + mod_exponent, puncture_p_rng, TryConvertFrom1, WithLocal, }, Encoder, Matrix, MatrixEntity, MatrixMut, RowEntity, RowMut, }; @@ -40,10 +39,10 @@ use crate::{ use super::{ keys::{ ClientKey, CommonReferenceSeededCollectivePublicKeyShare, - CommonReferenceSeededMultiPartyServerKeyShare, + CommonReferenceSeededInteractiveMultiPartyServerKeyShare, CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare, InteractiveMultiPartyClientKey, NonInteractiveMultiPartyClientKey, - SeededMultiPartyServerKey, SeededNonInteractiveMultiPartyServerKey, + SeededInteractiveMultiPartyServerKey, SeededNonInteractiveMultiPartyServerKey, SeededSinglePartyServerKey, SinglePartyClientKey, }, parameters::{BoolParameters, CiphertextModulus, DecompositionCount, DoubleDecomposerParams}, @@ -268,13 +267,13 @@ pub(super) trait BoolEncoding { impl BoolEncoding for CiphertextModulus where CiphertextModulus: Modulus, - T: PrimInt, + T: PrimInt + NumInfo, { type Element = T; fn qby4(&self) -> Self::Element { if self.is_native() { - T::one() << (CiphertextModulus::::_bits() - 2) + T::one() << ((T::BITS as usize) - 2) } else { self.q().unwrap() >> 2 } @@ -282,7 +281,7 @@ where /// Q/8 fn true_el(&self) -> Self::Element { if self.is_native() { - T::one() << (CiphertextModulus::::_bits() - 3) + T::one() << ((T::BITS as usize) - 3) } else { self.q().unwrap() >> 3 } @@ -954,7 +953,7 @@ where cr_seed: &MultiPartyCrs<[u8; 32]>, collective_pk: &M, client_key: &K, - ) -> CommonReferenceSeededMultiPartyServerKeyShare< + ) -> CommonReferenceSeededInteractiveMultiPartyServerKeyShare< M, BoolParameters, MultiPartyCrs<[u8; 32]>, @@ -1085,7 +1084,7 @@ where &sk_lwe, ); - CommonReferenceSeededMultiPartyServerKeyShare::new( + CommonReferenceSeededInteractiveMultiPartyServerKeyShare::new( self_leader_rgsws, not_self_leader_rgsws, auto_keys, @@ -1098,12 +1097,12 @@ where pub(super) fn aggregate_multi_party_server_key_shares( &self, - shares: &[CommonReferenceSeededMultiPartyServerKeyShare< + shares: &[CommonReferenceSeededInteractiveMultiPartyServerKeyShare< M, BoolParameters, MultiPartyCrs, >], - ) -> SeededMultiPartyServerKey, BoolParameters> + ) -> SeededInteractiveMultiPartyServerKey, BoolParameters> where S: PartialEq + Clone, M: Clone, @@ -1255,7 +1254,13 @@ where lweq_modop.elwise_add_mut(lwe_ksk.as_mut(), si.lwe_ksk().as_ref()) }); - SeededMultiPartyServerKey::new(rgsw_cts, auto_keys, lwe_ksk, cr_seed.clone(), parameters) + SeededInteractiveMultiPartyServerKey::new( + rgsw_cts, + auto_keys, + lwe_ksk, + cr_seed.clone(), + parameters, + ) } pub(super) fn aggregate_non_interactive_multi_party_key_share( @@ -1264,6 +1269,7 @@ where total_users: usize, key_shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare< M, + BoolParameters, NonInteractiveMultiPartyCrs<[u8; 32]>, >], ) -> SeededNonInteractiveMultiPartyServerKey< @@ -1280,6 +1286,8 @@ where &ParameterVariant::NonInteractiveMultiParty ); + // TODO: Check parameters are equivalent! + let total_users = key_shares.len(); let key_shares = (0..total_users) .map(|user_id| { @@ -1662,6 +1670,7 @@ where client_key: &K, ) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare< M, + BoolParameters, NonInteractiveMultiPartyCrs<[u8; 32]>, > { assert_eq!( @@ -2004,6 +2013,7 @@ where total_users, self.parameters().lwe_n().0, cr_seed.clone(), + self.parameters().clone(), ) } diff --git a/src/bool/keys.rs b/src/bool/keys.rs index ab2afde..740fd38 100644 --- a/src/bool/keys.rs +++ b/src/bool/keys.rs @@ -1,17 +1,14 @@ -use std::{collections::HashMap, hash::Hash, marker::PhantomData}; +use std::{collections::HashMap, marker::PhantomData}; use crate::{ backend::{ModInit, VectorOps}, pbs::WithShoupRepr, random::{NewWithSeed, RandomFillUniformInModulus}, - utils::{ToShoup, WithLocal}, - Decryptor, Encryptor, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor, RowEntity, RowMut, + utils::ToShoup, + Matrix, MatrixEntity, MatrixMut, RowEntity, RowMut, SizeInBitsWithLogModulus, }; -use super::{ - evaluator::BoolEvaluator, - parameters::{BoolParameters, CiphertextModulus}, -}; +use super::parameters::{BoolParameters, CiphertextModulus}; pub(crate) trait SinglePartyClientKey { type Element; @@ -48,7 +45,7 @@ pub(crate) trait NonInteractiveMultiPartyClientKey { /// interactive/non-interactive multi-party /// /// Puncture 3 -> Seed of RLWE secret used as `u` in -/// interactive/non-interactive multi-party. +/// non-interactive multi-party. #[derive(Clone)] pub struct ClientKey { seed: S, @@ -143,8 +140,6 @@ pub struct PublicKey { } pub(super) mod impl_pk { - use crate::evaluator::MultiPartyCrs; - use super::*; impl PublicKey { @@ -296,8 +291,11 @@ mod impl_seeded_pk { /// CRS seeded collective public key share pub struct CommonReferenceSeededCollectivePublicKeyShare { + /// Public key share polynomial share: Ro, + /// Common reference seed cr_seed: S, + /// Parameters parameters: P, } impl CommonReferenceSeededCollectivePublicKeyShare { @@ -310,21 +308,36 @@ impl CommonReferenceSeededCollectivePublicKeyShare { } } -/// CRS seeded Multi-party server key share -pub struct CommonReferenceSeededMultiPartyServerKeyShare { +/// Common reference seed seeded interactive multi-party server key share +pub struct CommonReferenceSeededInteractiveMultiPartyServerKeyShare { + /// Public key encrypted RGSW(m = X^{s[i]}) ciphertexts for LWE secret + /// indices for which `Self` is the leader. Note that when `Self` is + /// leader RGSW ciphertext is encrypted using RLWE x RGSW decomposer self_leader_rgsws: Vec, + /// Public key encrypted RGSW(m = X^{s[i]}) ciphertext for LWE secret + /// indices for which `Self` is `not` the leader. Note that when `Self` + /// is not the leader RGSW ciphertext is encrypted using RGSW1 + /// decomposer for RGSW0 x RGSW1 not_self_leader_rgsws: Vec, - /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding - /// to -g is at 0 + /// Auto key shares for auto elements [g^{-1}, g, g^2, .., g^{w}] where `w` + /// is the window size parameter. Share corresponding to auto element g^{-1} + /// is stored at key `0` and share corresponding to auto element g^{k} is + /// stored at key `k`. auto_keys: HashMap, + /// LWE key switching key share to key switching ciphertext LWE_{q, s}(m) to + /// LWE_{q, z}(m) where q is LWE ciphertext modulus, `s` is the ideal RLWE + /// secret with dimension N, and `z` is the ideal LWE secret of dimension n. lwe_ksk: M::R, /// Common reference seed cr_seed: S, parameters: P, + /// User id assigned by the server. + /// + /// User id must be unique and a number in range [0, total_users) user_id: usize, } -impl CommonReferenceSeededMultiPartyServerKeyShare { +impl CommonReferenceSeededInteractiveMultiPartyServerKeyShare { pub(super) fn new( self_leader_rgsws: Vec, not_self_leader_rgsws: Vec, @@ -334,7 +347,7 @@ impl CommonReferenceSeededMultiPartyServerKeyShare { parameters: P, user_id: usize, ) -> Self { - CommonReferenceSeededMultiPartyServerKeyShare { + CommonReferenceSeededInteractiveMultiPartyServerKeyShare { self_leader_rgsws, not_self_leader_rgsws, auto_keys, @@ -374,18 +387,26 @@ impl CommonReferenceSeededMultiPartyServerKeyShare { } } -/// CRS seeded MultiParty server key -pub struct SeededMultiPartyServerKey { +/// Common reference seeded interactive multi-party server key +pub struct SeededInteractiveMultiPartyServerKey { + /// RGSW ciphertexts RGSW(X^{s[i]}) encrypted under ideal RLWE secret key + /// where `s` is ideal LWE secret key for each LWE secret dimension. rgsw_cts: Vec, - /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding - /// to -g is at 0 + /// Seeded auto keys under ideal RLWE secret for RLWE automorphisms with + /// auto elements [g^-1, g, g^2,..., g^{w}]. Auto key corresponidng to + /// auto element g^{-1} is stored at key `0` and key corresponding to auto + /// element g^{k} is stored at key `k` auto_keys: HashMap, + /// Seeded LWE key switching key under ideal LWE secret to switch LWE_{q, + /// s}(m) to LWE_{q, z}(m) where s is ideal RLWE secret and z is ideal LWE + /// secret. lwe_ksk: M::R, + /// Common reference seed cr_seed: S, parameters: P, } -impl SeededMultiPartyServerKey { +impl SeededInteractiveMultiPartyServerKey { pub(super) fn new( rgsw_cts: Vec, auto_keys: HashMap, @@ -393,7 +414,7 @@ impl SeededMultiPartyServerKey { cr_seed: S, parameters: P, ) -> Self { - SeededMultiPartyServerKey { + SeededInteractiveMultiPartyServerKey { rgsw_cts, auto_keys, lwe_ksk, @@ -461,12 +482,12 @@ impl SeededSinglePartyServerKey, /// Server key in evaluation domain pub(crate) struct ServerKeyEvaluationDomain { - /// Rgsw cts of LWE secret elements + /// RGSW ciphertext RGSW(X^{s[i]}) for each LWE index in evaluation domain rgsw_cts: Vec, - /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding - /// to -g is at 0 + /// Auto keys for all auto elements [g^{-1}, g, g^2,..., g^w] in evaluation + /// domain galois_keys: HashMap, - /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret + /// LWE key switching key to key switch LWE_{q, s}(m) to LWE_{q, z}(m) lwe_ksk: M, parameters: P, _phanton: PhantomData<(R, N)>, @@ -627,8 +648,13 @@ pub(super) mod impl_server_key_eval_domain { Rng: NewWithSeed, N: NttInit> + Ntt, > - From<&SeededMultiPartyServerKey, BoolParameters>> - for ServerKeyEvaluationDomain, Rng, N> + From< + &SeededInteractiveMultiPartyServerKey< + M, + MultiPartyCrs, + BoolParameters, + >, + > for ServerKeyEvaluationDomain, Rng, N> where ::R: RowMut, Rng::Seed: Copy + Default, @@ -637,7 +663,7 @@ pub(super) mod impl_server_key_eval_domain { M::MatElement: Copy, { fn from( - value: &SeededMultiPartyServerKey< + value: &SeededInteractiveMultiPartyServerKey< M, MultiPartyCrs, BoolParameters, @@ -767,15 +793,21 @@ pub(super) mod impl_server_key_eval_domain { } } +/// Non-interactive multi-party server key in evaluation domain. +/// +/// The key is derived from Seeded non-interactive mmulti-party server key +/// `SeededNonInteractiveMultiPartyServerKey`. pub(crate) struct NonInteractiveServerKeyEvaluationDomain { - /// RGSW ciphertexts ideal lwe secret key elements under ideal rlwe secret + /// RGSW ciphertexts RGSW(X^{s[i]}) under ideal RLWE secret key in + /// evaluation domain rgsw_cts: Vec, - /// Automorphism keys under ideal rlwe secret + /// Auto keys for all auto elements [g^{-1}, g, g^2, g^w] in evaluation + /// domain auto_keys: HashMap, - /// LWE key switching key from Q -> Q_{ks} + /// LWE key switching key to key switch LWE_{q, s}(m) to LWE_{q, z}(m) lwe_ksk: M, - /// Key switching key from user j to ideal secret key s. User j's ksk is at - /// j'th element + /// Key switching key from user j's secret u_j to ideal RLWE secret key `s` + /// in evaluation domain. User j's key switching key is at j'th index. ui_to_s_ksks: Vec, parameters: P, _phanton: PhantomData<(R, N)>, @@ -967,14 +999,22 @@ pub(super) mod impl_non_interactive_server_key_eval_domain { } } +/// Seeded non-interactive multi-party server key. +/// +/// Given common reference seeded non-interactive multi-party key shares of each +/// users with unique user-ids, seeded non-interactive can be generated using +/// `BoolEvaluator::aggregate_non_interactive_multi_party_key_share` pub struct SeededNonInteractiveMultiPartyServerKey { - /// u_i to s key switching keys in random order. Key switchin key for user j - /// is stored at j^th index + /// Key switching key from user j's secret u_j to ideal RLWE secret key `s`. + /// User j's key switching key is at j'th index. ui_to_s_ksks: Vec, - /// RGSW ciphertets + /// RGSW ciphertexts RGSW(X^{s[i]}) under ideal RLWE secret key rgsw_cts: Vec, + /// Auto keys for all auto elements [g^{-1}, g, g^2, g^w] auto_keys: HashMap, + /// LWE key switching key to key switch LWE_{q, s}(m) to LWE_{q, z}(m) lwe_ksk: M::R, + /// Common reference seed cr_seed: S, parameters: P, } @@ -982,7 +1022,6 @@ pub struct SeededNonInteractiveMultiPartyServerKey { impl SeededNonInteractiveMultiPartyServerKey { pub(super) fn new( ui_to_s_ksks: Vec, - rgsw_cts: Vec, auto_keys: HashMap, lwe_ksk: M::R, @@ -1001,15 +1040,13 @@ impl SeededNonInteractiveMultiPartyServerKey { } } +/// This key is equivalent to NonInteractiveServerKeyEvaluationDomain with the +/// addition that each polynomial in evaluation domain has a corresponding shoup +/// representation suitable for shoup multiplication. pub(crate) struct ShoupNonInteractiveServerKeyEvaluationDomain { - /// RGSW ciphertexts ideal lwe secret key elements under ideal rlwe secret rgsw_cts: Vec>, - /// Automorphism keys under ideal rlwe secret auto_keys: HashMap>, - /// LWE key switching key from Q -> Q_{ks} lwe_ksk: M, - /// Key switching key from user j to ideal secret key s. User j's ksk is at - /// j'th element ui_to_s_ksks: Vec>, } @@ -1018,7 +1055,7 @@ mod impl_shoup_non_interactive_server_key_eval_domain { use num_traits::{FromPrimitive, PrimInt, ToPrimitive}; use super::*; - use crate::{backend::Modulus, pbs::PbsKey}; + use crate::{backend::Modulus, decomposer::NumInfo, pbs::PbsKey}; impl ShoupNonInteractiveServerKeyEvaluationDomain { pub(in super::super) fn ui_to_s_ksk(&self, user_id: usize) -> &NormalAndShoup { @@ -1030,7 +1067,7 @@ mod impl_shoup_non_interactive_server_key_eval_domain { From, R, N>> for ShoupNonInteractiveServerKeyEvaluationDomain where - M::MatElement: FromPrimitive + ToPrimitive + PrimInt, + M::MatElement: FromPrimitive + ToPrimitive + PrimInt + NumInfo, { fn from( value: NonInteractiveServerKeyEvaluationDomain, R, N>, @@ -1114,14 +1151,12 @@ mod impl_shoup_non_interactive_server_key_eval_domain { } } -/// Server key in evaluation domain with Shoup representations +/// This is equivalent to ServerKeyEvaluationDomain with the addition that each +/// polynomial in evaluation domain has corresponding shoup representation +/// suitable for shoup multiplication. pub(crate) struct ShoupServerKeyEvaluationDomain { - /// Rgsw cts of LWE secret elements rgsw_cts: Vec>, - /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding - /// to -g is at 0 galois_keys: HashMap>, - /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret lwe_ksk: M, } @@ -1129,7 +1164,7 @@ mod shoup_server_key_eval_domain { use itertools::{izip, Itertools}; use num_traits::{FromPrimitive, PrimInt}; - use crate::{backend::Modulus, pbs::PbsKey}; + use crate::{backend::Modulus, decomposer::NumInfo, pbs::PbsKey}; use super::*; @@ -1138,7 +1173,7 @@ mod shoup_server_key_eval_domain { for ShoupServerKeyEvaluationDomain where ::R: RowMut, - M::MatElement: PrimInt + FromPrimitive, + M::MatElement: PrimInt + FromPrimitive + NumInfo, { fn from(value: ServerKeyEvaluationDomain, R, N>) -> Self { let q = value.parameters.rlwe_q().q().unwrap(); @@ -1180,27 +1215,56 @@ mod shoup_server_key_eval_domain { } } -pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare { - /// Non-interactive RGSW ciphertexts for secret indices for which user is - /// the leader +pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare { + /// Non-interactive RGSW ciphertexts for LWE secret indices for which user + /// is the leader self_leader_ni_rgsw_cts: Vec, - /// Non-interactive RGSW ciphertexts for secret indices for which user is - /// not the leader + /// Non-interactive RGSW ciphertexts for LWE secret indices for which user + /// is not the leader not_self_leader_ni_rgsw_cts: Vec, - /// Zero encryptions for RGSW ciphertexts for all indicces + /// Zero encryptions for RGSW ciphertexts for all indices ni_rgsw_zero_encs: Vec, - /// (ak*si + e + \beta ui, ak*si + e) + /// Key switching key from u_j to s where u_j is user j's RLWE secret `u` + /// and `s` is ideal RLWE secret. Note that in server key share the key + /// switching key is encrypted under user j's RLWE secret `s_j`. It is + /// then switched to ideal RLWE secret after adding zero encryptions + /// generated using same `a_k`s from other users. + /// + /// That is the key share has the following key switching key: + /// (a_k*s_j + e + \beta u_j, a_k*s_j + e) ui_to_s_ksk: M, + /// Zero encryptions to switch user l's key switching key u_l to s from + /// user l's RLWE secret s_l to ideal RLWE secret `s`. + /// + /// If there are P total parties then zero encryption sets are generated for + /// each party l \in [0, P) and l != j where j self's user_id. + /// + /// Zero encryption set for user `l` is stored at index l is l < j otherwise + /// it is stored at index l - 1, where j is self's user_id ksk_zero_encs_for_others: Vec, + /// RLWE auto key shares for auto elements [g^{-1}, g, g^2, g^{w}] where `w` + /// is the window size. Auto key share corresponding to auto element g^{-1} + /// is stored at key 0 and key share corresponding to auto element g^{k} is + /// stored at key `k` auto_keys_share: HashMap, + /// LWE key switching key share to key switching LWE_{q, s}(m) to LWE_{q, + /// z}(m) lwe_ksk_share: M::R, - user_index: usize, + /// User's id. + /// + /// If there are P total parties, then user id must be inque and in range + /// [0, P) + user_id: usize, + /// Total users participating in multi-party compute total_users: usize, + /// LWE dimension lwe_n: usize, + /// Common reference seed cr_seed: S, + parameters: P, } mod impl_common_ref_non_interactive_multi_party_server_share { @@ -1208,7 +1272,7 @@ mod impl_common_ref_non_interactive_multi_party_server_share { use super::*; - impl CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare { + impl CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare { pub(in super::super) fn new( self_leader_ni_rgsw_cts: Vec, not_self_leader_ni_rgsw_cts: Vec, @@ -1221,6 +1285,7 @@ mod impl_common_ref_non_interactive_multi_party_server_share { total_users: usize, lwe_n: usize, cr_seed: S, + parameters: P, ) -> Self { Self { self_leader_ni_rgsw_cts, @@ -1230,10 +1295,11 @@ mod impl_common_ref_non_interactive_multi_party_server_share { ksk_zero_encs_for_others, auto_keys_share, lwe_ksk_share, - user_index, + user_id: user_index, total_users, lwe_n, cr_seed, + parameters, } } @@ -1242,7 +1308,7 @@ mod impl_common_ref_non_interactive_multi_party_server_share { lwe_index: usize, ) -> &M { let self_segment = interactive_mult_party_user_id_lwe_segment( - self.user_index, + self.user_id, self.total_users, self.lwe_n, ); @@ -1255,7 +1321,7 @@ mod impl_common_ref_non_interactive_multi_party_server_share { lwe_index: usize, ) -> &M { let self_segment = interactive_mult_party_user_id_lwe_segment( - self.user_index, + self.user_id, self.total_users, self.lwe_n, ); @@ -1281,7 +1347,7 @@ mod impl_common_ref_non_interactive_multi_party_server_share { } pub(in super::super) fn user_index(&self) -> usize { - self.user_index + self.user_id } pub(in super::super) fn auto_keys_share(&self) -> &HashMap { @@ -1293,8 +1359,8 @@ mod impl_common_ref_non_interactive_multi_party_server_share { } pub(in super::super) fn ui_to_s_ksk_zero_encs_for_user_i(&self, user_i: usize) -> &M { - assert!(user_i != self.user_index); - if user_i < self.user_index { + assert!(user_i != self.user_id); + if user_i < self.user_id { &self.ksk_zero_encs_for_others[user_i] } else { &self.ksk_zero_encs_for_others[user_i - 1] @@ -1303,7 +1369,11 @@ mod impl_common_ref_non_interactive_multi_party_server_share { } } -/// Stores normal and shoup representation of Matrix elements (Normal, Shoup) +/// Stores both normal and shoup representation of elements in the container +/// (for ex, a matrix). +/// +/// To access normal representation borrow self as a `self.as_ref()`. To access +/// shoup representation call `self.shoup_repr()` pub(crate) struct NormalAndShoup(M, M); impl NormalAndShoup { @@ -1326,6 +1396,83 @@ impl WithShoupRepr for NormalAndShoup { } } +#[cfg(test)] +pub(crate) mod key_size { + use num_traits::{FromPrimitive, PrimInt}; + + use crate::{backend::Modulus, decomposer::NumInfo}; + + use super::*; + + /// Size of the Key in Bits + pub(crate) trait KeySize { + /// Returns size of the key in bits + fn size(&self) -> usize; + } + + impl KeySize + for CommonReferenceSeededInteractiveMultiPartyServerKeyShare, S> + where + M: SizeInBitsWithLogModulus, + M::R: SizeInBitsWithLogModulus, + El: PrimInt + NumInfo + FromPrimitive, + { + fn size(&self) -> usize { + let mut total = 0; + + let log_rlweq = self.parameters().rlwe_q().log_q(); + self.self_leader_rgsws + .iter() + .for_each(|v| total += v.size(log_rlweq)); + self.not_self_leader_rgsws + .iter() + .for_each(|v| total += v.size(log_rlweq)); + self.auto_keys + .values() + .for_each(|v| total += v.size(log_rlweq)); + + let log_lweq = self.parameters().lwe_q().log_q(); + total += self.lwe_ksk.size(log_lweq); + total + } + } + + impl KeySize + for CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare, S> + where + M: SizeInBitsWithLogModulus, + M::R: SizeInBitsWithLogModulus, + El: PrimInt + NumInfo + FromPrimitive, + { + fn size(&self) -> usize { + let mut total = 0; + + let log_rlweq = self.parameters.rlwe_q().log_q(); + self.self_leader_ni_rgsw_cts + .iter() + .for_each(|v| total += v.size(log_rlweq)); + self.not_self_leader_ni_rgsw_cts + .iter() + .for_each(|v| total += v.size(log_rlweq)); + self.ni_rgsw_zero_encs + .iter() + .for_each(|v| total += v.size(log_rlweq)); + total += self.ui_to_s_ksk.size(log_rlweq); + self.ksk_zero_encs_for_others + .iter() + .for_each(|v| total += v.size(log_rlweq)); + self.auto_keys_share + .values() + .for_each(|v| total += v.size(log_rlweq)); + + let log_lweq = self.parameters.lwe_q().log_q(); + total += self.lwe_ksk_share.size(log_lweq); + + total + } + } +} + pub(super) mod tests { use itertools::izip; use num_traits::{FromPrimitive, PrimInt, ToPrimitive, Zero}; @@ -1333,6 +1480,7 @@ pub(super) mod tests { use crate::{ backend::{GetModulus, Modulus}, bool::ClientKey, + decomposer::NumInfo, lwe::decrypt_lwe, parameters::CiphertextModulus, utils::TryConvertFrom1, @@ -1376,7 +1524,7 @@ pub(super) mod tests { ) -> f64 where R: TryConvertFrom1<[S], CiphertextModulus>, - R::Element: Zero + FromPrimitive + PrimInt, + R::Element: Zero + FromPrimitive + PrimInt + NumInfo, { let noisy_m = decrypt_lwe(lwe_ct, &sk, modop); let noise = modop.sub(&m_expected, &noisy_m); diff --git a/src/bool/mod.rs b/src/bool/mod.rs index c27ef13..21ed627 100644 --- a/src/bool/mod.rs +++ b/src/bool/mod.rs @@ -20,8 +20,7 @@ pub use mp_api::*; pub type ClientKey = keys::ClientKey<[u8; 32], u64>; pub enum ParameterSelector { - HighCommunicationButFast2Party, - MultiPartyLessThanOrEqualTo16, + InteractiveLTE2Party, NonInteractiveLTE2Party, NonInteractiveLTE4Party, } diff --git a/src/bool/mp_api.rs b/src/bool/mp_api.rs index db2055f..8e51fce 100644 --- a/src/bool/mp_api.rs +++ b/src/bool/mp_api.rs @@ -27,13 +27,10 @@ static MULTI_PARTY_CRS: OnceLock> = OnceLock::new(); pub fn set_parameter_set(select: ParameterSelector) { match select { - ParameterSelector::MultiPartyLessThanOrEqualTo16 => { - BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(SMALL_MP_BOOL_PARAMS))); - } - ParameterSelector::HighCommunicationButFast2Party => { - BOOL_EVALUATOR - .with_borrow_mut(|v| *v = Some(BoolEvaluator::new(OPTIMISED_SMALL_MP_BOOL_PARAMS))); + ParameterSelector::InteractiveLTE2Party => { + BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_2P))); } + _ => { panic!("Paramerters not supported") } @@ -65,7 +62,7 @@ pub fn gen_mp_keys_phase2( user_id: usize, total_users: usize, pk: &PublicKey>, R, ModOp>, -) -> CommonReferenceSeededMultiPartyServerKeyShare< +) -> CommonReferenceSeededInteractiveMultiPartyServerKeyShare< Vec>, BoolParameters, MultiPartyCrs<[u8; 32]>, @@ -93,17 +90,18 @@ pub fn aggregate_public_key_shares( } pub fn aggregate_server_key_shares( - shares: &[CommonReferenceSeededMultiPartyServerKeyShare< + shares: &[CommonReferenceSeededInteractiveMultiPartyServerKeyShare< Vec>, BoolParameters, MultiPartyCrs<[u8; 32]>, >], -) -> SeededMultiPartyServerKey>, MultiPartyCrs<[u8; 32]>, BoolParameters> { +) -> SeededInteractiveMultiPartyServerKey>, MultiPartyCrs<[u8; 32]>, BoolParameters> +{ BoolEvaluator::with_local(|e| e.aggregate_multi_party_server_key_shares(shares)) } impl - SeededMultiPartyServerKey< + SeededInteractiveMultiPartyServerKey< Vec>, MultiPartyCrs<::Seed>, BoolParameters, @@ -256,7 +254,7 @@ mod tests { #[test] fn multi_party_bool_gates() { - set_parameter_set(ParameterSelector::HighCommunicationButFast2Party); + set_parameter_set(ParameterSelector::InteractiveLTE2Party); let mut seed = [0u8; 32]; thread_rng().fill_bytes(&mut seed); set_mp_seed(seed); @@ -349,7 +347,7 @@ mod tests { use crate::{ backend::ModulusPowerOf2, evaluator::BoolEncoding, pbs::PbsInfo, - rgsw::secret_key_encrypt_rlwe, utils::WithLocal, Decryptor, ModularOpsU64, + rgsw::seeded_secret_key_encrypt_rlwe, utils::WithLocal, Decryptor, ModularOpsU64, NttBackendU64, SampleExtractor, }; @@ -441,8 +439,7 @@ mod tests { // encrypt message let mut rlwe_out = vec![0u64; parameters.rlwe_n().0]; - - secret_key_encrypt_rlwe( + seeded_secret_key_encrypt_rlwe( &message, &mut rlwe_out, &sk_u, diff --git a/src/bool/ni_mp_api.rs b/src/bool/ni_mp_api.rs index 31891d3..1d70337 100644 --- a/src/bool/ni_mp_api.rs +++ b/src/bool/ni_mp_api.rs @@ -79,6 +79,7 @@ pub fn gen_server_key_share( client_key: &ClientKey, ) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare< Vec>, + BoolParameters, NonInteractiveMultiPartyCrs<[u8; 32]>, > { BoolEvaluator::with_local(|e| { @@ -90,6 +91,7 @@ pub fn gen_server_key_share( pub fn aggregate_server_key_shares( shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare< Vec>, + BoolParameters, NonInteractiveMultiPartyCrs<[u8; 32]>, >], ) -> SeededNonInteractiveMultiPartyServerKey< diff --git a/src/bool/parameters.rs b/src/bool/parameters.rs index d174709..fda4168 100644 --- a/src/bool/parameters.rs +++ b/src/bool/parameters.rs @@ -1,8 +1,10 @@ -use std::ops::Deref; - use num_traits::{ConstZero, FromPrimitive, PrimInt}; -use crate::{backend::Modulus, decomposer::Decomposer}; +use crate::{ + backend::Modulus, + decomposer::{Decomposer, NumInfo}, + utils::log2, +}; pub(crate) trait DoubleDecomposerCount { type Count; @@ -84,27 +86,69 @@ pub(crate) enum ParameterVariant { } #[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), - /// RLWE x RGSW decomposition count for (part A, part B) + /// 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), - /// RGSW x RGSW decomposition count for (part A, part B) + /// 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, } @@ -347,10 +391,10 @@ impl CiphertextModulus { impl CiphertextModulus where - T: PrimInt, + T: PrimInt + NumInfo, { - pub(crate) fn _bits() -> usize { - std::mem::size_of::() as usize * 8 + fn _bits() -> usize { + T::BITS as usize } fn _native(&self) -> bool { @@ -376,7 +420,7 @@ where impl Modulus for CiphertextModulus where - T: PrimInt + FromPrimitive, + T: PrimInt + FromPrimitive + NumInfo, { type Element = T; fn is_native(&self) -> bool { @@ -403,6 +447,7 @@ where } 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() @@ -414,18 +459,24 @@ where 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() - T::from_f64(v.abs()).unwrap() + T::one() + self.largest_unsigned_value() - v_el + T::one() } else { - T::from_f64(v.abs()).unwrap() + 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() - T::from_i64(v.abs()).unwrap() + T::one() + self.largest_unsigned_value() - v_el + T::one() } else { - T::from_i64(v.abs()).unwrap() + v_el } } @@ -440,6 +491,14 @@ where 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:: { @@ -491,7 +550,7 @@ pub(crate) const I_2P: BoolParameters = BoolParameters:: { lwe_q: CiphertextModulus::new_non_native(1 << 15), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), - lwe_n: LweDimension(500), + lwe_n: LweDimension(480), lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(11)), rlrg_decomposer_params: ( DecompostionLogBase(16), diff --git a/src/bool/print_noise.rs b/src/bool/print_noise.rs index d5b0d29..0f3ec18 100644 --- a/src/bool/print_noise.rs +++ b/src/bool/print_noise.rs @@ -6,7 +6,7 @@ use rand_distr::uniform::SampleUniform; use crate::{ backend::{GetModulus, Modulus}, - decomposer::RlweDecomposer, + decomposer::{Decomposer, NumInfo, RlweDecomposer}, lwe::{decrypt_lwe, lwe_key_switch}, parameters::{BoolParameters, CiphertextModulus}, random::{DefaultSecureRng, RandomFillUniformInModulus}, @@ -15,8 +15,8 @@ use crate::{ RuntimeScratchMutRef, }, utils::{encode_x_pow_si_with_emebedding_factor, tests::Stats, TryConvertFrom1}, - ArithmeticOps, ClientKey, Decomposer, MatrixEntity, MatrixMut, ModInit, Ntt, NttInit, - RowEntity, RowMut, VectorOps, + ArithmeticOps, ClientKey, MatrixEntity, MatrixMut, ModInit, Ntt, NttInit, RowEntity, RowMut, + VectorOps, }; use super::keys::tests::{ideal_sk_lwe, ideal_sk_rlwe}; @@ -84,7 +84,7 @@ fn collect_server_key_stats< ) -> ServerKeyStats where M::R: RowMut + RowEntity + TryConvertFrom1<[i32], CiphertextModulus> + Clone, - M::MatElement: Copy + PrimInt + FromPrimitive + SampleUniform + Zero + Debug, + M::MatElement: Copy + PrimInt + FromPrimitive + SampleUniform + Zero + Debug + NumInfo, { let ideal_sk_rlwe = ideal_sk_rlwe(client_keys); let ideal_sk_lwe = ideal_sk_lwe(client_keys); @@ -370,27 +370,30 @@ mod tests { fn qwerty() { use crate::{ aggregate_public_key_shares, aggregate_server_key_shares, - bool::keys::ServerKeyEvaluationDomain, + bool::keys::{key_size::KeySize, ServerKeyEvaluationDomain}, evaluator::MultiPartyCrs, gen_client_key, gen_mp_keys_phase1, gen_mp_keys_phase2, - parameters::{BoolParameters, CiphertextModulus}, + parameters::CiphertextModulus, random::DefaultSecureRng, set_mp_seed, set_parameter_set, utils::WithLocal, BoolEvaluator, DefaultDecomposer, ModularOpsU64, Ntt, NttBackendU64, }; - set_parameter_set(crate::ParameterSelector::HighCommunicationButFast2Party); + set_parameter_set(crate::ParameterSelector::InteractiveLTE2Party); set_mp_seed(MultiPartyCrs::random().seed); let parties = 2; let cks = (0..parties).map(|_| gen_client_key()).collect_vec(); let pk_shares = cks.iter().map(|k| gen_mp_keys_phase1(k)).collect_vec(); + let pk = aggregate_public_key_shares(&pk_shares); let server_key_shares = cks .iter() .enumerate() .map(|(index, k)| gen_mp_keys_phase2(k, index, parties, &pk)) .collect_vec(); + + println!("Size: {}", server_key_shares[0].size()); let seeded_server_key = aggregate_server_key_shares(&server_key_shares); let server_key_eval = ServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from( @@ -428,11 +431,16 @@ mod tests { #[cfg(feature = "non_interactive_mp")] fn querty2() { use crate::{ - aggregate_server_key_shares, bool::keys::NonInteractiveServerKeyEvaluationDomain, - evaluator::NonInteractiveMultiPartyCrs, gen_client_key, gen_server_key_share, - parameters::CiphertextModulus, random::DefaultSecureRng, set_common_reference_seed, - set_parameter_set, utils::WithLocal, BoolEvaluator, DefaultDecomposer, ModularOpsU64, - NttBackendU64, + aggregate_server_key_shares, + bool::keys::{key_size::KeySize, NonInteractiveServerKeyEvaluationDomain}, + decomposer::DefaultDecomposer, + evaluator::NonInteractiveMultiPartyCrs, + gen_client_key, gen_server_key_share, + parameters::CiphertextModulus, + random::DefaultSecureRng, + set_common_reference_seed, set_parameter_set, + utils::WithLocal, + BoolEvaluator, ModularOpsU64, NttBackendU64, }; set_parameter_set(crate::ParameterSelector::NonInteractiveLTE2Party); @@ -444,6 +452,7 @@ mod tests { .enumerate() .map(|(user_id, k)| gen_server_key_share(user_id, parties, k)) .collect_vec(); + println!("Size: {}", server_key_shares[0].size()); let server_key = aggregate_server_key_shares(&server_key_shares); let server_key_eval = diff --git a/src/decomposer.rs b/src/decomposer.rs index f315c93..4ffcf4a 100644 --- a/src/decomposer.rs +++ b/src/decomposer.rs @@ -7,6 +7,7 @@ use crate::{ parameters::{ DecompositionCount, DecompostionLogBase, DoubleDecomposerParams, SingleDecomposerParams, }, + utils::log2, }; fn gadget_vector(logq: usize, logb: usize, d: usize) -> Vec { @@ -156,12 +157,7 @@ impl< fn new(q: T, logb: usize, d: usize) -> DefaultDecomposer { // if q is power of 2, then `BITS - leading_zeros` outputs logq + 1. - let logq = if q & (q - T::one()) == T::zero() { - (T::BITS - q.leading_zeros() - 1) as usize - } else { - (T::BITS - q.leading_zeros()) as usize - }; - + let logq = log2(&q); assert!( logq >= (logb * d), "Decomposer wants logq >= logb*d but got logq={logq}, logb={logb}, d={d}" diff --git a/src/lib.rs b/src/lib.rs index 955be3b..d8cc1ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,10 +24,11 @@ pub use backend::{ // gen_mp_keys_phase1, gen_mp_keys_phase2, set_mp_seed, set_parameter_set, // ParameterSelector, }; pub use bool::*; -pub use decomposer::{Decomposer, DecomposerIter, DefaultDecomposer}; pub use ntt::{Ntt, NttBackendU64, NttInit}; pub use shortint::FheUint8; +pub use decomposer::{Decomposer, DecomposerIter, DefaultDecomposer}; + pub trait Matrix: AsRef<[Self::R]> { type MatElement; type R: Row; @@ -193,3 +194,22 @@ pub trait SampleExtractor { trait Encoder { fn encode(&self, v: F) -> T; } + +trait SizeInBitsWithLogModulus { + /// Returns size of `Self` containing several elements mod Q where + /// 2^{log_modulus-1} < Q <= `2^log_modulus` + fn size(&self, log_modulus: usize) -> usize; +} +impl SizeInBitsWithLogModulus for Vec> { + fn size(&self, log_modulus: usize) -> usize { + let mut total = 0; + self.iter().for_each(|r| total += log_modulus * r.len()); + total + } +} + +impl SizeInBitsWithLogModulus for Vec { + fn size(&self, log_modulus: usize) -> usize { + self.len() * log_modulus + } +} diff --git a/src/utils.rs b/src/utils.rs index a8f8f04..d81b14e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,6 +5,7 @@ use num_traits::{One, PrimInt, Signed}; use crate::{ backend::Modulus, + decomposer::NumInfo, random::{RandomElementInModulus, RandomFill}, Matrix, RowEntity, RowMut, }; @@ -226,6 +227,15 @@ pub(crate) fn puncture_p_rng>( return out; } +pub(crate) fn log2(v: &T) -> usize { + if (*v & (*v - T::one())) == T::zero() { + // value is power of 2 + (T::BITS - v.leading_zeros() - 1) as usize + } else { + (T::BITS - v.leading_zeros()) as usize + } +} + pub trait TryConvertFrom1 { fn try_convert_from(value: &T, parameters: &P) -> Self; }