Skip to content

Commit 1b5a431

Browse files
committed
feat(CompressedXofKeySet): impl key gen of ClientKey
This adds the implemention of the ClientKey generation that respects the threshold specs, some points are: * 2 random generators are used one for everything public (masks) the other for everything private (private keys and noise). These generators are seeded once. * binary secret keys are generated using `fill_slice_with_random_uniform_binary_bits` and support pmax param
1 parent 19c2146 commit 1b5a431

File tree

8 files changed

+782
-311
lines changed

8 files changed

+782
-311
lines changed

tfhe/src/core_crypto/algorithms/glwe_secret_key_generation.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,53 @@ pub fn generate_binary_glwe_secret_key<Scalar, InCont, Gen>(
6767
{
6868
generator.fill_slice_with_random_uniform_binary(glwe_secret_key.as_mut());
6969
}
70+
71+
/// Fill a [`GLWE secret key`](`GlweSecretKey`) with uniformly random binary coefficients.
72+
///
73+
/// The hamming weight of the secret key will be in: `(1-max_norm_hwt)*len..=max_norm_hwt*len`
74+
/// where max_norm_hwt is in ]0.5, 1.0]
75+
///
76+
/// # Example
77+
///
78+
/// ```rust
79+
/// use tfhe::core_crypto::prelude::*;
80+
///
81+
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
82+
/// // computations
83+
/// // Define parameters for GlweSecretKey creation
84+
/// let glwe_size = GlweSize(2);
85+
/// let polynomial_size = PolynomialSize(1024);
86+
///
87+
/// // Create the PRNG
88+
/// let mut seeder = new_seeder();
89+
/// let seeder = seeder.as_mut();
90+
/// let mut secret_generator = SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
91+
/// let max_norm_hwt = MaxNormalizedHammingWeith::new(0.7).unwrap();
92+
///
93+
/// let mut glwe_secret_key =
94+
/// GlweSecretKey::new_empty_key(0u64, glwe_size.to_glwe_dimension(), polynomial_size);
95+
///
96+
/// generate_binary_glwe_secret_key_with_bounded_hamming_weight(
97+
/// &mut glwe_secret_key,
98+
/// &mut secret_generator,
99+
/// max_norm_hwt,
100+
/// );
101+
///
102+
/// for polynomial in glwe_secret_key.iter() {
103+
/// assert!(max_norm_hwt.check_binary_slice(polynomial.as_ref()).is_ok());
104+
/// }
105+
/// ```
106+
pub fn generate_binary_glwe_secret_key_with_bounded_hamming_weight<Scalar, InCont, Gen>(
107+
glwe_secret_key: &mut GlweSecretKey<InCont>,
108+
generator: &mut SecretRandomGenerator<Gen>,
109+
max_norm_hwt: MaxNormalizedHammingWeight,
110+
) where
111+
Scalar: UnsignedInteger + RandomGenerable<UniformBinary>,
112+
InCont: ContainerMut<Element = Scalar>,
113+
Gen: ByteRandomGenerator,
114+
{
115+
for mut secret_poly in glwe_secret_key.as_mut_polynomial_list().iter_mut() {
116+
generator
117+
.fill_slice_with_bounded_random_uniform_binary_bits(secret_poly.as_mut(), max_norm_hwt);
118+
}
119+
}

tfhe/src/core_crypto/algorithms/lwe_secret_key_generation.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Module containing primitives pertaining to the generation of
22
//! [`LWE secret keys`](`LweSecretKey`).
3-
43
use crate::core_crypto::commons::generators::SecretRandomGenerator;
4+
55
use crate::core_crypto::commons::math::random::{RandomGenerable, UniformBinary};
66
use crate::core_crypto::commons::parameters::*;
77
use crate::core_crypto::commons::traits::*;
@@ -62,3 +62,47 @@ pub fn generate_binary_lwe_secret_key<Scalar, InCont, Gen>(
6262
{
6363
generator.fill_slice_with_random_uniform_binary(lwe_secret_key.as_mut());
6464
}
65+
66+
/// Fill an [`LWE secret key`](`LweSecretKey`) with uniformly random binary coefficients.
67+
///
68+
/// The hamming weight of the secret key will be in: `(1-max_norm_hwt)*len..=max_norm_hwt*len`
69+
/// where max_norm_hwt is in ]0.5, 1.0]
70+
///
71+
/// # Example
72+
///
73+
/// ```rust
74+
/// use tfhe::core_crypto::prelude::*;
75+
///
76+
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
77+
/// // computations
78+
/// // Define parameters for LweCiphertext creation
79+
/// let lwe_dimension = LweDimension(742);
80+
///
81+
/// // Create the PRNG
82+
/// let mut seeder = new_seeder();
83+
/// let seeder = seeder.as_mut();
84+
/// let mut secret_generator = SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
85+
/// let max_norm_hwt = MaxNormalizedHammingWeith::new(0.7).unwrap();
86+
/// let mut lwe_secret_key = LweSecretKey::new_empty_key(0u64, lwe_dimension);
87+
///
88+
/// generate_binary_lwe_secret_key_with_bounded_hamming_weight(
89+
/// &mut lwe_secret_key,
90+
/// &mut secret_generator,
91+
/// max_norm_hwt,
92+
/// );
93+
///
94+
/// // Check all coefficients are not zero as we just generated a new key
95+
/// assert!(max_norm_hwt.check_binary_slice(lwe_secret_key.as_ref()).is_ok());
96+
/// ```
97+
pub fn generate_binary_lwe_secret_key_with_bounded_hamming_weight<Scalar, InCont, Gen>(
98+
lwe_secret_key: &mut LweSecretKey<InCont>,
99+
generator: &mut SecretRandomGenerator<Gen>,
100+
max_norm_hwt: MaxNormalizedHammingWeight,
101+
) where
102+
Scalar: RandomGenerable<UniformBinary> + UnsignedInteger,
103+
InCont: ContainerMut<Element = Scalar>,
104+
Gen: ByteRandomGenerator,
105+
{
106+
generator
107+
.fill_slice_with_bounded_random_uniform_binary_bits(lwe_secret_key.as_mut(), max_norm_hwt);
108+
}

tfhe/src/core_crypto/commons/generators/encryption/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl<G: ByteRandomGenerator> EncryptionRandomGenerator<G> {
110110
}
111111
}
112112

113-
#[cfg(all(feature = "integer", test))]
113+
#[cfg(feature = "integer")]
114114
pub(crate) fn from_raw_parts(
115115
mask: MaskRandomGenerator<G>,
116116
noise: NoiseRandomGenerator<G>,

tfhe/src/core_crypto/commons/generators/encryption/noise_random_generator.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ impl<G: ByteRandomGenerator> NoiseRandomGenerator<G> {
108108
}
109109
}
110110

111+
pub fn from_raw_parts(gen: RandomGenerator<G>) -> Self {
112+
Self { gen }
113+
}
114+
111115
/// Create a new [`NoiseRandomGenerator`], using the provided seed
112116
pub fn new_from_seed(seed: impl Into<SeedKind>) -> Self {
113117
Self {

tfhe/src/core_crypto/commons/generators/secret.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
//! Module containing primitives pertaining to random generation in the context of secret key
22
//! generation.
33
4+
use tfhe_csprng::seeders::SeedKind;
5+
46
use crate::core_crypto::commons::math::random::{
5-
ByteRandomGenerator, RandomGenerable, RandomGenerator, Seed, UniformBinary,
7+
ByteRandomGenerator, RandomGenerable, RandomGenerator, UniformBinary,
68
};
9+
use crate::core_crypto::prelude::{MaxNormalizedHammingWeight, UnsignedInteger};
710

811
/// A random number generator which can be used to generate secret keys.
912
pub struct SecretRandomGenerator<G: ByteRandomGenerator>(RandomGenerator<G>);
1013

1114
impl<G: ByteRandomGenerator> SecretRandomGenerator<G> {
1215
/// Create a new generator, optionally seeding it with the given value.
13-
pub fn new(seed: Seed) -> Self {
16+
pub fn new(seed: impl Into<SeedKind>) -> Self {
1417
Self(RandomGenerator::new(seed))
1518
}
1619

20+
pub fn from_raw_parts(inner: RandomGenerator<G>) -> Self {
21+
Self(inner)
22+
}
23+
24+
pub fn into_raw_parts(self) -> RandomGenerator<G> {
25+
self.0
26+
}
27+
1728
/// Return the number of remaining bytes, if the generator is bounded.
1829
pub fn remaining_bytes(&self) -> Option<usize> {
1930
self.0.remaining_bytes()
@@ -32,4 +43,31 @@ impl<G: ByteRandomGenerator> SecretRandomGenerator<G> {
3243
{
3344
self.0.random_uniform_binary()
3445
}
46+
47+
pub fn fill_slice_with_random_uniform_binary_bits<Scalar>(&mut self, output: &mut [Scalar])
48+
where
49+
Scalar: UnsignedInteger,
50+
{
51+
self.0.fill_slice_with_random_uniform_binary_bits(output);
52+
}
53+
54+
/// Fills the slice with uniform binary random, such that the
55+
/// hamming weight of the output is ok with regards to the max normalized hamming weight
56+
///
57+
/// This will loop and reject slices until an ok sequence of random binary was generated
58+
pub fn fill_slice_with_bounded_random_uniform_binary_bits<Scalar>(
59+
&mut self,
60+
output: &mut [Scalar],
61+
max_norm_hwt: MaxNormalizedHammingWeight,
62+
) where
63+
Scalar: UnsignedInteger,
64+
{
65+
loop {
66+
self.0.fill_slice_with_random_uniform_binary_bits(output);
67+
68+
if let Ok(()) = max_norm_hwt.check_binary_slice(output) {
69+
break;
70+
}
71+
}
72+
}
3573
}

tfhe/src/core_crypto/commons/parameters.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
//!
44
//! These types have 0 overhead compared to the type being wrapped.
55
6+
use std::ops::RangeInclusive;
7+
68
use serde::{Deserialize, Serialize};
79
use tfhe_versionable::Versionize;
810

911
pub use super::ciphertext_modulus::CiphertextModulus;
12+
use super::traits::CastInto;
1013
use crate::core_crypto::backward_compatibility::commons::parameters::*;
1114

1215
/// The number plaintexts in a plaintext list.
@@ -408,3 +411,59 @@ pub struct NoiseEstimationMeasureBound(pub f64);
408411
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Versionize)]
409412
#[versionize(ChunkSizeVersions)]
410413
pub struct ChunkSize(pub usize);
414+
415+
/// The max normalized hamming weight
416+
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
417+
pub struct MaxNormalizedHammingWeight(f64);
418+
419+
impl MaxNormalizedHammingWeight {
420+
/// Creates `self`, returns None if pmax not in ]0.5, 1.0]
421+
pub fn new(pmax: f64) -> Option<Self> {
422+
if 0.5 < pmax && pmax <= 1.0 {
423+
Some(Self(pmax))
424+
} else {
425+
None
426+
}
427+
}
428+
429+
/// Returns the inner value
430+
pub fn get(self) -> f64 {
431+
self.0
432+
}
433+
434+
/// Given the number of bits `num_bits`, returns the range
435+
/// of acceptable hamming weights for a slice of `num_bits` bits
436+
pub fn hamming_weight_range(self, num_bits: usize) -> RangeInclusive<u128> {
437+
RangeInclusive::new(
438+
((1.0 - self.0) * num_bits as f64) as u128,
439+
(self.0 * num_bits as f64) as u128,
440+
)
441+
}
442+
443+
/// Checks that the binary_slice's hamming weight is ok with regards to `self`
444+
///
445+
/// * Returns Ok(()) if the hamming weight is ok, otherwise Err(())
446+
///
447+
/// # Note
448+
///
449+
/// The slice elements must be binary, that is they must be 0 or 1,
450+
/// otherwise the result will be incorrect
451+
pub fn check_binary_slice<T>(self, binary_slice: &[T]) -> Result<(), ()>
452+
where
453+
T: Copy + CastInto<u128>,
454+
{
455+
let hamming_weight = binary_slice
456+
.iter()
457+
.copied()
458+
.map(|bit| -> u128 { bit.cast_into() })
459+
.sum::<u128>();
460+
461+
let bounds = self.hamming_weight_range(binary_slice.len());
462+
463+
if bounds.contains(&hamming_weight) {
464+
Ok(())
465+
} else {
466+
Err(())
467+
}
468+
}
469+
}

tfhe/src/core_crypto/entities/glwe_secret_key.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> GlweSecretKey<C
149149
let polynomial_size = self.polynomial_size;
150150
GlweSecretKey::from_container(self.as_mut(), polynomial_size)
151151
}
152+
153+
/// Interpret the [`GlweSecretKey`] as a [`PolynomialListMutView`].
154+
pub fn as_mut_polynomial_list(&mut self) -> PolynomialListMutView<'_, C::Element> {
155+
let poly_size = self.polynomial_size;
156+
PolynomialListMutView::from_container(self.as_mut(), poly_size)
157+
}
152158
}
153159

154160
/// A [`GlweSecretKey`] owning the memory for its own storage.

0 commit comments

Comments
 (0)