Skip to content

Commit

Permalink
refactor(all): refactor oprf integer and hl APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
mayeul-zama committed Aug 22, 2024
1 parent e62ec8c commit ba4e4bf
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 123 deletions.
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
33 changes: 15 additions & 18 deletions tfhe/docs/fundamentals/encrypted-prf.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,45 @@ This document explains the mechanism and steps to generate an oblivious encrypte

The goal is to give to the server the possibility to generate a random value, which will be obtained in a encrypted format and will remain unknown to the server. The implementation is based on [this article](https://eprint.iacr.org/2024/665).

In *TFHE-rs*, this is possible through the method `generate_oblivious_pseudo_random` of `FheUint` and `FheInt`.
In *TFHE-rs*, this is possible through the function `generate_oblivious_pseudo_random` of `FheUint` and `FheInt`.
It relies on the use of the usual server keys.
The method takes two inputs: a seed `Seed`, which could be any `u128` value and an upper bound on the range of the random values `random_bits_count`.
The function takes two inputs: a seed `Seed`, which could be any `u128` value and an upper bound on the range of the random values `random_bits_count`.
It outputs a `FheIntN` or `FheUintN`, where `N` is the number of bits of the homomorphic integer.
The output is reproducible, i.e., the method is deterministic from the inputs: assuming the same hardware, seed and server key, this method outputs the same random encrypted value.
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.

More in details, these are the possibilities depending on the output type and the on the specification used:
- a `FheUint_N` taken uniformly in `[0; 2^random_bits_count[` for any `random_bits_count <= N`
- a `FheInt_N` taken uniformly in `[0; 2^random_bits_count[` for any `random_bits_count <= N - 1`
- a `FheInt_N` taken uniformly in its full range ([-2^N; 2^N[).
There are two functions :
- `generate_oblivious_pseudo_random` which return an integer taken uniformly inthe full integer range (`[0; 2^N[` for a `FheUint_N` and `[-2^(N-1); 2^(N-1)[` for a `FheInt_N`).
- `generate_oblivious_pseudo_random_bounded` which return an integer taken uniformly in `[0; 2^random_bits_count[`. For a `FheUint_N`, we must have `random_bits_count <= N`. For a `FheInt_N`, we must have `random_bits_count <= N - 1`.

Here is an example of the usage:


```rust
use tfhe::prelude::FheDecrypt;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, FheInt8, SignedRandomizationSpec, Seed};
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, FheInt8, Seed};

pub fn main() {
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 random_bits_count = 3;

// You can pass a 128 bits Seed here
let ct_res = FheUint8::generate_oblivious_pseudo_random(Seed(0), random_bits_count);
// 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_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), SignedRandomizationSpec::Unsigned { random_bits_count });

let ct_res = FheInt8::generate_oblivious_pseudo_random(Seed(0));
let dec_result: i8 = ct_res.decrypt(&client_key);
assert!(dec_result < (1 << random_bits_count));


let ct_res = FheInt8::generate_oblivious_pseudo_random(Seed(0), SignedRandomizationSpec::FullSigned);

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
108 changes: 87 additions & 21 deletions tfhe/src/high_level_api/integers/oprf.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,47 @@
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 {
let ct = global_state::with_internal_keys(|key| match key {
InternalServerKey::Cpu(key) => key
.key
.par_generate_oblivious_pseudo_random_unsigned_integer(
seed,
Id::num_blocks(key.message_modulus()) as u64,
),
#[cfg(feature = "gpu")]
InternalServerKey::Cuda(_) => {
todo!("Cuda devices do not yet support oblivious pseudo random generation")
}
});

Self::new(ct)
}
/// 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,16 +54,16 @@ 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 {
let ct = global_state::with_internal_keys(|key| match key {
InternalServerKey::Cpu(key) => key
.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 @@ -46,38 +79,31 @@ 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 {
let ct = global_state::with_internal_keys(|key| match key {
InternalServerKey::Cpu(key) => {
key.key.par_generate_oblivious_pseudo_random_signed_integer(
seed,
randomizer,
Id::num_blocks(key.message_modulus()) as u64,
)
}
Expand All @@ -89,4 +115,44 @@ impl<Id: FheIntId> FheInt<Id> {

Self::new(ct)
}

/// 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 {
let ct = global_state::with_internal_keys(|key| match key {
InternalServerKey::Cpu(key) => key
.key
.par_generate_oblivious_pseudo_random_signed_integer_bounded(
seed,
random_bits_count,
Id::num_blocks(key.message_modulus()) as u64,
),
#[cfg(feature = "gpu")]
InternalServerKey::Cuda(_) => {
todo!("Cuda devices do not yet support oblivious pseudo random generation")
}
});

Self::new(ct)
}
}
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

0 comments on commit ba4e4bf

Please sign in to comment.