Skip to content

Commit

Permalink
chore(all)!: use a builder pattern for safe serialization API
Browse files Browse the repository at this point in the history
BREAKING CHANGES:
- `safe_serialize` and `safe_deserialize` are replaced by
  `SerializationConfig::serialize_into` and
  `DeserializationConfig::deserialize_from`.
- C API: the `XXX_safe_serialize_versioned` is deprecated, `XXX_safe_serialize`
  is now versioned by default
- JS API: the `safe_serialize` method now versionize the data before
  serialization.

This is *NOT* a serialization breaking change for data serialized in previous
versions with `safe_serialize_versioned`.
  • Loading branch information
nsarlin-zama committed Sep 30, 2024
1 parent 53c4850 commit e9d3e21
Show file tree
Hide file tree
Showing 20 changed files with 1,041 additions and 767 deletions.
51 changes: 1 addition & 50 deletions tfhe/c_api_tests/test_high_level_integers.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,54 +399,7 @@ int uint8_safe_serialization(const ClientKey *client_key, const ServerKey *serve
deser_view.pointer = value_buffer.pointer;
deser_view.length = value_buffer.length;
ok = fhe_uint8_safe_deserialize_conformant(deser_view, max_serialization_size, server_key,
&deserialized_lhs);
assert(ok == 0);

uint8_t clear;
ok = fhe_uint8_decrypt(deserialized_lhs, deserialized_client_key, &clear);
assert(ok == 0);

assert(clear == lhs_clear);

if (value_buffer.pointer != NULL) {
destroy_dynamic_buffer(&value_buffer);
}
fhe_uint8_destroy(lhs);
fhe_uint8_destroy(deserialized_lhs);
return ok;
}

int uint8_safe_serialization_versioned(const ClientKey *client_key, const ServerKey *server_key) {
int ok;
FheUint8 *lhs = NULL;
FheUint8 *deserialized_lhs = NULL;
DynamicBuffer value_buffer = {.pointer = NULL, .length = 0, .destructor = NULL};
DynamicBuffer cks_buffer = {.pointer = NULL, .length = 0, .destructor = NULL};
DynamicBufferView deser_view = {.pointer = NULL, .length = 0};
ClientKey *deserialized_client_key = NULL;

const uint64_t max_serialization_size = UINT64_C(1) << UINT64_C(20);

uint8_t lhs_clear = 123;

ok = client_key_serialize(client_key, &cks_buffer);
assert(ok == 0);

deser_view.pointer = cks_buffer.pointer;
deser_view.length = cks_buffer.length;
ok = client_key_deserialize(deser_view, &deserialized_client_key);
assert(ok == 0);

ok = fhe_uint8_try_encrypt_with_client_key_u8(lhs_clear, client_key, &lhs);
assert(ok == 0);

ok = fhe_uint8_safe_serialize_versioned(lhs, &value_buffer, max_serialization_size);
assert(ok == 0);

deser_view.pointer = value_buffer.pointer;
deser_view.length = value_buffer.length;
ok = fhe_uint8_safe_deserialize_conformant_versioned(deser_view, max_serialization_size,
server_key, &deserialized_lhs);
&deserialized_lhs);
assert(ok == 0);

uint8_t clear;
Expand Down Expand Up @@ -657,8 +610,6 @@ int main(void) {
assert(ok == 0);
ok = uint8_safe_serialization(client_key, server_key);
assert(ok == 0);
ok = uint8_safe_serialization_versioned(client_key, server_key);
assert(ok == 0);
ok = uint8_compressed(client_key);
assert(ok == 0);

Expand Down
36 changes: 14 additions & 22 deletions tfhe/docs/fundamentals/serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ When dealing with sensitive types, it's important to implement safe serializatio
The safe deserialization must take the output of a safe-serialization as input. During the process, the following validation occurs:

* **Type match**: deserializing `type A` from a serialized `type B` raises an error indicating "On deserialization, expected type A, got type B".
* **Version compatibility**: deserializing `type A` of a newer version (for example, version 0.2) from a serialized `type A` of an older version (for example, version 0.1) raises an error indicating "On deserialization, expected serialization version 0.2, got version 0.1".
* **Version compatibility**: data serialized in previous versions of **TFHE-rs** are automatically upgraded to the latest version using the [data versioning](../guides/data\_versioning.md) feature.
* **Parameter compatibility**: deserializing an object of `type A` with one set of crypto parameters from an object of `type A` with another set of crypto parameters raises an error indicating "Deserialized object of type A not conformant with given parameter set"
* If both parameter sets have the same LWE dimension for ciphertexts, a ciphertext from param 1 may not fail this deserialization check with param 2.
* This check can't distinguish ciphertexts/server keys from independent client keys with the same parameters.
Expand All @@ -97,7 +97,7 @@ Here is an example:

use tfhe::conformance::ParameterSetConformant;
use tfhe::prelude::*;
use tfhe::safe_deserialization::{safe_deserialize_conformant, safe_serialize};
use tfhe::safe_serialization::{SerializationConfig, DeserializationConfig};
use tfhe::shortint::parameters::{PARAM_MESSAGE_2_CARRY_2_KS_PBS, PARAM_MESSAGE_2_CARRY_2_PBS_KS};
use tfhe::conformance::ListSizeConstraint;
use tfhe::{
Expand Down Expand Up @@ -127,19 +127,15 @@ fn main() {

let mut buffer = vec![];

safe_serialize(&ct, &mut buffer, 1 << 40).unwrap();
SerializationConfig::new(1 << 20).serialize_into(&ct, &mut buffer).unwrap();

assert!(safe_deserialize_conformant::<FheUint8>(
buffer.as_slice(),
1 << 20,
&conformance_params_2
).is_err());

let ct2 = safe_deserialize_conformant::<FheUint8>(
buffer.as_slice(),
1 << 20,
&conformance_params_1
).unwrap();
assert!(DeserializationConfig::new(1 << 20)
.deserialize_from::<FheUint8>(buffer.as_slice(), &conformance_params_2)
.is_err());

let ct2 = DeserializationConfig::new(1 << 20)
.deserialize_from::<FheUint8>(buffer.as_slice(), &conformance_params_1)
.unwrap();

let dec: u8 = ct2.decrypt(&client_key);
assert_eq!(msg, dec);
Expand All @@ -152,18 +148,14 @@ fn main() {
let compact_list = builder.build();

let mut buffer = vec![];
safe_serialize(&compact_list, &mut buffer, 1 << 40).unwrap();
SerializationConfig::new(1 << 20).serialize_into(&compact_list, &mut buffer).unwrap();

let conformance_params = CompactCiphertextListConformanceParams {
shortint_params: params_1.to_shortint_conformance_param(),
num_elements_constraint: ListSizeConstraint::exact_size(2),
};
assert!(safe_deserialize_conformant::<CompactCiphertextList>(
buffer.as_slice(),
1 << 20,
&conformance_params
).is_ok());
DeserializationConfig::new(1 << 20)
.deserialize_from::<CompactCiphertextList>(buffer.as_slice(), &conformance_params)
.unwrap();
}
```

You can combine this serialization/deserialization feature with the [data versioning](../guides/data\_versioning.md) feature by using the `safe_serialize_versioned` and `safe_deserialize_conformant_versioned` functions.
8 changes: 2 additions & 6 deletions tfhe/src/c_api/high_level_api/booleans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ impl_destroy_on_type!(FheBool);
impl_clone_on_type!(FheBool);
impl_serialize_deserialize_on_type!(FheBool);
impl_safe_serialize_on_type!(FheBool);
impl_safe_serialize_versioned_on_type!(FheBool);
impl_safe_deserialize_conformant_integer!(FheBool, FheBoolConformanceParams);
impl_safe_deserialize_conformant_versioned_integer!(FheBool, FheBoolConformanceParams);
impl_safe_deserialize_conformant_on_type!(FheBool, FheBoolConformanceParams);

impl_binary_fn_on_type!(FheBool => bitand, bitor, bitxor);
impl_binary_assign_fn_on_type!(FheBool => bitand_assign, bitor_assign, bitxor_assign);
Expand Down Expand Up @@ -48,9 +46,7 @@ impl_destroy_on_type!(CompressedFheBool);
impl_clone_on_type!(CompressedFheBool);
impl_serialize_deserialize_on_type!(CompressedFheBool);
impl_safe_serialize_on_type!(CompressedFheBool);
impl_safe_serialize_versioned_on_type!(CompressedFheBool);
impl_safe_deserialize_conformant_integer!(CompressedFheBool, FheBoolConformanceParams);
impl_safe_deserialize_conformant_versioned_integer!(CompressedFheBool, FheBoolConformanceParams);
impl_safe_deserialize_conformant_on_type!(CompressedFheBool, FheBoolConformanceParams);
impl_try_encrypt_with_client_key_on_type!(CompressedFheBool{crate::high_level_api::CompressedFheBool}, bool);

#[no_mangle]
Expand Down
14 changes: 2 additions & 12 deletions tfhe/src/c_api/high_level_api/integers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,8 @@ macro_rules! create_integer_wrapper_type {

impl_safe_serialize_on_type!($name);

impl_safe_serialize_versioned_on_type!($name);

::paste::paste! {
impl_safe_deserialize_conformant_integer!($name, [<$name ConformanceParams>]);
}

::paste::paste! {
impl_safe_deserialize_conformant_versioned_integer!($name, [<$name ConformanceParams>]);
impl_safe_deserialize_conformant_on_type!($name, [<$name ConformanceParams>]);
}

define_all_cast_into_for_integer_type!($name);
Expand All @@ -332,11 +326,7 @@ macro_rules! create_integer_wrapper_type {

impl_safe_serialize_on_type!([<Compressed $name>]);

impl_safe_serialize_versioned_on_type!([<Compressed $name>]);

impl_safe_deserialize_conformant_integer!([<Compressed $name>], [<$name ConformanceParams>]);

impl_safe_deserialize_conformant_versioned_integer!([<Compressed $name>], [<$name ConformanceParams>]);
impl_safe_deserialize_conformant_on_type!([<Compressed $name>], [<$name ConformanceParams>]);

#[no_mangle]
pub unsafe extern "C" fn [<compressed_ $name:snake _decompress>](
Expand Down
13 changes: 13 additions & 0 deletions tfhe/src/c_api/high_level_api/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ impl_serialize_deserialize_on_type!(CompressedCompactPublicKey);
impl_serialize_deserialize_on_type!(ServerKey);
impl_serialize_deserialize_on_type!(CompressedServerKey);

impl_safe_serialize_on_type!(ClientKey);
impl_safe_serialize_on_type!(PublicKey);
impl_safe_serialize_on_type!(CompactPublicKey);
impl_safe_serialize_on_type!(CompressedCompactPublicKey);
impl_safe_serialize_on_type!(ServerKey);
impl_safe_serialize_on_type!(CompressedServerKey);

impl_safe_deserialize_on_type!(ClientKey);
impl_safe_deserialize_on_type!(PublicKey);
impl_safe_deserialize_on_type!(CompactPublicKey);
impl_safe_deserialize_on_type!(CompressedCompactPublicKey);
impl_safe_deserialize_on_type!(CompressedServerKey);

#[no_mangle]
pub unsafe extern "C" fn generate_keys(
config: *mut super::config::Config,
Expand Down
Loading

0 comments on commit e9d3e21

Please sign in to comment.