Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(all): refactor oprf integer and hl APIs #1448

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions tfhe/benches/integer/oprf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
);
})
});

Expand Down
6 changes: 3 additions & 3 deletions tfhe/c_api_tests/test_high_level_integers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand Down
36 changes: 30 additions & 6 deletions tfhe/docs/fundamentals/encrypted-prf.md
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decryption of ct_res ?

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));
}
```
32 changes: 13 additions & 19 deletions tfhe/src/c_api/high_level_api/integers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,15 +491,13 @@ 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();
let seed = crate::Seed((seed_high_bytes << 64) | seed_low_bytes);

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)));
})
Expand All @@ -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,
Expand All @@ -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)));
})
}
Expand All @@ -532,48 +530,44 @@ 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>](
pub unsafe extern "C" fn [<generate_oblivious_pseudo_random_ $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
},
);
let result = crate::FheInt::generate_oblivious_pseudo_random(
seed,
);
*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_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(
seed,
crate::high_level_api::SignedRandomizationSpec::FullSigned,
);
let result =
crate::FheInt::generate_oblivious_pseudo_random_bounded(
seed,
random_bits_count,
);
*out_result = Box::into_raw(Box::new($name(result)));
})
}
Expand Down
112 changes: 91 additions & 21 deletions tfhe/src/high_level_api/integers/oprf.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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,
Expand All @@ -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())
Expand All @@ -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")
}
})
}
}
1 change: 0 additions & 1 deletion tfhe/src/high_level_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
Loading
Loading