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 Sep 11, 2024
1 parent 2a4026c commit 93ff699
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 111 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
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));

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

0 comments on commit 93ff699

Please sign in to comment.