mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-10 15:18:33 -05:00
refactor(all): refactor oprf integer and hl APIs
This commit is contained in:
@@ -19,11 +19,13 @@ pub fn unsigned_oprf(c: &mut Criterion) {
|
||||
let bench_id = format!("{}::{}::{}_bits", bench_name, param.name(), bit_size);
|
||||
bench_group.bench_function(&bench_id, |b| {
|
||||
b.iter(|| {
|
||||
_ = black_box(sk.par_generate_oblivious_pseudo_random_unsigned_integer(
|
||||
Seed(0),
|
||||
bit_size as u64,
|
||||
num_block as u64,
|
||||
));
|
||||
_ = black_box(
|
||||
sk.par_generate_oblivious_pseudo_random_unsigned_integer_bounded(
|
||||
Seed(0),
|
||||
bit_size as u64,
|
||||
num_block as u64,
|
||||
),
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -599,7 +599,7 @@ void test_oprf(const ClientKey *client_key) {
|
||||
|
||||
fhe_uint8_destroy(ct);
|
||||
|
||||
status = generate_oblivious_pseudo_random_bits_fhe_uint8(&ct, 0, 0, 2);
|
||||
status = generate_oblivious_pseudo_random_bounded_fhe_uint8(&ct, 0, 0, 2);
|
||||
assert(status == 0);
|
||||
|
||||
status = fhe_uint8_decrypt(ct, client_key, &decrypted);
|
||||
@@ -613,7 +613,7 @@ void test_oprf(const ClientKey *client_key) {
|
||||
{
|
||||
FheInt8 *ct = NULL;
|
||||
|
||||
int status = generate_oblivious_pseudo_random_full_signed_range_fhe_int8(&ct, 0, 0);
|
||||
int status = generate_oblivious_pseudo_random_fhe_int8(&ct, 0, 0);
|
||||
assert(status == 0);
|
||||
|
||||
int8_t decrypted;
|
||||
@@ -623,7 +623,7 @@ void test_oprf(const ClientKey *client_key) {
|
||||
|
||||
fhe_int8_destroy(ct);
|
||||
|
||||
status = generate_oblivious_pseudo_random_unsigned_fhe_int8(&ct, 0, 0, 2);
|
||||
status = generate_oblivious_pseudo_random_bounded_fhe_int8(&ct, 0, 0, 2);
|
||||
assert(status == 0);
|
||||
|
||||
status = fhe_int8_decrypt(ct, client_key, &decrypted);
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
# Generate encrypted pseudo random values
|
||||
|
||||
This document gives an example of generating pseudo random values in FHE that are not known by the server.
|
||||
This document explains the mechanism and steps to generate an oblivious encrypted random value using only server keys.
|
||||
|
||||
The goal is to give to the server the possibility to generate a random value, which will be obtained in an encrypted format and will remain unknown to the server. The implementation is based on [this article](https://eprint.iacr.org/2024/665).
|
||||
|
||||
This is possible through two methods on `FheUint` and `FheInt`:
|
||||
- `generate_oblivious_pseudo_random` which return an integer taken uniformly in the full integer range (`[0; 2^N[` for a `FheUintN` and `[-2^(N-1); 2^(N-1)[` for a `FheIntN`).
|
||||
- `generate_oblivious_pseudo_random_bounded` which return an integer taken uniformly in `[0; 2^random_bits_count[`. For a `FheUintN`, we must have `random_bits_count <= N`. For a `FheIntN`, we must have `random_bits_count <= N - 1`.
|
||||
|
||||
Both methods functions take a seed `Seed` as input, which could be any `u128` value.
|
||||
They both rely on the use of the usual server key.
|
||||
The output is reproducible, i.e., the function is deterministic from the inputs: assuming the same hardware, seed and server key, this function outputs the same random encrypted value.
|
||||
|
||||
|
||||
Here is an example of the usage:
|
||||
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::FheDecrypt;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, Seed};
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, FheInt8, Seed};
|
||||
|
||||
pub fn main() {
|
||||
let config = ConfigBuilder::default().build();
|
||||
@@ -14,12 +28,22 @@ pub fn main() {
|
||||
|
||||
let random_bits_count = 3;
|
||||
|
||||
// You can pass a 128 bits Seed here
|
||||
// The generated values will always be the same for a given server key
|
||||
// The server cannot know what value was generated
|
||||
let ct_res = FheUint8::generate_oblivious_pseudo_random(Seed(0), random_bits_count);
|
||||
let ct_res = FheUint8::generate_oblivious_pseudo_random(Seed(0));
|
||||
|
||||
let dec_result: u8 = ct_res.decrypt(&client_key);
|
||||
|
||||
let ct_res = FheUint8::generate_oblivious_pseudo_random_bounded(Seed(0), random_bits_count);
|
||||
|
||||
let dec_result: u8 = ct_res.decrypt(&client_key);
|
||||
assert!(dec_result < (1 << random_bits_count));
|
||||
|
||||
let ct_res = FheInt8::generate_oblivious_pseudo_random(Seed(0));
|
||||
|
||||
let dec_result: i8 = ct_res.decrypt(&client_key);
|
||||
|
||||
let ct_res = FheInt8::generate_oblivious_pseudo_random_bounded(Seed(0), random_bits_count);
|
||||
|
||||
let dec_result: i8 = ct_res.decrypt(&client_key);
|
||||
assert!(dec_result < (1 << random_bits_count));
|
||||
}
|
||||
```
|
||||
|
||||
@@ -491,7 +491,6 @@ macro_rules! impl_oprf_for_uint {
|
||||
seed_low_bytes: u64,
|
||||
seed_high_bytes: u64,
|
||||
) -> c_int {
|
||||
use crate::high_level_api::IntegerId;
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let seed_low_bytes: u128 = seed_low_bytes.into();
|
||||
let seed_high_bytes: u128 = seed_high_bytes.into();
|
||||
@@ -499,7 +498,6 @@ macro_rules! impl_oprf_for_uint {
|
||||
|
||||
let result = crate::FheUint::generate_oblivious_pseudo_random(
|
||||
seed,
|
||||
<crate::[<$name Id>] as IntegerId>::num_bits() as u64
|
||||
);
|
||||
*out_result = Box::into_raw(Box::new($name(result)));
|
||||
})
|
||||
@@ -508,7 +506,7 @@ macro_rules! impl_oprf_for_uint {
|
||||
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<generate_oblivious_pseudo_random_bits_ $name:snake>](
|
||||
pub unsafe extern "C" fn [<generate_oblivious_pseudo_random_bounded_ $name:snake>](
|
||||
out_result: *mut *mut $name,
|
||||
seed_low_bytes: u64,
|
||||
seed_high_bytes: u64,
|
||||
@@ -520,7 +518,7 @@ macro_rules! impl_oprf_for_uint {
|
||||
let seed_high_bytes: u128 = seed_high_bytes.into();
|
||||
let seed = crate::Seed((seed_high_bytes << 64) | seed_low_bytes);
|
||||
|
||||
let result = crate::FheUint::generate_oblivious_pseudo_random(seed, random_bits_count);
|
||||
let result = crate::FheUint::generate_oblivious_pseudo_random_bounded(seed, random_bits_count);
|
||||
*out_result = Box::into_raw(Box::new($name(result)));
|
||||
})
|
||||
}
|
||||
@@ -532,35 +530,9 @@ macro_rules! impl_oprf_for_int {
|
||||
(
|
||||
name: $name:ident
|
||||
) => {
|
||||
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<generate_oblivious_pseudo_random_unsigned_ $name:snake>](
|
||||
out_result: *mut *mut $name,
|
||||
seed_low_bytes: u64,
|
||||
seed_high_bytes: u64,
|
||||
random_bits_count: u64,
|
||||
) -> c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let seed_low_bytes: u128 = seed_low_bytes.into();
|
||||
let seed_high_bytes: u128 = seed_high_bytes.into();
|
||||
let seed = crate::Seed((seed_high_bytes << 64) | seed_low_bytes);
|
||||
|
||||
let result =
|
||||
crate::FheInt::generate_oblivious_pseudo_random(
|
||||
seed,
|
||||
crate::high_level_api::SignedRandomizationSpec::Unsigned {
|
||||
random_bits_count
|
||||
},
|
||||
);
|
||||
*out_result = Box::into_raw(Box::new($name(result)));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<generate_oblivious_pseudo_random_full_signed_range_ $name:snake>](
|
||||
pub unsafe extern "C" fn [<generate_oblivious_pseudo_random_ $name:snake>](
|
||||
out_result: *mut *mut $name,
|
||||
seed_low_bytes: u64,
|
||||
seed_high_bytes: u64,
|
||||
@@ -572,12 +544,34 @@ macro_rules! impl_oprf_for_int {
|
||||
|
||||
let result = crate::FheInt::generate_oblivious_pseudo_random(
|
||||
seed,
|
||||
crate::high_level_api::SignedRandomizationSpec::FullSigned,
|
||||
);
|
||||
*out_result = Box::into_raw(Box::new($name(result)));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<generate_oblivious_pseudo_random_bounded_ $name:snake>](
|
||||
out_result: *mut *mut $name,
|
||||
seed_low_bytes: u64,
|
||||
seed_high_bytes: u64,
|
||||
random_bits_count: u64,
|
||||
) -> c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let seed_low_bytes: u128 = seed_low_bytes.into();
|
||||
let seed_high_bytes: u128 = seed_high_bytes.into();
|
||||
let seed = crate::Seed((seed_high_bytes << 64) | seed_low_bytes);
|
||||
|
||||
let result =
|
||||
crate::FheInt::generate_oblivious_pseudo_random_bounded(
|
||||
seed,
|
||||
random_bits_count,
|
||||
);
|
||||
*out_result = Box::into_raw(Box::new($name(result)));
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,49 @@
|
||||
use super::{FheIntId, FheUintId};
|
||||
use crate::high_level_api::global_state;
|
||||
use crate::high_level_api::keys::InternalServerKey;
|
||||
use crate::integer::oprf::SignedRandomizationSpec;
|
||||
use crate::{FheInt, FheUint, Seed};
|
||||
|
||||
impl<Id: FheUintId> FheUint<Id> {
|
||||
/// Generates an encrypted unsigned integer
|
||||
/// taken uniformly in its full range using the given seed.
|
||||
/// The encryted value is oblivious to the server.
|
||||
/// It can be useful to make server random generation deterministic.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::prelude::FheDecrypt;
|
||||
/// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, Seed};
|
||||
///
|
||||
/// let config = ConfigBuilder::default().build();
|
||||
/// let (client_key, server_key) = generate_keys(config);
|
||||
///
|
||||
/// set_server_key(server_key);
|
||||
///
|
||||
/// let ct_res = FheUint8::generate_oblivious_pseudo_random(Seed(0));
|
||||
///
|
||||
/// let dec_result: u16 = ct_res.decrypt(&client_key);
|
||||
/// ```
|
||||
pub fn generate_oblivious_pseudo_random(seed: Seed) -> Self {
|
||||
global_state::with_internal_keys(|key| match key {
|
||||
InternalServerKey::Cpu(key) => {
|
||||
let ct = key
|
||||
.pbs_key()
|
||||
.par_generate_oblivious_pseudo_random_unsigned_integer(
|
||||
seed,
|
||||
Id::num_blocks(key.message_modulus()) as u64,
|
||||
);
|
||||
|
||||
Self::new(ct, key.tag.clone())
|
||||
}
|
||||
#[cfg(feature = "gpu")]
|
||||
InternalServerKey::Cuda(_) => {
|
||||
todo!("Cuda devices do not yet support oblivious pseudo random generation")
|
||||
}
|
||||
})
|
||||
}
|
||||
/// Generates an encrypted `num_block` blocks unsigned integer
|
||||
/// taken uniformly in `[0, 2^random_bits_count[` using the given seed
|
||||
/// The encryted value is oblivious to the server
|
||||
/// It can be useful to make server random generation deterministic
|
||||
/// taken uniformly in `[0, 2^random_bits_count[` using the given seed.
|
||||
/// The encryted value is oblivious to the server.
|
||||
/// It can be useful to make server random generation deterministic.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::prelude::FheDecrypt;
|
||||
@@ -21,17 +56,17 @@ impl<Id: FheUintId> FheUint<Id> {
|
||||
///
|
||||
/// let random_bits_count = 3;
|
||||
///
|
||||
/// let ct_res = FheUint8::generate_oblivious_pseudo_random(Seed(0), random_bits_count);
|
||||
/// let ct_res = FheUint8::generate_oblivious_pseudo_random_bounded(Seed(0), random_bits_count);
|
||||
///
|
||||
/// let dec_result: u16 = ct_res.decrypt(&client_key);
|
||||
/// assert!(dec_result < (1 << random_bits_count));
|
||||
/// ```
|
||||
pub fn generate_oblivious_pseudo_random(seed: Seed, random_bits_count: u64) -> Self {
|
||||
pub fn generate_oblivious_pseudo_random_bounded(seed: Seed, random_bits_count: u64) -> Self {
|
||||
global_state::with_internal_keys(|key| match key {
|
||||
InternalServerKey::Cpu(key) => {
|
||||
let ct = key
|
||||
.pbs_key()
|
||||
.par_generate_oblivious_pseudo_random_unsigned_integer(
|
||||
.par_generate_oblivious_pseudo_random_unsigned_integer_bounded(
|
||||
seed,
|
||||
random_bits_count,
|
||||
Id::num_blocks(key.message_modulus()) as u64,
|
||||
@@ -48,40 +83,33 @@ impl<Id: FheUintId> FheUint<Id> {
|
||||
}
|
||||
|
||||
impl<Id: FheIntId> FheInt<Id> {
|
||||
/// Generates an encrypted `num_block` blocks signed integer
|
||||
/// using the given seed following the randomizer spec
|
||||
/// The encryted value is oblivious to the server
|
||||
/// It can be useful to make server random generation deterministic
|
||||
/// Generates an encrypted signed integer
|
||||
/// taken uniformly in its full range using the given seed.
|
||||
/// The encryted value is oblivious to the server.
|
||||
/// It can be useful to make server random generation deterministic.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::prelude::FheDecrypt;
|
||||
/// use tfhe::{
|
||||
/// generate_keys, set_server_key, ConfigBuilder, FheInt8, Seed, SignedRandomizationSpec,
|
||||
/// };
|
||||
/// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt8, Seed};
|
||||
///
|
||||
/// let config = ConfigBuilder::default().build();
|
||||
/// let (client_key, server_key) = generate_keys(config);
|
||||
///
|
||||
/// set_server_key(server_key);
|
||||
///
|
||||
/// let ct_res =
|
||||
/// FheInt8::generate_oblivious_pseudo_random(Seed(0), SignedRandomizationSpec::FullSigned);
|
||||
/// let ct_res = FheInt8::generate_oblivious_pseudo_random(Seed(0));
|
||||
///
|
||||
/// let dec_result: i16 = ct_res.decrypt(&client_key);
|
||||
/// assert!(dec_result < 1 << 7);
|
||||
/// assert!(dec_result >= -(1 << 7));
|
||||
/// ```
|
||||
pub fn generate_oblivious_pseudo_random(
|
||||
seed: Seed,
|
||||
randomizer: SignedRandomizationSpec,
|
||||
) -> Self {
|
||||
pub fn generate_oblivious_pseudo_random(seed: Seed) -> Self {
|
||||
global_state::with_internal_keys(|key| match key {
|
||||
InternalServerKey::Cpu(key) => {
|
||||
let ct = key
|
||||
.pbs_key()
|
||||
.par_generate_oblivious_pseudo_random_signed_integer(
|
||||
seed,
|
||||
randomizer,
|
||||
Id::num_blocks(key.message_modulus()) as u64,
|
||||
);
|
||||
Self::new(ct, key.tag.clone())
|
||||
@@ -92,4 +120,46 @@ impl<Id: FheIntId> FheInt<Id> {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates an encrypted `num_block` blocks signed integer
|
||||
/// taken uniformly in `[0, 2^random_bits_count[` using the given seed.
|
||||
/// The encryted value is oblivious to the server.
|
||||
/// It can be useful to make server random generation deterministic.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::prelude::FheDecrypt;
|
||||
/// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt8, Seed};
|
||||
///
|
||||
/// let config = ConfigBuilder::default().build();
|
||||
/// let (client_key, server_key) = generate_keys(config);
|
||||
///
|
||||
/// set_server_key(server_key);
|
||||
///
|
||||
/// let random_bits_count = 3;
|
||||
///
|
||||
/// let ct_res = FheInt8::generate_oblivious_pseudo_random_bounded(Seed(0), random_bits_count);
|
||||
///
|
||||
/// let dec_result: i16 = ct_res.decrypt(&client_key);
|
||||
/// assert!(dec_result >= 0);
|
||||
/// assert!(dec_result < 1 << random_bits_count);
|
||||
/// ```
|
||||
pub fn generate_oblivious_pseudo_random_bounded(seed: Seed, random_bits_count: u64) -> Self {
|
||||
global_state::with_internal_keys(|key| match key {
|
||||
InternalServerKey::Cpu(key) => {
|
||||
let ct = key
|
||||
.pbs_key()
|
||||
.par_generate_oblivious_pseudo_random_signed_integer_bounded(
|
||||
seed,
|
||||
random_bits_count,
|
||||
Id::num_blocks(key.message_modulus()) as u64,
|
||||
);
|
||||
|
||||
Self::new(ct, key.tag.clone())
|
||||
}
|
||||
#[cfg(feature = "gpu")]
|
||||
InternalServerKey::Cuda(_) => {
|
||||
todo!("Cuda devices do not yet support oblivious pseudo random generation")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ macro_rules! expand_pub_use_fhe_type(
|
||||
);
|
||||
|
||||
pub use crate::core_crypto::commons::math::random::Seed;
|
||||
pub use crate::integer::oprf::SignedRandomizationSpec;
|
||||
pub use crate::integer::server_key::MatchValues;
|
||||
pub use config::{Config, ConfigBuilder};
|
||||
pub use global_state::{set_server_key, unset_server_key, with_server_key_as_context};
|
||||
|
||||
@@ -7,9 +7,79 @@ pub use concrete_csprng::seeders::{Seed, Seeder};
|
||||
|
||||
impl ServerKey {
|
||||
/// Generates an encrypted `num_block` blocks unsigned integer
|
||||
/// taken uniformly in `[0, 2^random_bits_count[` using the given seed
|
||||
/// The encryted value is oblivious to the server
|
||||
/// It can be useful to make server random generation deterministic
|
||||
/// taken uniformly in its full range using the given seed.
|
||||
/// The encryted value is oblivious to the server.
|
||||
/// It can be useful to make server random generation deterministic.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::integer::gen_keys_radix;
|
||||
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
|
||||
/// use tfhe::Seed;
|
||||
///
|
||||
/// let size = 4;
|
||||
///
|
||||
/// // Generate the client key and the server key:
|
||||
/// let (cks, sks) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, size);
|
||||
///
|
||||
/// let ct_res = sks.par_generate_oblivious_pseudo_random_unsigned_integer(Seed(0), size as u64);
|
||||
///
|
||||
/// // Decrypt:
|
||||
/// let dec_result: u64 = cks.decrypt(&ct_res);
|
||||
///
|
||||
/// assert!(dec_result < 1 << (2 * size));
|
||||
/// ```
|
||||
pub fn par_generate_oblivious_pseudo_random_unsigned_integer(
|
||||
&self,
|
||||
seed: Seed,
|
||||
num_blocks: u64,
|
||||
) -> RadixCiphertext {
|
||||
assert!(self.message_modulus().0.is_power_of_two());
|
||||
let range_log_size = self.message_modulus().0.ilog2() as u64 * num_blocks;
|
||||
|
||||
let random_bits_count = range_log_size;
|
||||
|
||||
assert!(self.message_modulus().0.is_power_of_two());
|
||||
let message_bits_count = self.message_modulus().0.ilog2() as u64;
|
||||
|
||||
let mut deterministic_seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
|
||||
|
||||
let seeds: Vec<Seed> = (0..num_blocks)
|
||||
.map(|_| deterministic_seeder.seed())
|
||||
.collect();
|
||||
|
||||
let blocks = seeds
|
||||
.into_par_iter()
|
||||
.enumerate()
|
||||
.map(|(i, seed)| {
|
||||
let i = i as u64;
|
||||
|
||||
if i * message_bits_count < random_bits_count {
|
||||
// if we generate 5 bits of noise in n blocks of 2 bits, the third (i=2) block
|
||||
// must have only one bit of random
|
||||
if random_bits_count < (i + 1) * message_bits_count {
|
||||
let top_message_bits_count = random_bits_count - i * message_bits_count;
|
||||
|
||||
assert!(top_message_bits_count <= message_bits_count);
|
||||
|
||||
self.key
|
||||
.generate_oblivious_pseudo_random(seed, top_message_bits_count)
|
||||
} else {
|
||||
self.key
|
||||
.generate_oblivious_pseudo_random(seed, message_bits_count)
|
||||
}
|
||||
} else {
|
||||
self.key.create_trivial(0)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
RadixCiphertext::from(blocks)
|
||||
}
|
||||
|
||||
/// Generates an encrypted `num_block` blocks unsigned integer
|
||||
/// taken uniformly in `[0, 2^random_bits_count[` using the given seed.
|
||||
/// The encryted value is oblivious to the server.
|
||||
/// It can be useful to make server random generation deterministic.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::integer::gen_keys_radix;
|
||||
@@ -23,7 +93,7 @@ impl ServerKey {
|
||||
///
|
||||
/// let random_bits_count = 3;
|
||||
///
|
||||
/// let ct_res = sks.par_generate_oblivious_pseudo_random_unsigned_integer(
|
||||
/// let ct_res = sks.par_generate_oblivious_pseudo_random_unsigned_integer_bounded(
|
||||
/// Seed(0),
|
||||
/// random_bits_count,
|
||||
/// size as u64,
|
||||
@@ -33,7 +103,7 @@ impl ServerKey {
|
||||
/// let dec_result: u64 = cks.decrypt(&ct_res);
|
||||
/// assert!(dec_result < (1 << random_bits_count));
|
||||
/// ```
|
||||
pub fn par_generate_oblivious_pseudo_random_unsigned_integer(
|
||||
pub fn par_generate_oblivious_pseudo_random_unsigned_integer_bounded(
|
||||
&self,
|
||||
seed: Seed,
|
||||
random_bits_count: u64,
|
||||
@@ -86,24 +156,14 @@ impl ServerKey {
|
||||
}
|
||||
}
|
||||
|
||||
// Describes in which range a random signed integer should be generated
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum SignedRandomizationSpec {
|
||||
// taken uniformly in `[0, 2^random_bits_count[`
|
||||
Unsigned { random_bits_count: u64 },
|
||||
// taken uniformly in the full range [-2^(p-1), 2^(p-1)[
|
||||
FullSigned,
|
||||
}
|
||||
|
||||
impl ServerKey {
|
||||
/// Generates an encrypted `num_block` blocks signed integer
|
||||
/// using the given seed following the randomizer spec
|
||||
/// The encryted value is oblivious to the server
|
||||
/// It can be useful to make server random generation deterministic
|
||||
/// taken uniformly in its full range using the given seed.
|
||||
/// The encryted value is oblivious to the server.
|
||||
/// It can be useful to make server random generation deterministic.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::integer::gen_keys_radix;
|
||||
/// use tfhe::integer::oprf::SignedRandomizationSpec;
|
||||
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
|
||||
/// use tfhe::Seed;
|
||||
///
|
||||
@@ -112,11 +172,7 @@ impl ServerKey {
|
||||
/// // Generate the client key and the server key:
|
||||
/// let (cks, sks) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, size);
|
||||
///
|
||||
/// let ct_res = sks.par_generate_oblivious_pseudo_random_signed_integer(
|
||||
/// Seed(0),
|
||||
/// SignedRandomizationSpec::FullSigned,
|
||||
/// size as u64,
|
||||
/// );
|
||||
/// let ct_res = sks.par_generate_oblivious_pseudo_random_signed_integer(Seed(0), size as u64);
|
||||
///
|
||||
/// // Decrypt:
|
||||
/// let dec_result: i64 = cks.decrypt_signed(&ct_res);
|
||||
@@ -126,14 +182,67 @@ impl ServerKey {
|
||||
pub fn par_generate_oblivious_pseudo_random_signed_integer(
|
||||
&self,
|
||||
seed: Seed,
|
||||
randomizer: SignedRandomizationSpec,
|
||||
num_blocks: u64,
|
||||
) -> SignedRadixCiphertext {
|
||||
#[allow(clippy::int_plus_one)]
|
||||
if let SignedRandomizationSpec::Unsigned { random_bits_count } = randomizer {
|
||||
assert!(self.message_modulus().0.is_power_of_two());
|
||||
let range_log_size = self.message_modulus().0.ilog2() as u64 * num_blocks;
|
||||
assert!(self.message_modulus().0.is_power_of_two());
|
||||
let message_bits_count = self.message_modulus().0.ilog2() as u64;
|
||||
|
||||
let mut deterministic_seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
|
||||
|
||||
let seeds: Vec<Seed> = (0..num_blocks)
|
||||
.map(|_| deterministic_seeder.seed())
|
||||
.collect();
|
||||
|
||||
let blocks = seeds
|
||||
.into_par_iter()
|
||||
.map(|seed| {
|
||||
self.key
|
||||
.generate_oblivious_pseudo_random(seed, message_bits_count)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
SignedRadixCiphertext::from(blocks)
|
||||
}
|
||||
|
||||
/// Generates an encrypted `num_block` blocks signed integer
|
||||
/// taken uniformly in `[0, 2^random_bits_count[` using the given seed.
|
||||
/// The encryted value is oblivious to the server.
|
||||
/// It can be useful to make server random generation deterministic.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::integer::gen_keys_radix;
|
||||
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
|
||||
/// use tfhe::Seed;
|
||||
///
|
||||
/// let size = 4;
|
||||
///
|
||||
/// let random_bits_count = 3;
|
||||
///
|
||||
/// // Generate the client key and the server key:
|
||||
/// let (cks, sks) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, size);
|
||||
///
|
||||
/// let ct_res = sks.par_generate_oblivious_pseudo_random_signed_integer_bounded(
|
||||
/// Seed(0),
|
||||
/// random_bits_count,
|
||||
/// size as u64,
|
||||
/// );
|
||||
///
|
||||
/// // Decrypt:
|
||||
/// let dec_result: i64 = cks.decrypt_signed(&ct_res);
|
||||
/// assert!(dec_result >= 0);
|
||||
/// assert!(dec_result < (1 << random_bits_count));
|
||||
/// ```
|
||||
pub fn par_generate_oblivious_pseudo_random_signed_integer_bounded(
|
||||
&self,
|
||||
seed: Seed,
|
||||
random_bits_count: u64,
|
||||
num_blocks: u64,
|
||||
) -> SignedRadixCiphertext {
|
||||
assert!(self.message_modulus().0.is_power_of_two());
|
||||
let range_log_size = self.message_modulus().0.ilog2() as u64 * num_blocks;
|
||||
|
||||
#[allow(clippy::int_plus_one)]
|
||||
{
|
||||
assert!(
|
||||
random_bits_count + 1 <= range_log_size,
|
||||
"The range asked for a random value (=[0, 2^{}[) does not fit in the available range [-2^{}, 2^{}[",
|
||||
@@ -156,30 +265,22 @@ impl ServerKey {
|
||||
.map(|(i, seed)| {
|
||||
let i = i as u64;
|
||||
|
||||
match randomizer {
|
||||
SignedRandomizationSpec::Unsigned { random_bits_count } => {
|
||||
if i * message_bits_count < random_bits_count {
|
||||
// if we generate 5 bits of noise in n blocks of 2 bits, the third (i=2)
|
||||
// block must have only one bit of random
|
||||
if random_bits_count < (i + 1) * message_bits_count {
|
||||
let top_message_bits_count =
|
||||
random_bits_count - i * message_bits_count;
|
||||
if i * message_bits_count < random_bits_count {
|
||||
// if we generate 5 bits of noise in n blocks of 2 bits, the third (i=2)
|
||||
// block must have only one bit of random
|
||||
if random_bits_count < (i + 1) * message_bits_count {
|
||||
let top_message_bits_count = random_bits_count - i * message_bits_count;
|
||||
|
||||
assert!(top_message_bits_count <= message_bits_count);
|
||||
assert!(top_message_bits_count <= message_bits_count);
|
||||
|
||||
self.key
|
||||
.generate_oblivious_pseudo_random(seed, top_message_bits_count)
|
||||
} else {
|
||||
self.key
|
||||
.generate_oblivious_pseudo_random(seed, message_bits_count)
|
||||
}
|
||||
} else {
|
||||
self.key.create_trivial(0)
|
||||
}
|
||||
self.key
|
||||
.generate_oblivious_pseudo_random(seed, top_message_bits_count)
|
||||
} else {
|
||||
self.key
|
||||
.generate_oblivious_pseudo_random(seed, message_bits_count)
|
||||
}
|
||||
SignedRandomizationSpec::FullSigned => self
|
||||
.key
|
||||
.generate_oblivious_pseudo_random(seed, message_bits_count),
|
||||
} else {
|
||||
self.key.create_trivial(0)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -191,7 +292,6 @@ impl ServerKey {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
|
||||
use crate::integer::oprf::SignedRandomizationSpec;
|
||||
use crate::shortint::oprf::test::test_uniformity;
|
||||
use concrete_csprng::seeders::Seed;
|
||||
|
||||
@@ -214,7 +314,7 @@ pub(crate) mod test {
|
||||
};
|
||||
|
||||
test_uniformity(1 << random_bits_count, &|seed| {
|
||||
let img = sk.par_generate_oblivious_pseudo_random_unsigned_integer(
|
||||
let img = sk.par_generate_oblivious_pseudo_random_unsigned_integer_bounded(
|
||||
Seed(seed as u128),
|
||||
random_bits_count,
|
||||
num_blocks as u64,
|
||||
@@ -223,9 +323,9 @@ pub(crate) mod test {
|
||||
});
|
||||
|
||||
test_uniformity(1 << random_bits_count, &|seed| {
|
||||
let img = sk.par_generate_oblivious_pseudo_random_signed_integer(
|
||||
let img = sk.par_generate_oblivious_pseudo_random_signed_integer_bounded(
|
||||
Seed(seed as u128),
|
||||
SignedRandomizationSpec::Unsigned { random_bits_count },
|
||||
random_bits_count,
|
||||
num_blocks as u64,
|
||||
);
|
||||
let result = ck.decrypt_signed::<i64>(&img);
|
||||
@@ -238,7 +338,6 @@ pub(crate) mod test {
|
||||
test_uniformity(1 << (2 * num_blocks), &|seed| {
|
||||
let img = sk.par_generate_oblivious_pseudo_random_signed_integer(
|
||||
Seed(seed as u128),
|
||||
SignedRandomizationSpec::FullSigned,
|
||||
num_blocks as u64,
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user