Browse Source

add KeySize trait (in test cfg) and describe fields in keys

par-agg-key-shares
Janmajaya Mall 9 months ago
parent
commit
978c0e2f7d
12 changed files with 402 additions and 146 deletions
  1. +2
    -2
      examples/interactive_fheuint8.rs
  2. +15
    -9
      src/backend/mod.rs
  3. +27
    -17
      src/bool/evaluator.rs
  4. +217
    -69
      src/bool/keys.rs
  5. +1
    -2
      src/bool/mod.rs
  6. +11
    -14
      src/bool/mp_api.rs
  7. +2
    -0
      src/bool/ni_mp_api.rs
  8. +73
    -14
      src/bool/parameters.rs
  9. +21
    -12
      src/bool/print_noise.rs
  10. +2
    -6
      src/decomposer.rs
  11. +21
    -1
      src/lib.rs
  12. +10
    -0
      src/utils.rs

+ 2
- 2
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())

+ 15
- 9
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<Self::Element>;
/// Log2 of `q`
fn log_q(&self) -> usize;
/// Modulus value as f64 if it fits in f64
fn q_as_f64(&self) -> Option<f64>;
/// 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<Self::Element> {
@ -79,6 +82,9 @@ impl Modulus for u64 {
fn q_as_f64(&self) -> Option<f64> {
self.to_f64()
}
fn log_q(&self) -> usize {
log2(&self.q().unwrap())
}
}
pub trait ModInit {

+ 27
- 17
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<T> BoolEncoding for CiphertextModulus<T>
where
CiphertextModulus<T>: Modulus<Element = T>,
T: PrimInt,
T: PrimInt + NumInfo,
{
type Element = T;
fn qby4(&self) -> Self::Element {
if self.is_native() {
T::one() << (CiphertextModulus::<T>::_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::<T>::_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<M::MatElement>,
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<S>(
&self,
shares: &[CommonReferenceSeededMultiPartyServerKeyShare<
shares: &[CommonReferenceSeededInteractiveMultiPartyServerKeyShare<
M,
BoolParameters<M::MatElement>,
MultiPartyCrs<S>,
>],
) -> SeededMultiPartyServerKey<M, MultiPartyCrs<S>, BoolParameters<M::MatElement>>
) -> SeededInteractiveMultiPartyServerKey<M, MultiPartyCrs<S>, BoolParameters<M::MatElement>>
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<M::MatElement>,
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<M::MatElement>,
NonInteractiveMultiPartyCrs<[u8; 32]>,
> {
assert_eq!(
@ -2004,6 +2013,7 @@ where
total_users,
self.parameters().lwe_n().0,
cr_seed.clone(),
self.parameters().clone(),
)
}

+ 217
- 69
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<S, E> {
seed: S,
@ -143,8 +140,6 @@ pub struct PublicKey {
}
pub(super) mod impl_pk {
use crate::evaluator::MultiPartyCrs;
use super::*;
impl<M, R, Mo> PublicKey<M, R, Mo> {
@ -296,8 +291,11 @@ mod impl_seeded_pk {
/// CRS seeded collective public key share
pub struct CommonReferenceSeededCollectivePublicKeyShare<Ro, S, P> {
/// Public key share polynomial
share: Ro,
/// Common reference seed
cr_seed: S,
/// Parameters
parameters: P,
}
impl<Ro, S, P> CommonReferenceSeededCollectivePublicKeyShare<Ro, S, P> {
@ -310,21 +308,36 @@ impl CommonReferenceSeededCollectivePublicKeyShare {
}
}
/// CRS seeded Multi-party server key share
pub struct CommonReferenceSeededMultiPartyServerKeyShare<M: Matrix, P, S> {
/// Common reference seed seeded interactive multi-party server key share
pub struct CommonReferenceSeededInteractiveMultiPartyServerKeyShare<M: Matrix, P, S> {
/// 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<M>,
/// 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<M>,
/// 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<usize, M>,
/// 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<M: Matrix, P, S> CommonReferenceSeededMultiPartyServerKeyShare<M, P, S> {
impl<M: Matrix, P, S> CommonReferenceSeededInteractiveMultiPartyServerKeyShare<M, P, S> {
pub(super) fn new(
self_leader_rgsws: Vec<M>,
not_self_leader_rgsws: Vec<M>,
@ -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<M: Matrix, S, P> {
/// Common reference seeded interactive multi-party server key
pub struct SeededInteractiveMultiPartyServerKey<M: Matrix, S, P> {
/// 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<M>,
/// 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<usize, M>,
/// 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<M: Matrix, S, P> SeededMultiPartyServerKey<M, S, P> {
impl<M: Matrix, S, P> SeededInteractiveMultiPartyServerKey<M, S, P> {
pub(super) fn new(
rgsw_cts: Vec<M>,
auto_keys: HashMap<usize, M>,
@ -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<M, P, R, N> {
/// Rgsw cts of LWE secret elements
/// RGSW ciphertext RGSW(X^{s[i]}) for each LWE index in evaluation domain
rgsw_cts: Vec<M>,
/// 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<usize, M>,
/// 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<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
>
From<&SeededMultiPartyServerKey<M, MultiPartyCrs<Rng::Seed>, BoolParameters<M::MatElement>>>
for ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, Rng, N>
From<
&SeededInteractiveMultiPartyServerKey<
M,
MultiPartyCrs<Rng::Seed>,
BoolParameters<M::MatElement>,
>,
> for ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, Rng, N>
where
<M as Matrix>::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<Rng::Seed>,
BoolParameters<M::MatElement>,
@ -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<M, P, R, N> {
/// 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<M>,
/// 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<usize, M>,
/// 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<M>,
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<M: Matrix, S, P> {
/// 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<M>,
/// RGSW ciphertets
/// RGSW ciphertexts RGSW(X^{s[i]}) under ideal RLWE secret key
rgsw_cts: Vec<M>,
/// Auto keys for all auto elements [g^{-1}, g, g^2, g^w]
auto_keys: HashMap<usize, M>,
/// 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<M: Matrix, S, P> SeededNonInteractiveMultiPartyServerKey<M, S, P> {
pub(super) fn new(
ui_to_s_ksks: Vec<M>,
rgsw_cts: Vec<M>,
auto_keys: HashMap<usize, M>,
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<M> {
/// RGSW ciphertexts ideal lwe secret key elements under ideal rlwe secret
rgsw_cts: Vec<NormalAndShoup<M>>,
/// Automorphism keys under ideal rlwe secret
auto_keys: HashMap<usize, NormalAndShoup<M>>,
/// 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<NormalAndShoup<M>>,
}
@ -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<M> ShoupNonInteractiveServerKeyEvaluationDomain<M> {
pub(in super::super) fn ui_to_s_ksk(&self, user_id: usize) -> &NormalAndShoup<M> {
@ -1030,7 +1067,7 @@ mod impl_shoup_non_interactive_server_key_eval_domain {
From<NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>>
for ShoupNonInteractiveServerKeyEvaluationDomain<M>
where
M::MatElement: FromPrimitive + ToPrimitive + PrimInt,
M::MatElement: FromPrimitive + ToPrimitive + PrimInt + NumInfo,
{
fn from(
value: NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, 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<M> {
/// Rgsw cts of LWE secret elements
rgsw_cts: Vec<NormalAndShoup<M>>,
/// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding
/// to -g is at 0
galois_keys: HashMap<usize, NormalAndShoup<M>>,
/// 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<M>
where
<M as Matrix>::R: RowMut,
M::MatElement: PrimInt + FromPrimitive,
M::MatElement: PrimInt + FromPrimitive + NumInfo,
{
fn from(value: ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>) -> Self {
let q = value.parameters.rlwe_q().q().unwrap();
@ -1180,27 +1215,56 @@ mod shoup_server_key_eval_domain {
}
}
pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M: Matrix, S> {
/// Non-interactive RGSW ciphertexts for secret indices for which user is
/// the leader
pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M: Matrix, P, S> {
/// Non-interactive RGSW ciphertexts for LWE secret indices for which user
/// is the leader
self_leader_ni_rgsw_cts: Vec<M>,
/// 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<M>,
/// Zero encryptions for RGSW ciphertexts for all indicces
/// Zero encryptions for RGSW ciphertexts for all indices
ni_rgsw_zero_encs: Vec<M>,
/// (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<M>,
/// 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<usize, M>,
/// 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<M: Matrix, S> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M, S> {
impl<M: Matrix, P, S> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M, P, S> {
pub(in super::super) fn new(
self_leader_ni_rgsw_cts: Vec<M>,
not_self_leader_ni_rgsw_cts: Vec<M>,
@ -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<usize, M> {
@ -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, M);
impl<M: ToShoup> NormalAndShoup<M> {
@ -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<M: Matrix, El, S> KeySize
for CommonReferenceSeededInteractiveMultiPartyServerKeyShare<M, BoolParameters<El>, 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<M: Matrix, El, S> KeySize
for CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M, BoolParameters<El>, 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>>,
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);

+ 1
- 2
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,
}

+ 11
- 14
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<Vec<Vec<u64>>, R, ModOp>,
) -> CommonReferenceSeededMultiPartyServerKeyShare<
) -> CommonReferenceSeededInteractiveMultiPartyServerKeyShare<
Vec<Vec<u64>>,
BoolParameters<u64>,
MultiPartyCrs<[u8; 32]>,
@ -93,17 +90,18 @@ pub fn aggregate_public_key_shares(
}
pub fn aggregate_server_key_shares(
shares: &[CommonReferenceSeededMultiPartyServerKeyShare<
shares: &[CommonReferenceSeededInteractiveMultiPartyServerKeyShare<
Vec<Vec<u64>>,
BoolParameters<u64>,
MultiPartyCrs<[u8; 32]>,
>],
) -> SeededMultiPartyServerKey<Vec<Vec<u64>>, MultiPartyCrs<[u8; 32]>, BoolParameters<u64>> {
) -> SeededInteractiveMultiPartyServerKey<Vec<Vec<u64>>, MultiPartyCrs<[u8; 32]>, BoolParameters<u64>>
{
BoolEvaluator::with_local(|e| e.aggregate_multi_party_server_key_shares(shares))
}
impl
SeededMultiPartyServerKey<
SeededInteractiveMultiPartyServerKey<
Vec<Vec<u64>>,
MultiPartyCrs<<DefaultSecureRng as NewWithSeed>::Seed>,
BoolParameters<u64>,
@ -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,

+ 2
- 0
src/bool/ni_mp_api.rs

@ -79,6 +79,7 @@ pub fn gen_server_key_share(
client_key: &ClientKey,
) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
Vec<Vec<u64>>,
BoolParameters<u64>,
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<Vec<u64>>,
BoolParameters<u64>,
NonInteractiveMultiPartyCrs<[u8; 32]>,
>],
) -> SeededNonInteractiveMultiPartyServerKey<

+ 73
- 14
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<El> {
/// RLWE ciphertext modulus Q
rlwe_q: CiphertextModulus<El>,
/// LWE ciphertext modulus q (usually referred to as Q_{ks})
lwe_q: CiphertextModulus<El>,
/// 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<T> CiphertextModulus<T>
where
T: PrimInt,
T: PrimInt + NumInfo,
{
pub(crate) fn _bits() -> usize {
std::mem::size_of::<T>() as usize * 8
fn _bits() -> usize {
T::BITS as usize
}
fn _native(&self) -> bool {
@ -376,7 +420,7 @@ where
impl<T> Modulus for CiphertextModulus<T>
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<u64> = BoolParameters::<u64> {
@ -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),

+ 21
- 12
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<i64>
where
M::R: RowMut + RowEntity + TryConvertFrom1<[i32], CiphertextModulus<M::MatElement>> + 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 =

+ 2
- 6
src/decomposer.rs

@ -7,6 +7,7 @@ use crate::{
parameters::{
DecompositionCount, DecompostionLogBase, DoubleDecomposerParams, SingleDecomposerParams,
},
utils::log2,
};
fn gadget_vector<T: PrimInt>(logq: usize, logb: usize, d: usize) -> Vec<T> {
@ -156,12 +157,7 @@ impl<
fn new(q: T, logb: usize, d: usize) -> DefaultDecomposer<T> {
// 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}"

+ 21
- 1
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<Element = Self::MatElement>;
@ -193,3 +194,22 @@ pub trait SampleExtractor {
trait Encoder<F, T> {
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<Vec<u64>> {
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<u64> {
fn size(&self, log_modulus: usize) -> usize {
self.len() * log_modulus
}
}

+ 10
- 0
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<T: PrimInt + NumInfo>(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<T: ?Sized, P> {
fn try_convert_from(value: &T, parameters: &P) -> Self;
}

Loading…
Cancel
Save