refactor(tfhe): Allow CompactCiphertextList to store heterogeneous types

This refactors the integer's CompactCiphertextList to allow storing
unsigned, signed (without necessarily the same number of blocks) and
booleans in a single comapct list.

This is better as its more flexible and allows for better compression
by not forcing to use a list per data type. This is especially
interessing with zero-knowledge proofs as they are expensive to compute.

This also adds the ability to pack integer blocks by using the carry
space, but makes the expansion require a ServerKey to split blocks
via PBS.

BREAKING CHANGE: expand method from CompactCiphertextList returns a
                 CiphertextExpander
BREAKING CHANGE: Removes 'typed' CompactList and Compact types from the hlapi
                 (e.g. CompactFheUintList/CompactFheUintX)
This commit is contained in:
tmontaigu
2024-06-05 16:41:51 +02:00
parent 301537a81b
commit 3f9c1b0ca6
47 changed files with 2432 additions and 4642 deletions

View File

@@ -0,0 +1,129 @@
#include "tfhe.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
int main(void) {
int ok = 0;
// First, we create a ClientKey and a CompactPublicKey
ClientKey *client_key = NULL;
CompactPublicKey *public_key = NULL;
{
ConfigBuilder *builder;
Config *config;
ok = config_builder_default(&builder);
assert(ok == 0);
ok = config_builder_build(builder, &config);
assert(ok == 0);
ok = client_key_generate(config, &client_key);
assert(ok == 0);
ok = compact_public_key_new(client_key, &public_key);
assert(ok == 0);
}
// Then, we create the compact list
CompactCiphertextList *compact_list = NULL;
{
CompactCiphertextListBuilder *builder;
ok = compact_ciphertext_list_builder_new(public_key, &builder);
assert(ok == 0);
// Push some values
ok = compact_ciphertext_list_builder_push_u32(builder, 38382);
assert(ok == 0);
ok = compact_ciphertext_list_builder_push_i64(builder, -1);
assert(ok == 0);
ok = compact_ciphertext_list_builder_push_bool(builder, true);
assert(ok == 0);
ok = compact_ciphertext_list_builder_push_u2(builder, 3);
assert(ok == 0);
ok = compact_ciphertext_list_builder_build(builder, &compact_list);
assert(ok == 0);
// Don't forget to destroy the builder
compact_ciphertext_list_builder_destroy(builder);
}
// Now we can expand values
FheUint32 *a = NULL;
FheInt64 *b = NULL;
FheBool *c = NULL;
FheUint2 *d = NULL;
{
FheTypes type = Type_FheBool;
CompactCiphertextListExpander *expander = NULL;
ok = compact_ciphertext_list_expand(compact_list, &expander);
assert(ok == 0);
size_t len = 0;
ok = compact_ciphertext_list_expander_len(expander, &len);
assert(ok == 0 && len == 4);
// First, an example of getting the type in a slot
ok = compact_ciphertext_list_expander_get_kind_of(expander, 0, &type);
assert(ok == 0 && type == Type_FheUint32);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 1, &type);
assert(ok == 0 && type == Type_FheInt64);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 2, &type);
assert(ok == 0 && type == Type_FheBool);
ok = compact_ciphertext_list_expander_get_kind_of(expander, 3, &type);
assert(ok == 0 && type == Type_FheUint2);
// Then how to get the values
ok = compact_ciphertext_list_expander_get_fhe_uint32(expander, 0, &a);
assert(ok == 0);
ok = compact_ciphertext_list_expander_get_fhe_int64(expander, 1, &b);
assert(ok == 0);
ok = compact_ciphertext_list_expander_get_fhe_bool(expander, 2, &c);
assert(ok == 0);
ok = compact_ciphertext_list_expander_get_fhe_uint2(expander, 3, &d);
assert(ok == 0);
// Don't forget to destroy the expander
compact_ciphertext_list_expander_destroy(expander);
}
uint32_t clear_a = 0;
ok = fhe_uint32_decrypt(a, client_key, &clear_a);
assert(ok == 0);
assert(clear_a == 38382);
int64_t clear_b = 0;
ok = fhe_int64_decrypt(b, client_key, &clear_b);
assert(ok == 0);
assert(clear_b == -1);
bool clear_c = false;
ok = fhe_bool_decrypt(c, client_key, &clear_c);
assert(ok == 0);
assert(clear_c == true);
uint8_t clear_d = 0;
ok = fhe_uint2_decrypt(d, client_key, &clear_d);
assert(ok == 0);
assert(clear_d == 3);
fhe_uint32_destroy(a);
fhe_int64_destroy(b);
fhe_bool_destroy(c);
fhe_uint2_destroy(d);
client_key_destroy(client_key);
compact_public_key_destroy(public_key);
return EXIT_SUCCESS;
}

View File

@@ -85,7 +85,6 @@ int uint256_compact_public_key(const ClientKey *client_key,
FheUint256 *lhs = NULL;
FheUint256 *rhs = NULL;
FheUint256 *result = NULL;
CompactFheUint256List *list = NULL;
U256 result_clear = {0};
U256 clears[2] = {{5, 6, 7, 8}, {1, 2, 3, 4}};
@@ -93,44 +92,6 @@ int uint256_compact_public_key(const ClientKey *client_key,
ok = compressed_compact_public_key_decompress(compressed_public_key, &public_key);
assert(ok == 0);
// Compact list example
{
ok = compact_fhe_uint256_list_try_encrypt_with_compact_public_key_u256(&clears[0], 2,
public_key, &list);
assert(ok == 0);
size_t len = 0;
ok = compact_fhe_uint256_list_len(list, &len);
assert(ok == 0);
assert(len == 2);
FheUint256 *expand_output[2] = {NULL};
ok = compact_fhe_uint256_list_expand(list, &expand_output[0], 2);
assert(ok == 0);
// transfer ownership
lhs = expand_output[0];
rhs = expand_output[1];
// We can destroy the compact list
// The expanded ciphertext are independent from it
compact_fhe_uint256_list_destroy(list);
ok = fhe_uint256_sub(lhs, rhs, &result);
assert(ok == 0);
ok = fhe_uint256_decrypt(result, client_key, &result_clear);
assert(ok == 0);
assert(result_clear.w0 == 4);
assert(result_clear.w1 == 4);
assert(result_clear.w2 == 4);
assert(result_clear.w3 == 4);
fhe_uint256_destroy(lhs);
fhe_uint256_destroy(rhs);
fhe_uint256_destroy(result);
}
{
ok = fhe_uint256_try_encrypt_with_compact_public_key_u256(clears[0], public_key, &lhs);
assert(ok == 0);
@@ -165,7 +126,6 @@ int int32_compact_public_key(const ClientKey *client_key,
FheInt32 *lhs = NULL;
FheInt32 *rhs = NULL;
FheInt32 *result = NULL;
CompactFheInt32List *list = NULL;
int32_t result_clear = 0;
int32_t clears[2] = {-9482394, 98712234};
@@ -173,41 +133,6 @@ int int32_compact_public_key(const ClientKey *client_key,
ok = compressed_compact_public_key_decompress(compressed_public_key, &public_key);
assert(ok == 0);
// Compact list example
{
ok = compact_fhe_int32_list_try_encrypt_with_compact_public_key_i32(&clears[0], 2, public_key,
&list);
assert(ok == 0);
size_t len = 0;
ok = compact_fhe_int32_list_len(list, &len);
assert(ok == 0);
assert(len == 2);
FheInt32 *expand_output[2] = {NULL};
ok = compact_fhe_int32_list_expand(list, &expand_output[0], 2);
assert(ok == 0);
// transfer ownership
lhs = expand_output[0];
rhs = expand_output[1];
// We can destroy the compact list
// The expanded ciphertext are independent from it
compact_fhe_int32_list_destroy(list);
ok = fhe_int32_sub(lhs, rhs, &result);
assert(ok == 0);
ok = fhe_int32_decrypt(result, client_key, &result_clear);
assert(ok == 0);
assert(result_clear == clears[0] - clears[1]);
fhe_int32_destroy(lhs);
fhe_int32_destroy(rhs);
fhe_int32_destroy(result);
}
{
ok = fhe_int32_try_encrypt_with_compact_public_key_i32(clears[0], public_key, &lhs);
assert(ok == 0);

View File

@@ -371,9 +371,8 @@ int uint8_public_key(const ClientKey *client_key, const PublicKey *public_key) {
int uint8_safe_serialization(const ClientKey *client_key, const ServerKey *server_key) {
int ok;
CompactFheUint8 *lhs = NULL;
CompactFheUint8 *deserialized_lhs = NULL;
CompactFheUint8 *result = NULL;
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};
@@ -391,30 +390,20 @@ int uint8_safe_serialization(const ClientKey *client_key, const ServerKey *serve
ok = client_key_deserialize(deser_view, &deserialized_client_key);
assert(ok == 0);
struct CompactPublicKey *public_key;
ok = compact_public_key_new(deserialized_client_key, &public_key);
ok = fhe_uint8_try_encrypt_with_client_key_u8(lhs_clear, client_key, &lhs);
assert(ok == 0);
ok = compact_fhe_uint8_try_encrypt_with_compact_public_key_u8(lhs_clear, public_key, &lhs);
assert(ok == 0);
ok = compact_fhe_uint8_safe_serialize(lhs, &value_buffer, max_serialization_size);
ok = fhe_uint8_safe_serialize(lhs, &value_buffer, max_serialization_size);
assert(ok == 0);
deser_view.pointer = value_buffer.pointer;
deser_view.length = value_buffer.length;
ok = compact_fhe_uint8_safe_deserialize_conformant(deser_view, max_serialization_size, server_key,
&deserialized_lhs);
assert(ok == 0);
FheUint8 *expanded = NULL;
ok = compact_fhe_uint8_expand(deserialized_lhs, &expanded);
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(expanded, deserialized_client_key, &clear);
ok = fhe_uint8_decrypt(deserialized_lhs, deserialized_client_key, &clear);
assert(ok == 0);
assert(clear == lhs_clear);
@@ -422,11 +411,8 @@ int uint8_safe_serialization(const ClientKey *client_key, const ServerKey *serve
if (value_buffer.pointer != NULL) {
destroy_dynamic_buffer(&value_buffer);
}
compact_fhe_uint8_destroy(lhs);
compact_fhe_uint8_destroy(deserialized_lhs);
compact_fhe_uint8_destroy(result);
fhe_uint8_destroy(expanded);
fhe_uint8_destroy(lhs);
fhe_uint8_destroy(deserialized_lhs);
return ok;
}
@@ -434,7 +420,6 @@ int uint8_serialization(const ClientKey *client_key) {
int ok;
FheUint8 *lhs = NULL;
FheUint8 *deserialized_lhs = NULL;
FheUint8 *result = 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};
@@ -472,7 +457,6 @@ int uint8_serialization(const ClientKey *client_key) {
}
fhe_uint8_destroy(lhs);
fhe_uint8_destroy(deserialized_lhs);
fhe_uint8_destroy(result);
return ok;
}
@@ -498,7 +482,6 @@ int uint8_compressed(const ClientKey *client_key) {
fhe_uint8_destroy(lhs);
compressed_fhe_uint8_destroy(clhs);
fhe_uint8_destroy(result);
return ok;
}

View File

@@ -25,7 +25,7 @@ int main(void) {
// Compute the CRS
// Note that we do that before generating the client key
// as client_key_generate thakes ownership of the config
// as client_key_generate takes ownership of the config
CompactPkeCrs *crs;
size_t max_num_bits = 32;
status = compact_pke_crs_from_config(config, max_num_bits, &crs);
@@ -44,64 +44,89 @@ int main(void) {
status = compact_public_key_new(client_key, &pk);
assert(status == 0);
// Demo of ProvenCompactFheUint32
// Then, we create the compact list
ProvenCompactCiphertextList *compact_list = NULL;
{
uint32_t msg = 8328937;
ProvenCompactFheUint32 *proven_fhe_uint;
status = proven_compact_fhe_uint32_try_encrypt(msg, public_params, pk, ZkComputeLoadProof,
&proven_fhe_uint);
CompactCiphertextListBuilder *builder;
status = compact_ciphertext_list_builder_new(pk, &builder);
assert(status == 0);
FheUint32 *fhe_uint;
// This function does not take ownership of the proven fhe uint, so we have to cleanup later
status =
proven_compact_fhe_uint32_verify_and_expand(proven_fhe_uint, public_params, pk, &fhe_uint);
// Push some values
status = compact_ciphertext_list_builder_push_u32(builder, 38382);
assert(status == 0);
uint32_t decrypted;
status = fhe_uint32_decrypt(fhe_uint, client_key, &decrypted);
status = compact_ciphertext_list_builder_push_i64(builder, -1);
assert(status == 0);
assert(decrypted == msg);
fhe_uint32_destroy(fhe_uint);
proven_compact_fhe_uint32_destroy(proven_fhe_uint);
status = compact_ciphertext_list_builder_push_bool(builder, true);
assert(status == 0);
status = compact_ciphertext_list_builder_push_u2(builder, 3);
assert(status == 0);
status = compact_ciphertext_list_builder_build_with_proof(builder, public_params,
ZkComputeLoadProof, &compact_list);
assert(status == 0);
// Don't forget to destroy the builder
compact_ciphertext_list_builder_destroy(builder);
}
// Demo of ProvenCompactFheUint32List
// Now we can expand values
FheUint32 *a = NULL;
FheInt64 *b = NULL;
FheBool *c = NULL;
FheUint2 *d = NULL;
{
uint32_t msgs[4] = {8328937, 217521191, 2753219039, 91099540};
ProvenCompactFheUint32List *proven_fhe_list;
status = proven_compact_fhe_uint32_list_try_encrypt(msgs, 4, public_params, pk,
ZkComputeLoadProof, &proven_fhe_list);
CompactCiphertextListExpander *expander = NULL;
status = proven_compact_ciphertext_list_verify_and_expand(compact_list, public_params, pk,
&expander);
assert(status == 0);
size_t list_len;
status = proven_compact_fhe_uint32_list_len(proven_fhe_list, &list_len);
assert(status == 0);
assert(list_len == 4);
FheUint32 *fhe_uints[4];
// This function does not take ownership of the proven fhe uint, so we have to cleanup later
status = proven_compact_fhe_uint32_list_verify_and_expand(proven_fhe_list, public_params, pk,
&fhe_uints[0], 4);
status = compact_ciphertext_list_expander_get_fhe_uint32(expander, 0, &a);
assert(status == 0);
for (size_t i = 0; i < 4; ++i) {
uint32_t decrypted;
status = fhe_uint32_decrypt(fhe_uints[i], client_key, &decrypted);
assert(status == 0);
status = compact_ciphertext_list_expander_get_fhe_int64(expander, 1, &b);
assert(status == 0);
assert(decrypted == msgs[i]);
fhe_uint32_destroy(fhe_uints[i]);
}
status = compact_ciphertext_list_expander_get_fhe_bool(expander, 2, &c);
assert(status == 0);
proven_compact_fhe_uint32_list_destroy(proven_fhe_list);
status = compact_ciphertext_list_expander_get_fhe_uint2(expander, 3, &d);
assert(status == 0);
// Don't forget to destroy the expander
compact_ciphertext_list_expander_destroy(expander);
}
uint32_t clear_a = 0;
status = fhe_uint32_decrypt(a, client_key, &clear_a);
assert(status == 0);
assert(clear_a == 38382);
int64_t clear_b = 0;
status = fhe_int64_decrypt(b, client_key, &clear_b);
assert(status == 0);
assert(clear_b == -1);
bool clear_c = false;
status = fhe_bool_decrypt(c, client_key, &clear_c);
assert(status == 0);
assert(clear_c == true);
uint8_t clear_d = 0;
status = fhe_uint2_decrypt(d, client_key, &clear_d);
assert(status == 0);
assert(clear_d == 3);
fhe_uint32_destroy(a);
fhe_int64_destroy(b);
fhe_bool_destroy(c);
fhe_uint2_destroy(d);
client_key_destroy(client_key);
compact_public_key_destroy(pk);
compact_pke_public_params_destroy(public_params);
compact_pke_crs_destroy(crs);
compact_public_key_destroy(pk);
client_key_destroy(client_key);
return EXIT_SUCCESS;
}

View File

@@ -103,8 +103,8 @@ use tfhe::safe_deserialization::{safe_deserialize_conformant, safe_serialize};
use tfhe::shortint::parameters::{PARAM_MESSAGE_2_CARRY_2_KS_PBS, PARAM_MESSAGE_2_CARRY_2_PBS_KS};
use tfhe::conformance::ListSizeConstraint;
use tfhe::{
generate_keys, CompactFheUint8, CompactFheUint8List, FheUint8ConformanceParams,
CompactFheUint8ListConformanceParams, CompactPublicKey, ConfigBuilder
generate_keys, FheUint8, CompactCiphertextList, FheUint8ConformanceParams,
CompactPublicKey, ConfigBuilder, CompactCiphertextListConformanceParams
};
fn main() {
@@ -124,7 +124,7 @@ fn main() {
let msg = 27u8;
let ct = CompactFheUint8::try_encrypt(msg, &public_key).unwrap();
let ct = FheUint8::try_encrypt(msg, &client_key).unwrap();
assert!(ct.is_conformant(&conformance_params_1));
assert!(!ct.is_conformant(&conformance_params_2));
@@ -133,31 +133,36 @@ fn main() {
safe_serialize(&ct, &mut buffer, 1 << 40).unwrap();
assert!(safe_deserialize_conformant::<CompactFheUint8>(
assert!(safe_deserialize_conformant::<FheUint8>(
buffer.as_slice(),
1 << 20,
&conformance_params_2
).is_err());
let ct2 = safe_deserialize_conformant::<CompactFheUint8>(
let ct2 = safe_deserialize_conformant::<FheUint8>(
buffer.as_slice(),
1 << 20,
&conformance_params_1
).unwrap();
let dec: u8 = ct2.expand().decrypt(&client_key);
let dec: u8 = ct2.decrypt(&client_key);
assert_eq!(msg, dec);
// Example with a compact list:
let msgs = [27, 188u8];
let compact_list = CompactFheUint8List::try_encrypt(&msgs, &public_key).unwrap();
let mut builder = CompactCiphertextList::builder(&public_key);
builder.extend(msgs.iter().copied());
let compact_list = builder.build();
let mut buffer = vec![];
safe_serialize(&compact_list, &mut buffer, 1 << 40).unwrap();
let conformance_params = CompactFheUint8ListConformanceParams::from((&server_key, ListSizeConstraint::exact_size(2)));
assert!(safe_deserialize_conformant::<CompactFheUint8List>(
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

View File

@@ -32,27 +32,20 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let clear_a = rng.gen::<u64>();
let clear_b = rng.gen::<u64>();
let a = tfhe::ProvenCompactFheUint64::try_encrypt(
clear_a,
public_zk_params,
&public_key,
ZkComputeLoad::Proof,
)?;
let b = tfhe::ProvenCompactFheUint64::try_encrypt(
clear_b,
public_zk_params,
&public_key,
ZkComputeLoad::Proof,
)?;
let proven_compact_list = tfhe::ProvenCompactCiphertextList::builder(&public_key)
.push(clear_a)
.push(clear_b)
.build_with_proof(public_zk_params, ZkComputeLoad::Proof)?;
// Server side
let result = {
set_server_key(server_key);
// Verify the ciphertexts
let a = a.verify_and_expand(&public_zk_params, &public_key)?;
let b = b.verify_and_expand(&public_zk_params, &public_key)?;
let mut expander = proven_compact_list.verify_and_expand(&public_zk_params, &public_key)?;
let a: tfhe::FheUint64 = expander.get(0).unwrap()?;
let b: tfhe::FheUint64 = expander.get(1).unwrap()?;
a + b
};

View File

@@ -14,7 +14,7 @@ use tfhe::shortint::parameters::classic::compact_pk::{
};
use tfhe::shortint::PBSParameters;
use tfhe::{
generate_keys, CompactFheUint256List, CompactFheUint32List, CompactPublicKey, ConfigBuilder,
generate_keys, CompactCiphertextList, CompactPublicKey, ConfigBuilder, FheUint256, FheUint32,
};
fn write_result(file: &mut File, name: &str, value: usize) {
@@ -70,7 +70,9 @@ pub fn cpk_and_cctl_sizes(results_file: &Path) {
let vec_inputs: Vec<_> = (0..NB_CTXT).map(|_| rng.gen::<u32>()).collect();
let encrypted_inputs = CompactFheUint32List::encrypt(&vec_inputs, &public_key);
let encrypted_inputs = CompactCiphertextList::builder(&public_key)
.extend(vec_inputs.iter().copied())
.build();
let cctl_size = bincode::serialize(&encrypted_inputs).unwrap().len();
println!("Compact CT list for {NB_CTXT} CTs: {} bytes", cctl_size);
@@ -86,15 +88,12 @@ pub fn cpk_and_cctl_sizes(results_file: &Path) {
vec![],
);
let expanded_inputs = encrypted_inputs.expand();
vec_inputs
.iter()
.zip(expanded_inputs.iter())
.for_each(|(&input, ct)| {
let clear: u32 = ct.decrypt(&client_key);
assert_eq!(clear, input);
});
let expander = encrypted_inputs.expand().unwrap();
for (i, input) in vec_inputs.into_iter().enumerate() {
let expanded: FheUint32 = expander.get(i).unwrap().unwrap();
let clear: u32 = expanded.decrypt(&client_key);
assert_eq!(clear, input);
}
}
{
@@ -129,7 +128,9 @@ pub fn cpk_and_cctl_sizes(results_file: &Path) {
let vec_inputs: Vec<_> = (0..NB_CTXT).map(|_| rng.gen::<u32>()).collect();
let encrypted_inputs = CompactFheUint32List::encrypt(&vec_inputs, &public_key);
let encrypted_inputs = CompactCiphertextList::builder(&public_key)
.extend(vec_inputs.iter().copied())
.build();
let cctl_size = bincode::serialize(&encrypted_inputs).unwrap().len();
println!("Compact CT list for {NB_CTXT} CTs: {} bytes", cctl_size);
@@ -145,15 +146,12 @@ pub fn cpk_and_cctl_sizes(results_file: &Path) {
vec![],
);
let expanded_inputs = encrypted_inputs.expand();
vec_inputs
.iter()
.zip(expanded_inputs.iter())
.for_each(|(&input, ct)| {
let clear: u32 = ct.decrypt(&client_key);
assert_eq!(clear, input);
});
let expander = encrypted_inputs.expand().unwrap();
for (i, input) in vec_inputs.into_iter().enumerate() {
let expanded: FheUint32 = expander.get(i).unwrap().unwrap();
let clear: u32 = expanded.decrypt(&client_key);
assert_eq!(clear, input);
}
}
// 256 bits
@@ -177,9 +175,11 @@ pub fn cpk_and_cctl_sizes(results_file: &Path) {
let test_name = format!("hlapi_sizes_{}_cctl_{NB_CTXT}_len_256_bits", params.name());
let vec_inputs: Vec<_> = (0..NB_CTXT).map(|_| rng.gen::<u32>()).collect();
let vec_inputs: Vec<_> = (0..NB_CTXT).map(|_| U256::from(rng.gen::<u32>())).collect();
let encrypted_inputs = CompactFheUint256List::encrypt(&vec_inputs, &public_key);
let encrypted_inputs = CompactCiphertextList::builder(&public_key)
.extend(vec_inputs.iter().copied())
.build();
let cctl_size = bincode::serialize(&encrypted_inputs).unwrap().len();
println!("Compact CT list for {NB_CTXT} CTs: {} bytes", cctl_size);
@@ -195,15 +195,12 @@ pub fn cpk_and_cctl_sizes(results_file: &Path) {
vec![],
);
let expanded_inputs = encrypted_inputs.expand();
vec_inputs
.iter()
.zip(expanded_inputs.iter())
.for_each(|(&input, ct)| {
let clear: U256 = ct.decrypt(&client_key);
assert_eq!(clear, U256::from(input));
});
let expander = encrypted_inputs.expand().unwrap();
for (i, input) in vec_inputs.into_iter().enumerate() {
let expanded: FheUint256 = expander.get(i).unwrap().unwrap();
let clear: U256 = expanded.decrypt(&client_key);
assert_eq!(clear, input);
}
}
{
@@ -226,9 +223,11 @@ pub fn cpk_and_cctl_sizes(results_file: &Path) {
let test_name = format!("hlapi_sizes_{}_cctl_{NB_CTXT}_len_256_bits", params.name());
let vec_inputs: Vec<_> = (0..NB_CTXT).map(|_| rng.gen::<u32>()).collect();
let vec_inputs: Vec<_> = (0..NB_CTXT).map(|_| U256::from(rng.gen::<u32>())).collect();
let encrypted_inputs = CompactFheUint256List::encrypt(&vec_inputs, &public_key);
let encrypted_inputs = CompactCiphertextList::builder(&public_key)
.extend(vec_inputs.iter().copied())
.build();
let cctl_size = bincode::serialize(&encrypted_inputs).unwrap().len();
println!("Compact CT list for {NB_CTXT} CTs: {} bytes", cctl_size);
@@ -244,15 +243,12 @@ pub fn cpk_and_cctl_sizes(results_file: &Path) {
vec![],
);
let expanded_inputs = encrypted_inputs.expand();
vec_inputs
.iter()
.zip(expanded_inputs.iter())
.for_each(|(&input, ct)| {
let clear: U256 = ct.decrypt(&client_key);
assert_eq!(clear, U256::from(input));
});
let expander = encrypted_inputs.expand().unwrap();
for (i, input) in vec_inputs.into_iter().enumerate() {
let expanded: FheUint256 = expander.get(i).unwrap().unwrap();
let clear: U256 = expanded.decrypt(&client_key);
assert_eq!(clear, input);
}
}
}

View File

@@ -9,21 +9,21 @@ const {
TfhePublicKey,
TfheCompressedPublicKey,
TfheCompactPublicKey,
TfheCompressedServerKey,
TfheConfigBuilder,
CompressedFheInt8,
FheInt8,
FheInt32,
CompactFheInt32,
CompactFheInt32List,
CompressedFheInt128,
FheInt128,
CompressedFheInt256,
CompactFheInt256,
CompactFheInt256List,
FheInt256
FheInt256,
CompactCiphertextList,
ProvenCompactCiphertextList,
} = require("../pkg/tfhe.js");
const {CompactPkeCrs, ZkComputeLoad} = require("../pkg");
const {
randomBytes,
} = require('node:crypto');
const I256_MIN = BigInt("-57896044618658097711785492504343953926634992332820282019728792003956564819968");
const I256_MAX = BigInt("28948022309329048855892746252171976963317496166410141009864396001978282409983");
@@ -394,216 +394,108 @@ test('hlapi_compact_public_key_encrypt_decrypt_int32_small_single', (t) => {
hlapi_compact_public_key_encrypt_decrypt_int32_single(config);
});
function hlapi_compact_public_key_encrypt_decrypt_int32_single_compact(config) {
function generateRandomBigInt(bitLength) {
const bytesNeeded = Math.ceil(bitLength / 8);
const randomBytesBuffer = randomBytes(bytesNeeded);
// Convert random bytes to BigInt
const randomBigInt = BigInt(`0x${randomBytesBuffer.toString('hex')}`);
return randomBigInt;
}
test('hlapi_compact_ciphertext_list', (t) => {
let config = TfheConfigBuilder.default()
.build();
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let compact_encrypted = CompactFheInt32.encrypt_with_compact_public_key(I32_MIN, publicKey);
let encrypted = compact_encrypted.expand();
let decrypted = encrypted.decrypt(clientKey);
assert.deepStrictEqual(decrypted, I32_MIN);
let clear_u2 = 3;
let clear_i32 = -3284;
let clear_bool = true;
let clear_u256 = generateRandomBigInt(256);
let serialized = compact_encrypted.serialize();
let deserialized = CompactFheInt32.deserialize(serialized);
let deserialized_decrypted = deserialized.expand().decrypt(clientKey);
assert.deepStrictEqual(deserialized_decrypted, I32_MIN);
let builder = CompactCiphertextList.builder(publicKey);
builder.push_u2(clear_u2);
builder.push_i32(clear_i32);
builder.push_boolean(clear_bool);
builder.push_u256(clear_u256);
let list = builder.build();
let safe_serialized = compact_encrypted.safe_serialize(BigInt(10000000));
let safe_deserialized = CompactFheInt32.safe_deserialize(safe_serialized, BigInt(10000000));
let safe_deserialized_decrypted = safe_deserialized.expand().decrypt(clientKey);
assert.deepStrictEqual(safe_deserialized_decrypted, I32_MIN);
}
let serialized = list.safe_serialize(BigInt(10000000));
let deserialized = CompactCiphertextList.safe_deserialize(serialized, BigInt(10000000));
test('hlapi_compact_public_key_encrypt_decrypt_int32_small_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let expander = deserialized.expand();
assert.deepStrictEqual(
expander.get_uint2(0).decrypt(clientKey),
clear_u2,
);
assert.deepStrictEqual(
expander.get_int32(1).decrypt(clientKey),
clear_i32,
);
assert.deepStrictEqual(
expander.get_bool(2).decrypt(clientKey),
clear_bool,
);
assert.deepStrictEqual(
expander.get_uint256(3).decrypt(clientKey),
clear_u256,
);
});
test('hlapi_compact_ciphertext_list_with_proof', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int32_single_compact(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_int32_big_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int32_single_compact(config);
});
function hlapi_compact_public_key_encrypt_decrypt_int32_list_compact(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let values = [0, 1, 2394, I32_MIN];
let crs = CompactPkeCrs.from_parameters(block_params, 2 + 32 + 1 + 256);
let public_params = crs.public_params();
let compact_list = CompactFheInt32List.encrypt_with_compact_public_key(values, publicKey);
let clear_u2 = 3;
let clear_i32 = -3284;
let clear_bool = true;
let clear_u256 = generateRandomBigInt(256);
{
let encrypted_list = compact_list.expand();
let builder = CompactCiphertextList.builder(publicKey);
builder.push_u2(clear_u2);
builder.push_i32(clear_i32);
builder.push_boolean(clear_bool);
builder.push_u256(clear_u256);
let list = builder.build_with_proof(public_params, ZkComputeLoad.Proof);
assert.deepStrictEqual(encrypted_list.length, values.length);
let serialized = list.safe_serialize(BigInt(10000000));
let deserialized = ProvenCompactCiphertextList.safe_deserialize(serialized, BigInt(10000000));
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}
let expander = deserialized.verify_and_expand(public_params, publicKey);
let serialized_list = compact_list.serialize();
let deserialized_list = CompactFheInt32List.deserialize(serialized_list);
let encrypted_list = deserialized_list.expand();
assert.deepStrictEqual(encrypted_list.length, values.length);
assert.deepStrictEqual(
expander.get_uint2(0).decrypt(clientKey),
clear_u2,
);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}
assert.deepStrictEqual(
expander.get_int32(1).decrypt(clientKey),
clear_i32,
);
test('hlapi_compact_public_key_encrypt_decrypt_int32_small_list_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
assert.deepStrictEqual(
expander.get_bool(2).decrypt(clientKey),
clear_bool,
);
hlapi_compact_public_key_encrypt_decrypt_int32_list_compact(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_int32_big_list_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int32_list_compact(config);
});
//////////////////////////////////////////////////////////////////////////////
/// 256 bits compact
//////////////////////////////////////////////////////////////////////////////
function hlapi_compact_public_key_encrypt_decrypt_int256_single(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let encrypted = FheInt256.encrypt_with_compact_public_key(I256_MIN, publicKey);
let decrypted = encrypted.decrypt(clientKey);
assert.deepStrictEqual(decrypted, I256_MIN);
let serialized = encrypted.serialize();
let deserialized = FheInt256.deserialize(serialized);
let deserialized_decrypted = deserialized.decrypt(clientKey);
assert.deepStrictEqual(deserialized_decrypted, I256_MIN);
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
let safe_deserialized = FheInt256.safe_deserialize(safe_serialized, BigInt(10000000));
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
assert.deepStrictEqual(safe_deserialized_decrypted, I256_MIN);
}
test('hlapi_compact_public_key_encrypt_decrypt_int256_big_single', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int256_single(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_int256_small_single', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int256_single(config);
});
function hlapi_compact_public_key_encrypt_decrypt_int256_single_compact(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let compact_encrypted = CompactFheInt256.encrypt_with_compact_public_key(I256_MIN, publicKey);
let encrypted = compact_encrypted.expand();
let decrypted = encrypted.decrypt(clientKey);
assert.deepStrictEqual(decrypted, I256_MIN);
let serialized = compact_encrypted.serialize();
let deserialized = CompactFheInt256.deserialize(serialized);
let deserialized_decrypted = deserialized.expand().decrypt(clientKey);
assert.deepStrictEqual(deserialized_decrypted, I256_MIN);
let safe_serialized = compact_encrypted.safe_serialize(BigInt(10000000));
let safe_deserialized = CompactFheInt256.safe_deserialize(safe_serialized, BigInt(10000000));
let safe_deserialized_decrypted = safe_deserialized.expand().decrypt(clientKey);
assert.deepStrictEqual(safe_deserialized_decrypted, I256_MIN);
}
test('hlapi_compact_public_key_encrypt_decrypt_int256_small_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int256_single_compact(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_int256_big_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int256_single_compact(config);
});
function hlapi_compact_public_key_encrypt_decrypt_int256_list_compact(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let values = [BigInt(0), BigInt(1), BigInt(2394), BigInt(-2309840239), BigInt(I32_MIN), I256_MIN, I128_MIN];
let compact_list = CompactFheInt256List.encrypt_with_compact_public_key(values, publicKey);
{
let encrypted_list = compact_list.expand();
assert.deepStrictEqual(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}
let serialized_list = compact_list.serialize();
let deserialized_list = CompactFheInt256List.deserialize(serialized_list);
let encrypted_list = deserialized_list.expand();
assert.deepStrictEqual(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}
test('hlapi_compact_public_key_encrypt_decrypt_int256_small_list_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int256_list_compact(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_int256_big_list_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_int256_list_compact(config);
assert.deepStrictEqual(
expander.get_uint256(3).decrypt(clientKey),
clear_u256,
);
});

View File

@@ -3,29 +3,17 @@ const assert = require('node:assert').strict;
const {performance} = require('perf_hooks');
const {
init_panic_hook,
Shortint,
ShortintParametersName,
ShortintParameters,
TfheClientKey,
TfhePublicKey,
TfheCompressedPublicKey,
TfheCompactPublicKey,
TfheCompressedServerKey,
TfheConfigBuilder,
CompressedFheUint8,
FheUint8,
FheUint32,
CompactFheUint32,
CompactFheUint32List,
CompressedFheUint128,
FheUint128,
CompressedFheUint256,
CompactFheUint256,
CompactFheUint256List,
ProvenCompactFheUint64,
ProvenCompactFheUint64List,
CompactPkeCrs,
ZkComputeLoad,
FheUint256
} = require("../pkg/tfhe.js");
const {
@@ -390,305 +378,3 @@ test('hlapi_public_key_encrypt_decrypt_uint256_small', (t) => {
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
});
//////////////////////////////////////////////////////////////////////////////
/// 32 bits compact
//////////////////////////////////////////////////////////////////////////////
function hlapi_compact_public_key_encrypt_decrypt_uint32_single(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let encrypted = FheUint32.encrypt_with_compact_public_key(U32_MAX, publicKey);
let decrypted = encrypted.decrypt(clientKey);
assert.deepStrictEqual(decrypted, U32_MAX);
let serialized = encrypted.serialize();
let deserialized = FheUint32.deserialize(serialized);
let deserialized_decrypted = deserialized.decrypt(clientKey);
assert.deepStrictEqual(deserialized_decrypted, U32_MAX);
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
let safe_deserialized = FheUint32.safe_deserialize(safe_serialized, BigInt(10000000));
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
assert.deepStrictEqual(safe_deserialized_decrypted, U32_MAX);
}
test('hlapi_compact_public_key_encrypt_decrypt_uint32_big_single', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint32_single(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_uint32_small_single', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint32_single(config);
});
function hlapi_compact_public_key_encrypt_decrypt_uint32_single_compact(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let compact_encrypted = CompactFheUint32.encrypt_with_compact_public_key(U32_MAX, publicKey);
let encrypted = compact_encrypted.expand();
let decrypted = encrypted.decrypt(clientKey);
assert.deepStrictEqual(decrypted, U32_MAX);
let serialized = compact_encrypted.serialize();
let deserialized = CompactFheUint32.deserialize(serialized);
let deserialized_decrypted = deserialized.expand().decrypt(clientKey);
assert.deepStrictEqual(deserialized_decrypted, U32_MAX);
let safe_serialized = compact_encrypted.safe_serialize(BigInt(10000000));
let safe_deserialized = CompactFheUint32.safe_deserialize(safe_serialized, BigInt(10000000));
let safe_deserialized_decrypted = safe_deserialized.expand().decrypt(clientKey);
assert.deepStrictEqual(safe_deserialized_decrypted, U32_MAX);
}
test('hlapi_compact_public_key_encrypt_decrypt_uint32_small_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint32_single_compact(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_uint32_big_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint32_single_compact(config);
});
function hlapi_compact_public_key_encrypt_decrypt_uint32_list_compact(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let values = [0, 1, 2394, U32_MAX];
let compact_list = CompactFheUint32List.encrypt_with_compact_public_key(values, publicKey);
{
let encrypted_list = compact_list.expand();
assert.deepStrictEqual(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}
let serialized_list = compact_list.serialize();
let deserialized_list = CompactFheUint32List.deserialize(serialized_list);
let encrypted_list = deserialized_list.expand();
assert.deepStrictEqual(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}
test('hlapi_compact_public_key_encrypt_decrypt_uint32_small_list_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint32_list_compact(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_uint32_big_list_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint32_list_compact(config);
});
//////////////////////////////////////////////////////////////////////////////
/// 256 bits compact
//////////////////////////////////////////////////////////////////////////////
function hlapi_compact_public_key_encrypt_decrypt_uint256_single(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let encrypted = FheUint256.encrypt_with_compact_public_key(U256_MAX, publicKey);
let decrypted = encrypted.decrypt(clientKey);
assert.deepStrictEqual(decrypted, U256_MAX);
let serialized = encrypted.serialize();
let deserialized = FheUint256.deserialize(serialized);
let deserialized_decrypted = deserialized.decrypt(clientKey);
assert.deepStrictEqual(deserialized_decrypted, U256_MAX);
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
let safe_deserialized = FheUint256.safe_deserialize(safe_serialized, BigInt(10000000));
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
}
test('hlapi_compact_public_key_encrypt_decrypt_uint256_big_single', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint256_single(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_uint256_small_single', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint256_single(config);
});
function hlapi_compact_public_key_encrypt_decrypt_uint256_single_compact(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let compact_encrypted = CompactFheUint256.encrypt_with_compact_public_key(U256_MAX, publicKey);
let encrypted = compact_encrypted.expand();
let decrypted = encrypted.decrypt(clientKey);
assert.deepStrictEqual(decrypted, U256_MAX);
let serialized = compact_encrypted.serialize();
let deserialized = CompactFheUint256.deserialize(serialized);
let deserialized_decrypted = deserialized.expand().decrypt(clientKey);
assert.deepStrictEqual(deserialized_decrypted, U256_MAX);
let safe_serialized = compact_encrypted.safe_serialize(BigInt(10000000));
let safe_deserialized = CompactFheUint256.safe_deserialize(safe_serialized, BigInt(10000000));
let safe_deserialized_decrypted = safe_deserialized.expand().decrypt(clientKey);
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
}
test('hlapi_compact_public_key_encrypt_decrypt_uint256_small_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint256_single_compact(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_uint256_big_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint256_single_compact(config);
});
function hlapi_compact_public_key_encrypt_decrypt_uint256_list_compact(config) {
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let values = [BigInt(0), BigInt(1), BigInt(2394), BigInt(2309840239), BigInt(U32_MAX), U256_MAX, U128_MAX];
let compact_list = CompactFheUint256List.encrypt_with_compact_public_key(values, publicKey);
{
let encrypted_list = compact_list.expand();
assert.deepStrictEqual(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}
let serialized_list = compact_list.serialize();
let deserialized_list = CompactFheUint256List.deserialize(serialized_list);
let encrypted_list = deserialized_list.expand();
assert.deepStrictEqual(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}
test('hlapi_compact_public_key_encrypt_decrypt_uint256_small_list_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint256_list_compact(config);
});
test('hlapi_compact_public_key_encrypt_decrypt_uint256_big_list_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint256_list_compact(config);
});
function generateRandomBigInt(bitLength) {
const bytesNeeded = Math.ceil(bitLength / 8);
const randomBytesBuffer = randomBytes(bytesNeeded);
// Convert random bytes to BigInt
const randomBigInt = BigInt(`0x${randomBytesBuffer.toString('hex')}`);
return randomBigInt;
}
test('hlapi_compact_public_key_encrypt_and_prove_compact_uint256', (t) => {
let block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let crs = CompactPkeCrs.from_parameters(block_params, 128);
let public_params = crs.public_params();
{
let input = generateRandomBigInt(64)
let encrypted = ProvenCompactFheUint64.encrypt_with_compact_public_key(
input, public_params, publicKey, ZkComputeLoad.Proof);
assert.deepStrictEqual(encrypted.verifies(public_params, publicKey), true);
let expanded = encrypted.verify_and_expand(public_params, publicKey);
let decrypted = expanded.decrypt(clientKey);
assert.deepStrictEqual(decrypted, input);
}
{
let inputs = [generateRandomBigInt(64), generateRandomBigInt(64), generateRandomBigInt(64), generateRandomBigInt(64)];
let encrypted = ProvenCompactFheUint64List.encrypt_with_compact_public_key(
inputs, public_params, publicKey, ZkComputeLoad.Proof);
assert.deepStrictEqual(encrypted.verifies(public_params, publicKey), true);
let expanded_list = encrypted.verify_and_expand(public_params, publicKey);
for (let i = 0; i < inputs.length; i++) {
let decrypted = expanded_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, inputs[i]);
}
}
});

View File

@@ -1,5 +1,4 @@
use super::utils::*;
use crate::c_api::utils::check_ptr_is_non_null_and_aligned;
use crate::high_level_api::prelude::*;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
@@ -76,185 +75,3 @@ pub unsafe extern "C" fn fhe_bool_compress(
*result = Box::into_raw(Box::new(CompressedFheBool(compressed_inner)));
})
}
pub struct CompactFheBool(crate::high_level_api::CompactFheBool);
impl_destroy_on_type!(CompactFheBool);
impl_clone_on_type!(CompactFheBool);
impl_serialize_deserialize_on_type!(CompactFheBool);
impl_safe_serialize_on_type!(CompactFheBool);
impl_safe_deserialize_conformant_integer!(CompactFheBool, FheBoolConformanceParams);
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheBool{crate::high_level_api::CompactFheBool}, bool);
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_bool_expand(
sself: *const CompactFheBool,
result: *mut *mut FheBool,
) -> std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
let compact = crate::c_api::utils::get_ref_checked(sself).unwrap();
let expanded = compact.0.expand();
*result = Box::into_raw(Box::new(FheBool(expanded)));
})
}
pub struct CompactFheBoolList(crate::high_level_api::CompactFheBoolList);
impl_destroy_on_type!(CompactFheBoolList);
impl_clone_on_type!(CompactFheBoolList);
impl_serialize_deserialize_on_type!(CompactFheBoolList);
impl_safe_serialize_on_type!(CompactFheBoolList);
impl_safe_deserialize_conformant_integer_list!(CompactFheBoolList);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheBoolList{crate::high_level_api::CompactFheBoolList}, bool);
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_bool_list_len(
sself: *const CompactFheBoolList,
result: *mut usize,
) -> ::std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
let list = crate::c_api::utils::get_ref_checked(sself).unwrap();
*result = list.0.len();
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_bool_list_expand(
sself: *const CompactFheBoolList,
output: *mut *mut FheBool,
output_len: usize,
) -> std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
check_ptr_is_non_null_and_aligned(output).unwrap();
let list = crate::c_api::utils::get_ref_checked(sself).unwrap();
let expanded = list.0.expand();
let num_to_take = output_len.max(list.0.len());
let iter = expanded.into_iter().take(num_to_take).enumerate();
for (i, fhe_uint) in iter {
let ptr = output.wrapping_add(i);
*ptr = Box::into_raw(Box::new(FheBool(fhe_uint)));
}
})
}
#[cfg(feature = "zk-pok-experimental")]
mod zk {
use crate::c_api::high_level_api::utils::{
impl_clone_on_type, impl_destroy_on_type, impl_safe_serialize_on_type,
impl_serialize_deserialize_on_type,
};
use std::ffi::c_int;
pub struct ProvenCompactFheBool(crate::high_level_api::ProvenCompactFheBool);
impl_destroy_on_type!(ProvenCompactFheBool);
impl_clone_on_type!(ProvenCompactFheBool);
impl_serialize_deserialize_on_type!(ProvenCompactFheBool);
impl_safe_serialize_on_type!(ProvenCompactFheBool);
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_try_encrypt(
message: bool,
public_params: &crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &crate::c_api::high_level_api::keys::CompactPublicKey,
compute_load: crate::c_api::high_level_api::zk::ZkComputeLoad,
out_result: *mut *mut ProvenCompactFheBool,
) -> c_int {
crate::c_api::utils::catch_panic(|| {
let result = crate::high_level_api::ProvenCompactFheBool::try_encrypt(
message,
&public_params.0,
&pk.0,
compute_load.into(),
)
.unwrap();
*out_result = Box::into_raw(Box::new(ProvenCompactFheBool(result)));
})
}
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_verify_and_expand(
ct: *const ProvenCompactFheBool,
public_params: &crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &crate::c_api::high_level_api::keys::CompactPublicKey,
out_result: *mut *mut super::FheBool,
) -> c_int {
crate::c_api::utils::catch_panic(|| {
let ct = crate::c_api::utils::get_ref_checked(ct).unwrap();
let result =
ct.0.clone()
.verify_and_expand(&public_params.0, &pk.0)
.unwrap();
*out_result = Box::into_raw(Box::new(super::FheBool(result)));
})
}
pub struct ProvenCompactFheBoolList(crate::high_level_api::ProvenCompactFheBoolList);
impl_destroy_on_type!(ProvenCompactFheBoolList);
impl_clone_on_type!(ProvenCompactFheBoolList);
impl_serialize_deserialize_on_type!(ProvenCompactFheBoolList);
impl_safe_serialize_on_type!(ProvenCompactFheBoolList);
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_list_try_encrypt(
input: *const bool,
input_len: usize,
public_params: &crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &crate::c_api::high_level_api::keys::CompactPublicKey,
compute_load: crate::c_api::high_level_api::zk::ZkComputeLoad,
out_result: *mut *mut ProvenCompactFheBoolList,
) -> ::std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
let messages = std::slice::from_raw_parts(input, input_len);
let result = crate::high_level_api::ProvenCompactFheBoolList::try_encrypt(
messages,
&public_params.0,
&pk.0,
compute_load.into(),
)
.unwrap();
*out_result = Box::into_raw(Box::new(ProvenCompactFheBoolList(result)));
})
}
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_list_len(
sself: *const ProvenCompactFheBoolList,
result: *mut usize,
) -> ::std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
let list = crate::c_api::utils::get_ref_checked(sself).unwrap();
*result = list.0.len();
})
}
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_list_verify_and_expand(
list: &ProvenCompactFheBoolList,
public_params: &crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &crate::c_api::high_level_api::keys::CompactPublicKey,
output: *mut *mut super::FheBool,
output_len: usize,
) -> ::std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
let expanded = list.0.verify_and_expand(&public_params.0, &pk.0).unwrap();
let num_to_take = output_len.max(list.0.len());
let iter = expanded.into_iter().take(num_to_take).enumerate();
for (i, fhe_uint) in iter {
let ptr = output.wrapping_add(i);
*ptr = Box::into_raw(Box::new(super::FheBool(fhe_uint)));
}
})
}
}

View File

@@ -0,0 +1,308 @@
use crate::c_api::high_level_api::booleans::FheBool;
use crate::c_api::high_level_api::i128::I128;
use crate::c_api::high_level_api::i256::I256;
use crate::c_api::high_level_api::integers::{
FheInt10, FheInt12, FheInt128, FheInt14, FheInt16, FheInt160, FheInt2, FheInt256, FheInt32,
FheInt4, FheInt6, FheInt64, FheInt8, FheUint10, FheUint12, FheUint128, FheUint14, FheUint16,
FheUint160, FheUint2, FheUint256, FheUint32, FheUint4, FheUint6, FheUint64, FheUint8,
};
use crate::c_api::high_level_api::keys::CompactPublicKey;
use crate::c_api::high_level_api::u128::U128;
use crate::c_api::high_level_api::u256::U256;
use crate::c_api::high_level_api::utils::{
impl_destroy_on_type, impl_serialize_deserialize_on_type, CApiIntegerType,
};
#[cfg(feature = "zk-pok-experimental")]
use crate::c_api::high_level_api::zk::{CompactPkePublicParams, ZkComputeLoad};
use crate::c_api::utils::{catch_panic, get_mut_checked, get_ref_checked};
use std::ffi::c_int;
pub struct CompactCiphertextListBuilder(crate::high_level_api::CompactCiphertextListBuilder);
impl_destroy_on_type!(CompactCiphertextListBuilder);
pub struct CompactCiphertextList(crate::high_level_api::CompactCiphertextList);
impl_destroy_on_type!(CompactCiphertextList);
impl_serialize_deserialize_on_type!(CompactCiphertextList);
#[cfg(feature = "zk-pok-experimental")]
pub struct ProvenCompactCiphertextList(crate::high_level_api::ProvenCompactCiphertextList);
#[cfg(feature = "zk-pok-experimental")]
impl_destroy_on_type!(ProvenCompactCiphertextList);
#[cfg(feature = "zk-pok-experimental")]
impl_serialize_deserialize_on_type!(ProvenCompactCiphertextList);
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_builder_new(
compact_public_key: *const CompactPublicKey,
builder: *mut *mut CompactCiphertextListBuilder,
) -> c_int {
catch_panic(|| {
let pk = get_ref_checked(compact_public_key).unwrap();
let inner = crate::high_level_api::CompactCiphertextListBuilder::new(&pk.0);
*builder = Box::into_raw(Box::new(CompactCiphertextListBuilder(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_builder_build(
builder: *const CompactCiphertextListBuilder,
list: *mut *mut CompactCiphertextList,
) -> c_int {
catch_panic(|| {
let builder = get_ref_checked(builder).unwrap();
let inner = builder.0.build();
*list = Box::into_raw(Box::new(CompactCiphertextList(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_builder_build_packed(
builder: *const CompactCiphertextListBuilder,
list: *mut *mut CompactCiphertextList,
) -> c_int {
catch_panic(|| {
let builder = get_ref_checked(builder).unwrap();
let inner = builder.0.build_packed();
*list = Box::into_raw(Box::new(CompactCiphertextList(inner)));
})
}
#[cfg(feature = "zk-pok-experimental")]
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_builder_build_with_proof(
builder: *const CompactCiphertextListBuilder,
public_params: *const CompactPkePublicParams,
compute_load: ZkComputeLoad,
list: *mut *mut ProvenCompactCiphertextList,
) -> c_int {
catch_panic(|| {
let builder = get_ref_checked(builder).unwrap();
let public_params = get_ref_checked(public_params).unwrap();
let inner = builder
.0
.build_with_proof(&public_params.0, compute_load.into())
.unwrap();
*list = Box::into_raw(Box::new(ProvenCompactCiphertextList(inner)));
})
}
#[cfg(feature = "zk-pok-experimental")]
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_builder_build_with_proof_packed(
builder: *const CompactCiphertextListBuilder,
public_params: *const CompactPkePublicParams,
compute_load: ZkComputeLoad,
list: *mut *mut ProvenCompactCiphertextList,
) -> c_int {
catch_panic(|| {
let builder = get_ref_checked(builder).unwrap();
let public_params = get_ref_checked(public_params).unwrap();
let inner = builder
.0
.build_with_proof_packed(&public_params.0, compute_load.into())
.unwrap();
*list = Box::into_raw(Box::new(ProvenCompactCiphertextList(inner)));
})
}
/// Pushes a boolean into the list
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_builder_push_bool(
builder: *mut CompactCiphertextListBuilder,
value: bool,
) -> c_int {
catch_panic(|| {
let builder = get_mut_checked(builder).unwrap();
builder.0.push(value);
})
}
macro_rules! define_compact_ciphertext_list_builder_push_method {
(
unsigned: $($num_bits:literal: $rust_ty:ty),* $(,)?
) => {
::paste::paste!{
$(
#[doc = concat!("Pushes an unsigned integer of ", stringify!($num_bits), " bits to the list")]
#[no_mangle]
pub unsafe extern "C" fn [<compact_ciphertext_list_builder_push_u $num_bits>](
builder: *mut CompactCiphertextListBuilder,
value: $rust_ty,
) -> c_int {
catch_panic(|| {
let builder = get_mut_checked(builder).unwrap();
builder.0.push_with_num_bits(value.to_rust(), $num_bits).unwrap();
})
}
)*
}
};
(
signed: $($num_bits:literal: $rust_ty:ty),* $(,)?
) => {
::paste::paste!{
$(
#[doc = concat!("Pushes a signed integer of ", stringify!($num_bits), " bits to the list")]
#[no_mangle]
pub unsafe extern "C" fn [<compact_ciphertext_list_builder_push_i $num_bits>](
builder: *mut CompactCiphertextListBuilder,
value: $rust_ty,
) -> c_int {
catch_panic(|| {
let builder = get_mut_checked(builder).unwrap();
builder.0.push_with_num_bits(value.to_rust(), $num_bits).unwrap();
})
}
)*
}
};
}
define_compact_ciphertext_list_builder_push_method!(
unsigned: 2: u8, 4: u8, 6: u8, 8: u8, 10: u16, 12: u16, 14: u16, 16: u16, 32: u32, 64: u64, 128: U128, 160: U256, 256: U256
);
define_compact_ciphertext_list_builder_push_method!(
signed: 2: i8, 4: i8, 6: i8, 8: i8, 10: i16, 12: i16, 14: i16, 16: i16, 32: i32, 64: i64, 128: I128, 160: I256, 256: I256
);
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_expand(
compact_list: *const CompactCiphertextList,
expander: *mut *mut CompactCiphertextListExpander,
) -> c_int {
catch_panic(|| {
let list = get_ref_checked(compact_list).unwrap();
let inner = list.0.expand().unwrap();
*expander = Box::into_raw(Box::new(CompactCiphertextListExpander(inner)));
})
}
#[cfg(feature = "zk-pok-experimental")]
#[no_mangle]
pub unsafe extern "C" fn proven_compact_ciphertext_list_verify_and_expand(
compact_list: *const ProvenCompactCiphertextList,
public_params: *const CompactPkePublicParams,
public_key: *const CompactPublicKey,
expander: *mut *mut CompactCiphertextListExpander,
) -> c_int {
catch_panic(|| {
let list = get_ref_checked(compact_list).unwrap();
let public_params = get_ref_checked(public_params).unwrap();
let public_key = get_ref_checked(public_key).unwrap();
let inner = list
.0
.verify_and_expand(&public_params.0, &public_key.0)
.unwrap();
*expander = Box::into_raw(Box::new(CompactCiphertextListExpander(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_expander_len(
expander: *mut CompactCiphertextListExpander,
out: *mut usize,
) -> c_int {
catch_panic(|| {
let expander = get_ref_checked(expander).unwrap();
*out = expander.0.len();
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_expander_get_kind_of(
expander: *mut CompactCiphertextListExpander,
index: usize,
out: *mut super::FheTypes,
) -> c_int {
let mut result = None;
catch_panic(|| {
let expander = get_ref_checked(expander).unwrap();
result = expander.0.get_kind_of(index);
});
result.map_or(1, |r| {
*out = r.into();
0
})
}
macro_rules! define_compact_ciphertext_list_expander_get {
(
unsigned: $($num_bits:literal),*
$(,)?
) => {
::paste::paste!(
$(
#[no_mangle]
pub unsafe extern "C" fn [<compact_ciphertext_list_expander_get_fhe_uint $num_bits>](
expander: *mut CompactCiphertextListExpander,
index: usize,
out: *mut *mut [<FheUint $num_bits>],
) -> c_int {
catch_panic(|| {
let expander = get_mut_checked(expander).unwrap();
let inner = expander.0.get(index).unwrap().unwrap();
*out = Box::into_raw(Box::new([<FheUint $num_bits>](inner)));
})
}
)*
);
};
(
signed: $($num_bits:literal),*
$(,)?
) => {
::paste::paste!(
$(
#[no_mangle]
pub unsafe extern "C" fn [<compact_ciphertext_list_expander_get_fhe_int $num_bits>](
expander: *mut CompactCiphertextListExpander,
index: usize,
out: *mut *mut [<FheInt $num_bits>],
) -> c_int {
catch_panic(|| {
let expander = get_mut_checked(expander).unwrap();
let inner = expander.0.get(index).unwrap().unwrap();
*out = Box::into_raw(Box::new([<FheInt $num_bits>](inner)));
})
}
)*
);
}
}
pub struct CompactCiphertextListExpander(crate::high_level_api::CompactCiphertextListExpander);
impl_destroy_on_type!(CompactCiphertextListExpander);
define_compact_ciphertext_list_expander_get!(unsigned: 2, 4, 6, 8, 10, 12, 14, 16, 32, 64, 128, 160, 256);
define_compact_ciphertext_list_expander_get!(signed: 2, 4, 6, 8, 10, 12, 14, 16, 32, 64, 128, 160, 256);
#[no_mangle]
pub unsafe extern "C" fn compact_ciphertext_list_expander_get_fhe_bool(
expander: *mut CompactCiphertextListExpander,
index: usize,
out: *mut *mut FheBool,
) -> c_int {
catch_panic(|| {
let expander = get_mut_checked(expander).unwrap();
let inner = expander.0.get(index).unwrap().unwrap();
*out = Box::into_raw(Box::new(FheBool(inner)));
})
}

View File

@@ -2,10 +2,8 @@ use super::utils::*;
use crate::c_api::high_level_api::booleans::FheBool;
use crate::c_api::high_level_api::i128::I128;
use crate::c_api::high_level_api::i256::I256;
use crate::c_api::high_level_api::keys::CompactPublicKey;
use crate::c_api::high_level_api::u128::U128;
use crate::c_api::high_level_api::u256::U256;
use crate::c_api::utils::*;
use crate::high_level_api::prelude::*;
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
@@ -357,213 +355,6 @@ macro_rules! create_integer_wrapper_type {
})
}
}
// The compact version of the ciphertext type
::paste::paste! {
pub struct [<Compact $name>]($crate::high_level_api::[<Compact $name>]);
impl_destroy_on_type!([<Compact $name>]);
impl_clone_on_type!([<Compact $name>]);
impl_try_encrypt_with_compact_public_key_on_type!([<Compact $name>]{crate::high_level_api::[<Compact $name>]}, $clear_scalar_type);
impl_serialize_deserialize_on_type!([<Compact $name>]);
impl_safe_serialize_on_type!([<Compact $name>]);
impl_safe_deserialize_conformant_integer!([<Compact $name>], [<$name ConformanceParams>]);
#[no_mangle]
pub unsafe extern "C" fn [<compact_ $name:snake _expand>](
sself: *const [<Compact $name>],
output: *mut *mut $name,
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
check_ptr_is_non_null_and_aligned(output).unwrap();
let list = $crate::c_api::utils::get_ref_checked(sself).unwrap();
let expanded = list.0.expand();
*output = Box::into_raw(Box::new($name(expanded)));
})
}
}
// The compact list version of the ciphertext type
::paste::paste! {
pub struct [<Compact $name List>]($crate::high_level_api::[<Compact $name List>]);
impl_destroy_on_type!([<Compact $name List>]);
impl_clone_on_type!([<Compact $name List>]);
impl_serialize_deserialize_on_type!([<Compact $name List>]);
impl_safe_serialize_on_type!([<Compact $name List>]);
impl_safe_deserialize_conformant_integer_list!([<Compact $name List>]);
#[no_mangle]
pub unsafe extern "C" fn [<compact_ $name:snake _list_len>](
sself: *const [<Compact $name List>],
result: *mut usize,
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let list = $crate::c_api::utils::get_ref_checked(sself).unwrap();
*result = list.0.len();
})
}
#[no_mangle]
pub unsafe extern "C" fn [<compact_ $name:snake _list_expand>](
sself: *const [<Compact $name List>],
output: *mut *mut $name,
output_len: usize
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
check_ptr_is_non_null_and_aligned(output).unwrap();
let list = $crate::c_api::utils::get_ref_checked(sself).unwrap();
let expanded = list.0.expand();
let num_to_take = output_len.max(list.0.len());
let iter = expanded.into_iter().take(num_to_take).enumerate();
for (i, fhe_uint) in iter {
let ptr = output.wrapping_add(i);
*ptr = Box::into_raw(Box::new($name(fhe_uint)));
}
})
}
}
// The zk compact proven version of the compact ciphertext type
#[cfg(feature = "zk-pok-experimental")]
::paste::paste! {
pub struct [<ProvenCompact $name>]($crate::high_level_api::[<ProvenCompact $name>]);
impl_destroy_on_type!([<ProvenCompact $name>]);
impl_clone_on_type!([<ProvenCompact $name>]);
impl_serialize_deserialize_on_type!([<ProvenCompact $name>]);
impl_safe_serialize_on_type!([<ProvenCompact $name>]);
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _try_encrypt>](
message: $clear_scalar_type,
public_params: &$crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &$crate::c_api::high_level_api::keys::CompactPublicKey,
compute_load: $crate::c_api::high_level_api::zk::ZkComputeLoad,
out_result: *mut *mut [<ProvenCompact $name>],
) -> c_int {
$crate::c_api::utils::catch_panic(|| {
let message = <$clear_scalar_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(message);
let result = $crate::high_level_api::[<ProvenCompact $name>]::try_encrypt(
message,
&public_params.0,
&pk.0,
compute_load.into()
).unwrap();
*out_result = Box::into_raw(Box::new([<ProvenCompact $name>](result)));
})
}
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _verify_and_expand>](
ct: *const [<ProvenCompact $name>],
public_params: &$crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &$crate::c_api::high_level_api::keys::CompactPublicKey,
out_result: *mut *mut $name,
) -> c_int {
$crate::c_api::utils::catch_panic(|| {
let ct = $crate::c_api::utils::get_ref_checked(ct).unwrap();
let result = ct.0.clone().verify_and_expand(&public_params.0, &pk.0).unwrap();
*out_result = Box::into_raw(Box::new($name(result)));
})
}
}
// The zk compact proven version of the compact ciphertext list type
#[cfg(feature = "zk-pok-experimental")]
::paste::paste! {
pub struct [<ProvenCompact $name List>]($crate::high_level_api::[<ProvenCompact $name List>]);
impl_destroy_on_type!([<ProvenCompact $name List>]);
impl_clone_on_type!([<ProvenCompact $name List>]);
impl_serialize_deserialize_on_type!([<ProvenCompact $name List>]);
impl_safe_serialize_on_type!([<ProvenCompact $name List>]);
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _list_try_encrypt>](
input: *const $clear_scalar_type,
input_len: usize,
public_params: &$crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &$crate::c_api::high_level_api::keys::CompactPublicKey,
compute_load: $crate::c_api::high_level_api::zk::ZkComputeLoad,
out_result: *mut *mut [<ProvenCompact $name List>],
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let messages = std::slice::from_raw_parts(input, input_len)
.iter()
.copied()
.map(|value| {
<$clear_scalar_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(value)
})
.collect::<Vec<_>>();
let result = $crate::high_level_api::[<ProvenCompact $name List>]::try_encrypt(
&messages,
&public_params.0,
&pk.0,
compute_load.into()
).unwrap();
*out_result = Box::into_raw(Box::new([<ProvenCompact $name List>](result)));
})
}
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _list_len>](
sself: *const [<ProvenCompact $name List>],
result: *mut usize,
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let list = $crate::c_api::utils::get_ref_checked(sself).unwrap();
*result = list.0.len();
})
}
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _list_verify_and_expand>](
list: &[<ProvenCompact $name List>],
public_params: &$crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &$crate::c_api::high_level_api::keys::CompactPublicKey,
output: *mut *mut $name,
output_len: usize
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let expanded = list.0.verify_and_expand(&public_params.0, &pk.0).unwrap();
let num_to_take = output_len.max(list.0.len());
let iter = expanded.into_iter().take(num_to_take).enumerate();
for (i, fhe_uint) in iter {
let ptr = output.wrapping_add(i);
*ptr = Box::into_raw(Box::new($name(fhe_uint)));
}
})
}
}
};
// This entry point is meant for unsigned types
@@ -594,19 +385,6 @@ create_integer_wrapper_type!(name: FheUint128, clear_scalar_type: U128);
create_integer_wrapper_type!(name: FheUint160, clear_scalar_type: U256);
create_integer_wrapper_type!(name: FheUint256, clear_scalar_type: U256);
// compact list encryption is not part of the crate_integer_wrapper_type
// as for U128 and U256 clear scalar types, the function to use is different
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint2List{crate::high_level_api::CompactFheUint2List}, u8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint4List{crate::high_level_api::CompactFheUint4List}, u8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint6List{crate::high_level_api::CompactFheUint6List}, u8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint8List{crate::high_level_api::CompactFheUint8List}, u8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint10List{crate::high_level_api::CompactFheUint10List}, u16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint12List{crate::high_level_api::CompactFheUint12List}, u16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint14List{crate::high_level_api::CompactFheUint14List}, u16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint16List{crate::high_level_api::CompactFheUint16List}, u16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint32List{crate::high_level_api::CompactFheUint32List}, u32);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint64List{crate::high_level_api::CompactFheUint64List}, u64);
create_integer_wrapper_type!(
name: FheInt2,
fhe_unsigned_type: FheUint2,
@@ -686,155 +464,6 @@ create_integer_wrapper_type!(
clear_shift_type: U256,
);
// compact list encryption is not part of the crate_integer_wrapper_type
// as for U128 and U256 clear scalar types, the function to use is different
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt2List{crate::high_level_api::CompactFheInt2List}, i8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt4List{crate::high_level_api::CompactFheInt4List}, i8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt6List{crate::high_level_api::CompactFheInt6List}, i8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt8List{crate::high_level_api::CompactFheInt8List}, i8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt10List{crate::high_level_api::CompactFheInt10List}, i16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt12List{crate::high_level_api::CompactFheInt12List}, i16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt14List{crate::high_level_api::CompactFheInt14List}, i16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt16List{crate::high_level_api::CompactFheInt16List}, i16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt32List{crate::high_level_api::CompactFheInt32List}, i32);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt64List{crate::high_level_api::CompactFheInt64List}, i64);
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_uint128_list_try_encrypt_with_compact_public_key_u128(
input: *const U128,
input_len: usize,
public_key: *const CompactPublicKey,
result: *mut *mut CompactFheUint128List,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let slc = ::std::slice::from_raw_parts(input, input_len);
let values = slc.iter().copied().map(u128::from).collect::<Vec<_>>();
let inner =
<crate::high_level_api::CompactFheUint128List>::try_encrypt(&values, &public_key.0)
.unwrap();
*result = Box::into_raw(Box::new(CompactFheUint128List(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_uint160_list_try_encrypt_with_compact_public_key_u256(
input: *const U256,
input_len: usize,
public_key: *const CompactPublicKey,
result: *mut *mut CompactFheUint160List,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let slc = ::std::slice::from_raw_parts(input, input_len);
let values = slc
.iter()
.copied()
.map(crate::integer::U256::from)
.collect::<Vec<_>>();
let inner =
<crate::high_level_api::CompactFheUint160List>::try_encrypt(&values, &public_key.0)
.unwrap();
*result = Box::into_raw(Box::new(CompactFheUint160List(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_uint256_list_try_encrypt_with_compact_public_key_u256(
input: *const U256,
input_len: usize,
public_key: *const CompactPublicKey,
result: *mut *mut CompactFheUint256List,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let slc = ::std::slice::from_raw_parts(input, input_len);
let values = slc
.iter()
.copied()
.map(crate::integer::U256::from)
.collect::<Vec<_>>();
let inner =
<crate::high_level_api::CompactFheUint256List>::try_encrypt(&values, &public_key.0)
.unwrap();
*result = Box::into_raw(Box::new(CompactFheUint256List(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_int128_list_try_encrypt_with_compact_public_key_i128(
input: *const I128,
input_len: usize,
public_key: *const CompactPublicKey,
result: *mut *mut CompactFheInt128List,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let slc = ::std::slice::from_raw_parts(input, input_len);
let values = slc.iter().copied().map(i128::from).collect::<Vec<_>>();
let inner =
<crate::high_level_api::CompactFheInt128List>::try_encrypt(&values, &public_key.0)
.unwrap();
*result = Box::into_raw(Box::new(CompactFheInt128List(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_int160_list_try_encrypt_with_compact_public_key_i256(
input: *const I256,
input_len: usize,
public_key: *const CompactPublicKey,
result: *mut *mut CompactFheInt160List,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let slc = ::std::slice::from_raw_parts(input, input_len);
let values = slc
.iter()
.copied()
.map(crate::integer::I256::from)
.collect::<Vec<_>>();
let inner =
<crate::high_level_api::CompactFheInt160List>::try_encrypt(&values, &public_key.0)
.unwrap();
*result = Box::into_raw(Box::new(CompactFheInt160List(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_int256_list_try_encrypt_with_compact_public_key_i256(
input: *const I256,
input_len: usize,
public_key: *const CompactPublicKey,
result: *mut *mut CompactFheInt256List,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let slc = ::std::slice::from_raw_parts(input, input_len);
let values = slc
.iter()
.copied()
.map(crate::integer::I256::from)
.collect::<Vec<_>>();
let inner =
<crate::high_level_api::CompactFheInt256List>::try_encrypt(&values, &public_key.0)
.unwrap();
*result = Box::into_raw(Box::new(CompactFheInt256List(inner)));
})
}
define_all_cast_into_for_integer_type!(FheBool);
macro_rules! impl_oprf_for_uint {

View File

@@ -1,6 +1,7 @@
mod array;
#[cfg(feature = "boolean")]
pub mod booleans;
mod compact_list;
pub mod config;
pub mod i128;
pub mod i256;
@@ -12,3 +13,70 @@ pub mod u256;
mod utils;
#[cfg(feature = "zk-pok-experimental")]
mod zk;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(C)]
#[allow(non_camel_case_types)]
pub enum FheTypes {
Type_FheBool,
Type_FheUint2,
Type_FheUint4,
Type_FheUint6,
Type_FheUint8,
Type_FheUint10,
Type_FheUint12,
Type_FheUint14,
Type_FheUint16,
Type_FheUint32,
Type_FheUint64,
Type_FheUint128,
Type_FheUint160,
Type_FheUint256,
Type_FheInt2,
Type_FheInt4,
Type_FheInt6,
Type_FheInt8,
Type_FheInt10,
Type_FheInt12,
Type_FheInt14,
Type_FheInt16,
Type_FheInt32,
Type_FheInt64,
Type_FheInt128,
Type_FheInt160,
Type_FheInt256,
}
impl From<crate::FheTypes> for FheTypes {
fn from(value: crate::FheTypes) -> Self {
match value {
crate::FheTypes::Bool => Self::Type_FheBool,
crate::FheTypes::Uint2 => Self::Type_FheUint2,
crate::FheTypes::Uint4 => Self::Type_FheUint4,
crate::FheTypes::Uint6 => Self::Type_FheUint6,
crate::FheTypes::Uint8 => Self::Type_FheUint8,
crate::FheTypes::Uint10 => Self::Type_FheUint10,
crate::FheTypes::Uint12 => Self::Type_FheUint12,
crate::FheTypes::Uint14 => Self::Type_FheUint14,
crate::FheTypes::Uint16 => Self::Type_FheUint16,
crate::FheTypes::Uint32 => Self::Type_FheUint32,
crate::FheTypes::Uint64 => Self::Type_FheUint64,
crate::FheTypes::Uint128 => Self::Type_FheUint128,
crate::FheTypes::Uint160 => Self::Type_FheUint160,
crate::FheTypes::Uint256 => Self::Type_FheUint256,
crate::FheTypes::Int2 => Self::Type_FheInt2,
crate::FheTypes::Int4 => Self::Type_FheInt4,
crate::FheTypes::Int6 => Self::Type_FheInt6,
crate::FheTypes::Int8 => Self::Type_FheInt8,
crate::FheTypes::Int10 => Self::Type_FheInt10,
crate::FheTypes::Int12 => Self::Type_FheInt12,
crate::FheTypes::Int14 => Self::Type_FheInt14,
crate::FheTypes::Int16 => Self::Type_FheInt16,
crate::FheTypes::Int32 => Self::Type_FheInt32,
crate::FheTypes::Int64 => Self::Type_FheInt64,
crate::FheTypes::Int128 => Self::Type_FheInt128,
crate::FheTypes::Int160 => Self::Type_FheInt160,
crate::FheTypes::Int256 => Self::Type_FheInt256,
}
}
}

View File

@@ -138,30 +138,6 @@ macro_rules! impl_try_encrypt_with_compact_public_key_on_type {
pub(crate) use impl_try_encrypt_with_compact_public_key_on_type;
macro_rules! impl_try_encrypt_list_with_compact_public_key_on_type {
($wrapper_type:ty{$wrapped_type:ty}, $input_type:ty) => {
::paste::paste! {
#[no_mangle]
pub unsafe extern "C" fn [<$wrapper_type:snake _try_encrypt_with_compact_public_key_ $input_type:snake>](
input: *const $input_type,
input_len: usize,
public_key: *const $crate::c_api::high_level_api::keys::CompactPublicKey,
result: *mut *mut $wrapper_type,
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let public_key = $crate::c_api::utils::get_ref_checked(public_key).unwrap();
let slc = ::std::slice::from_raw_parts(input, input_len);
let inner = <$wrapped_type>::try_encrypt(slc, &public_key.0).unwrap();
*result = Box::into_raw(Box::new($wrapper_type(inner)));
})
}
}
};
}
pub(crate) use impl_try_encrypt_list_with_compact_public_key_on_type;
macro_rules! impl_try_encrypt_trivial_on_type {
($wrapper_type:ty{$wrapped_type:ty}, $input_type:ty) => {
::paste::paste! {
@@ -396,65 +372,6 @@ macro_rules! impl_safe_deserialize_conformant_integer {
pub(crate) use impl_safe_deserialize_conformant_integer;
macro_rules! impl_safe_deserialize_conformant_integer_list {
($wrapper_type:ty) => {
::paste::paste! {
/// Deserializes a list safely, and checks that the resulting ciphertext
/// is in compliance with the shape of ciphertext that the `server_key` expects.
///
/// This function can only deserialize, types which have been serialized
/// by a `safe_serialize` function.
///
/// - `serialized_size_limit`: size limit (in number of byte) of the serialized object
/// (to avoid out of memory attacks)
/// - `server_key`: ServerKey used in the conformance check
/// - `exact_len`: exact lenght/size/number of element the list must have
/// (not less nor more, otherwise, it's considered an error)
/// - `result`: pointer where resulting deserialized object needs to be stored.
/// * cannot be NULL
/// * (*result) will point the deserialized object on success, else NULL
#[no_mangle]
pub unsafe extern "C" fn [<$wrapper_type:snake _safe_deserialize_conformant>](
buffer_view: crate::c_api::buffer::DynamicBufferView,
serialized_size_limit: u64,
server_key: *const crate::c_api::high_level_api::keys::ServerKey,
exact_len: usize,
result: *mut *mut $wrapper_type,
) -> ::std::os::raw::c_int {
::paste::paste! {
crate::c_api::utils::catch_panic(|| {
crate::c_api::utils::check_ptr_is_non_null_and_aligned(result).unwrap();
let sk = crate::c_api::utils::get_ref_checked(server_key).unwrap();
let buffer_view: &[u8] = buffer_view.as_slice();
// First fill the result with a null ptr so that if we fail and the return code is not
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result = std::ptr::null_mut();
let params = $crate::high_level_api::[<$wrapper_type ConformanceParams>]::from(
(&sk.0, $crate::conformance::ListSizeConstraint::exact_size(exact_len))
);
let inner = $crate::safe_deserialization::safe_deserialize_conformant(
buffer_view,
serialized_size_limit,
&params
)
.unwrap();
let heap_allocated_object = Box::new($wrapper_type(inner));
*result = Box::into_raw(heap_allocated_object);
})
}
}
}
};
}
pub(crate) use impl_safe_deserialize_conformant_integer_list;
macro_rules! impl_binary_fn_on_type {
// More general binary fn case,
// where the type of the left-hand side can be different

View File

@@ -1,214 +0,0 @@
use super::FheBool;
use crate::conformance::{ListSizeConstraint, ParameterSetConformant};
use crate::high_level_api::traits::FheTryEncrypt;
use crate::integer::ciphertext::CompactCiphertextList;
use crate::integer::parameters::RadixCompactCiphertextListConformanceParams;
use crate::integer::BooleanBlock;
use crate::named::Named;
use crate::shortint::ciphertext::Degree;
use crate::shortint::parameters::CiphertextConformanceParams;
use crate::shortint::PBSParameters;
use crate::{CompactPublicKey, FheBoolConformanceParams, ServerKey};
/// Compact [FheBool]
///
/// Meant to save in storage space / transfer.
///
/// - A Compact type must be expanded using [expand](Self::expand) before it can be used.
/// - It is not possible to 'compact' an existing [FheBool]. Compacting can only be achieved at
/// encryption time by a [CompactPublicKey]
///
/// # Example
///
/// ```rust
/// use tfhe::prelude::*;
/// use tfhe::{generate_keys, CompactFheBool, CompactPublicKey, ConfigBuilder, FheBool};
///
/// let (client_key, _) = generate_keys(ConfigBuilder::default());
/// let compact_public_key = CompactPublicKey::try_new(&client_key).unwrap();
///
/// let compact = CompactFheBool::encrypt(true, &compact_public_key);
///
/// let ciphertext = compact.expand();
/// let decrypted: bool = ciphertext.decrypt(&client_key);
/// assert_eq!(decrypted, true);
/// ```
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct CompactFheBool {
pub(in crate::high_level_api) list: CompactCiphertextList,
}
impl CompactFheBool {
/// Expand to a [FheBool]
///
/// See [CompactFheBool] example.
pub fn expand(&self) -> FheBool {
let ct: crate::integer::RadixCiphertext = self.list.expand_one();
assert_eq!(ct.blocks.len(), 1);
let mut block = BooleanBlock::new_unchecked(ct.blocks.into_iter().next().unwrap());
block.0.degree = Degree::new(1);
let mut ciphertext = FheBool::new(block);
ciphertext.ciphertext.move_to_device_of_server_key_if_set();
ciphertext
}
}
impl FheTryEncrypt<bool, CompactPublicKey> for CompactFheBool {
type Error = crate::Error;
fn try_encrypt(value: bool, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let mut ciphertext = key.key.try_encrypt_compact(&[u8::from(value)], 1);
ciphertext.ct_list.degree = Degree::new(1);
Ok(Self { list: ciphertext })
}
}
impl Named for CompactFheBool {
const NAME: &'static str = "high_level_api::CompactFheBool";
}
impl ParameterSetConformant for CompactFheBool {
type ParameterSet = FheBoolConformanceParams;
fn is_conformant(&self, params: &FheBoolConformanceParams) -> bool {
let params = RadixCompactCiphertextListConformanceParams {
shortint_params: params.0,
num_blocks_per_integer: 1,
num_integers_constraint: ListSizeConstraint::exact_size(1),
};
self.list.is_conformant(&params)
}
}
/// Compact list of [FheBool]
///
/// Meant to save in storage space / transfer.
///
/// - A Compact type must be expanded using [expand](Self::expand) before it can be used.
/// - It is not possible to 'compact' an existing [FheBool]. Compacting can only be achieved at
/// encryption time by a [CompactPublicKey]
///
/// # Example
///
/// ```rust
/// use tfhe::prelude::*;
/// use tfhe::{generate_keys, CompactFheBoolList, CompactPublicKey, ConfigBuilder};
///
/// let (client_key, _) = generate_keys(ConfigBuilder::default());
/// let compact_public_key = CompactPublicKey::try_new(&client_key).unwrap();
///
/// let clears = vec![false, true];
/// let compact = CompactFheBoolList::encrypt(&clears, &compact_public_key);
/// assert_eq!(compact.len(), clears.len());
///
/// let ciphertexts = compact.expand();
/// let decrypted: Vec<bool> = ciphertexts
/// .into_iter()
/// .map(|ciphertext| ciphertext.decrypt(&client_key))
/// .collect();
/// assert_eq!(decrypted, clears);
/// ```
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct CompactFheBoolList {
list: CompactCiphertextList,
}
impl CompactFheBoolList {
/// Returns the number of element in the compact list
pub fn len(&self) -> usize {
self.list.ciphertext_count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Expand to a Vec<[FheBool]>
///
/// See [CompactFheBoolList] example.
pub fn expand(&self) -> Vec<FheBool> {
self.list
.expand()
.into_iter()
.map(|ct: crate::integer::RadixCiphertext| {
assert_eq!(ct.blocks.len(), 1);
let mut block = BooleanBlock::new_unchecked(ct.blocks.into_iter().next().unwrap());
block.0.degree = Degree::new(1);
let mut ciphertext = FheBool::new(block);
ciphertext.ciphertext.move_to_device_of_server_key_if_set();
ciphertext
})
.collect::<Vec<_>>()
}
}
impl<'a> FheTryEncrypt<&'a [bool], CompactPublicKey> for CompactFheBoolList {
type Error = crate::Error;
/// Encrypts a slice of bool
///
/// See [CompactFheBoolList] example.
fn try_encrypt(values: &'a [bool], key: &CompactPublicKey) -> Result<Self, Self::Error> {
let mut ciphertext = key
.key
.key
.encrypt_iter_radix_compact(values.iter().copied().map(|v| v as u8), 1);
ciphertext.ct_list.degree = Degree::new(1);
Ok(Self { list: ciphertext })
}
}
impl Named for CompactFheBoolList {
const NAME: &'static str = "high_level_api::CompactFheBoolList";
}
pub struct CompactFheBoolListConformanceParams {
shortint_params: CiphertextConformanceParams,
len_constraint: ListSizeConstraint,
}
impl CompactFheBoolListConformanceParams {
fn new(
mut shortint_params: CiphertextConformanceParams,
len_constraint: ListSizeConstraint,
) -> Self {
shortint_params.degree = Degree::new(1);
Self {
shortint_params,
len_constraint,
}
}
}
impl<P> From<(P, ListSizeConstraint)> for CompactFheBoolListConformanceParams
where
P: Into<PBSParameters>,
{
fn from((params, len_constraint): (P, ListSizeConstraint)) -> Self {
Self::new(
params.into().to_shortint_conformance_param(),
len_constraint,
)
}
}
impl From<(&ServerKey, ListSizeConstraint)> for CompactFheBoolListConformanceParams {
fn from((sk, len_constraint): (&ServerKey, ListSizeConstraint)) -> Self {
Self::new(sk.key.pbs_key().key.conformance_params(), len_constraint)
}
}
impl ParameterSetConformant for CompactFheBoolList {
type ParameterSet = CompactFheBoolListConformanceParams;
fn is_conformant(&self, params: &CompactFheBoolListConformanceParams) -> bool {
let params = RadixCompactCiphertextListConformanceParams {
shortint_params: params.shortint_params,
num_blocks_per_integer: 1,
num_integers_constraint: params.len_constraint,
};
self.list.is_conformant(&params)
}
}

View File

@@ -1,15 +1,9 @@
pub use base::{FheBool, FheBoolConformanceParams};
pub use compact::{CompactFheBool, CompactFheBoolList, CompactFheBoolListConformanceParams};
pub use compressed::CompressedFheBool;
#[cfg(feature = "zk-pok-experimental")]
pub use zk::{ProvenCompactFheBool, ProvenCompactFheBoolList};
mod base;
mod compact;
mod compressed;
mod encrypt;
mod inner;
#[cfg(test)]
mod tests;
#[cfg(feature = "zk-pok-experimental")]
mod zk;

View File

@@ -6,8 +6,8 @@
use crate::prelude::*;
use crate::{
generate_keys, set_server_key, ClientKey, CompactFheBool, CompactFheBoolList, CompactPublicKey,
CompressedFheBool, CompressedPublicKey, ConfigBuilder, Device, FheBool,
generate_keys, set_server_key, ClientKey, CompressedFheBool, CompressedPublicKey,
ConfigBuilder, Device, FheBool,
};
#[inline(always)]
@@ -316,48 +316,11 @@ fn compressed_bool_test_case(setup_fn: impl FnOnce() -> (ClientKey, Device)) {
assert_eq!(b.decrypt(&cks), false);
}
fn compact_bool_test_case(setup_fn: impl FnOnce() -> (ClientKey, Device)) {
let (cks, sks_device) = setup_fn();
let cpk = CompactPublicKey::new(&cks);
let cttrue = CompactFheBool::encrypt(true, &cpk);
let cffalse = CompactFheBool::encrypt(false, &cpk);
let a = cttrue.expand();
let b = cffalse.expand();
assert_degree_is_ok(&a);
assert_degree_is_ok(&b);
assert_eq!(a.current_device(), sks_device);
assert_eq!(b.current_device(), sks_device);
assert_eq!(a.decrypt(&cks), true);
assert_eq!(b.decrypt(&cks), false);
}
fn compact_bool_list_test_case(setup_fn: impl FnOnce() -> (ClientKey, Device)) {
let (cks, sks_device) = setup_fn();
let cpk = CompactPublicKey::new(&cks);
let clears = vec![false, true, true, false];
let compacts = CompactFheBoolList::encrypt(&clears, &cpk);
let ciphertexts = compacts.expand();
for (fhe_bool, clear) in ciphertexts.into_iter().zip(clears.into_iter()) {
assert_degree_is_ok(&fhe_bool);
assert_eq!(fhe_bool.current_device(), sks_device);
assert_eq!(fhe_bool.decrypt(&cks), clear);
}
}
mod cpu {
use super::*;
use crate::conformance::ListSizeConstraint;
use crate::safe_deserialization::safe_deserialize_conformant;
use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
use crate::{CompactFheBoolListConformanceParams, FheBoolConformanceParams};
use crate::FheBoolConformanceParams;
use rand::random;
fn setup_default() -> ClientKey {
@@ -666,16 +629,6 @@ mod cpu {
compressed_bool_test_case(|| (setup_default(), Device::Cpu));
}
#[test]
fn test_compact_bool() {
compact_bool_test_case(|| (setup_default(), Device::Cpu));
}
#[test]
fn test_compact_bool_list() {
compact_bool_list_test_case(|| (setup_default(), Device::Cpu));
}
#[test]
fn test_trivial_bool() {
let keys = setup_default();
@@ -765,118 +718,6 @@ mod cpu {
let decrypted: bool = deserialized_a.decompress().decrypt(&client_key);
assert_eq!(decrypted, clear_a);
}
#[test]
fn test_safe_deserialize_conformant_compact_fhe_bool() {
let block_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
let (client_key, server_key) =
generate_keys(ConfigBuilder::with_custom_parameters(block_params, None));
set_server_key(server_key.clone());
let pk = CompactPublicKey::new(&client_key);
let clear_a = random::<bool>();
let a = CompactFheBool::encrypt(clear_a, &pk);
let mut serialized = vec![];
assert!(crate::safe_serialize(&a, &mut serialized, 1 << 20).is_ok());
let params = FheBoolConformanceParams::from(&server_key);
let deserialized_a =
safe_deserialize_conformant::<CompactFheBool>(serialized.as_slice(), 1 << 20, &params)
.unwrap();
assert!(deserialized_a.is_conformant(&FheBoolConformanceParams::from(block_params)));
let decrypted: bool = deserialized_a.expand().decrypt(&client_key);
assert_eq!(decrypted, clear_a);
}
#[test]
fn test_safe_deserialize_conformant_compact_fhe_bool_list() {
let block_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
let (client_key, server_key) =
generate_keys(ConfigBuilder::with_custom_parameters(block_params, None));
set_server_key(server_key.clone());
let pk = CompactPublicKey::new(&client_key);
let clears = [random::<bool>(), random::<bool>(), random::<bool>()];
let compact_list = CompactFheBoolList::encrypt(&clears, &pk);
let mut serialized = vec![];
assert!(crate::safe_serialize(&compact_list, &mut serialized, 1 << 20).is_ok());
let params = CompactFheBoolListConformanceParams::from((
&server_key,
ListSizeConstraint::exact_size(3),
));
let deserialized_list = safe_deserialize_conformant::<CompactFheBoolList>(
serialized.as_slice(),
1 << 20,
&params,
)
.unwrap();
assert!(
deserialized_list.is_conformant(&CompactFheBoolListConformanceParams::from((
block_params,
ListSizeConstraint::exact_size(3)
)))
);
let expanded_list = deserialized_list.expand();
for (fhe_uint, expected) in expanded_list.iter().zip(clears.into_iter()) {
let decrypted: bool = fhe_uint.decrypt(&client_key);
assert_eq!(decrypted, expected)
}
}
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_fhe_bool_zk() {
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
let params =
crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40;
let config = ConfigBuilder::with_custom_parameters(params, None).build();
let crs = CompactPkeCrs::from_config(config, 2).unwrap();
let ck = ClientKey::generate(config);
let pk = CompactPublicKey::new(&ck);
for msg in [true, false] {
let proven_compact_fhe_bool = crate::ProvenCompactFheBool::try_encrypt(
msg,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_bool = proven_compact_fhe_bool
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = fhe_bool.decrypt(&ck);
assert_eq!(decrypted, msg);
assert_degree_is_ok(&fhe_bool);
}
let proven_compact_fhe_bool_list = crate::ProvenCompactFheBoolList::try_encrypt(
&[true, false],
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_bools = proven_compact_fhe_bool_list
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = fhe_bools
.iter()
.map(|fb| fb.decrypt(&ck))
.collect::<Vec<_>>();
assert_eq!(decrypted.as_slice(), &[true, false]);
for fhe_bool in fhe_bools {
assert_degree_is_ok(&fhe_bool);
}
}
}
#[cfg(feature = "gpu")]
@@ -1184,14 +1025,4 @@ mod gpu {
fn test_compressed_bool() {
compressed_bool_test_case(|| (setup_gpu_default(), Device::CudaGpu));
}
#[test]
fn test_compact_bool() {
compact_bool_test_case(|| (setup_gpu_default(), Device::CudaGpu));
}
#[test]
fn test_compact_bool_list() {
compact_bool_list_test_case(|| (setup_gpu_default(), Device::CudaGpu));
}
}

View File

@@ -1,134 +0,0 @@
use crate::integer::{BooleanBlock, ProvenCompactCiphertextList, RadixCiphertext};
use crate::named::Named;
use crate::shortint::ciphertext::Degree;
use crate::zk::{CompactPkePublicParams, ZkComputeLoad, ZkVerificationOutCome};
use crate::{CompactPublicKey, FheBool};
use serde::{Deserialize, Serialize};
/// A `CompactFheBool` tied to a Zero-Knowledge proof
///
/// The zero-knowledge proof allows to verify that the ciphertext is correctly
/// encrypted.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactFheBool {
inner: ProvenCompactCiphertextList,
}
impl Named for ProvenCompactFheBool {
const NAME: &'static str = "high_level_api::ProvenCompactFheBool";
}
impl ProvenCompactFheBool {
/// Encrypts the message while also generating the zero-knowledge proof
pub fn try_encrypt(
value: bool,
public_params: &CompactPkePublicParams,
key: &CompactPublicKey,
load: ZkComputeLoad,
) -> crate::Result<Self> {
let value = value as u8;
let inner = key.key.key.encrypt_and_prove_radix_compact(
&[value],
1, /* num blocks */
public_params,
load,
)?;
Ok(Self { inner })
}
/// Verifies the ciphertext and the proof
///
/// If the proof and ciphertext are valid, it returns an `Ok` with
/// the underlying `FheBool`.
pub fn verify_and_expand(
self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<FheBool> {
let mut radix = self
.inner
.verify_and_expand_one::<RadixCiphertext>(public_params, &public_key.key.key)?;
assert_eq!(radix.blocks.len(), 1);
radix.blocks[0].degree = Degree::new(1);
Ok(FheBool::new(BooleanBlock::new_unchecked(
radix.blocks.pop().unwrap(),
)))
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
self.inner.verify(public_params, &public_key.key.key)
}
}
/// A `CompactFheBoolList` tied to a Zero-Knowledge proof
///
/// The zero-knowledge proof allows to verify that the ciphertext list is correctly
/// encrypted.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactFheBoolList {
inner: ProvenCompactCiphertextList,
}
impl Named for ProvenCompactFheBoolList {
const NAME: &'static str = "high_level_api::ProvenCompactFheBoolList";
}
impl ProvenCompactFheBoolList {
/// Encrypts the message while also generating the zero-knowledge proof
pub fn try_encrypt(
values: &[bool],
public_params: &CompactPkePublicParams,
key: &CompactPublicKey,
load: ZkComputeLoad,
) -> crate::Result<Self> {
let values = values.iter().copied().map(u8::from).collect::<Vec<_>>();
let inner = key.key.key.encrypt_and_prove_radix_compact(
&values,
1, /* num_blocks */
public_params,
load,
)?;
Ok(Self { inner })
}
pub fn len(&self) -> usize {
self.inner.ciphertext_count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Verifies the ciphertext and the proof
///
/// If the proof and ciphertext are valid, it returns an `Ok` with
/// the underlying `FheBool`s.
pub fn verify_and_expand(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<Vec<FheBool>> {
Ok(self
.inner
.verify_and_expand::<RadixCiphertext>(public_params, &public_key.key.key)?
.into_iter()
.map(|mut radix| {
assert_eq!(radix.blocks.len(), 1);
radix.blocks[0].degree = Degree::new(1);
FheBool::new(BooleanBlock::new_unchecked(radix.blocks.pop().unwrap()))
})
.collect())
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
self.inner.verify(public_params, &public_key.key.key)
}
}

View File

@@ -0,0 +1,492 @@
use crate::conformance::ParameterSetConformant;
use crate::core_crypto::commons::math::random::{Deserialize, Serialize};
use crate::core_crypto::prelude::Numeric;
use crate::high_level_api::global_state;
use crate::high_level_api::integers::{FheIntId, FheUintId};
use crate::high_level_api::keys::InternalServerKey;
use crate::integer::ciphertext::{Compactable, DataKind, Expandable};
use crate::integer::encryption::KnowsMessageModulus;
use crate::integer::parameters::CompactCiphertextListConformanceParams;
use crate::integer::BooleanBlock;
use crate::named::Named;
use crate::shortint::{Ciphertext, MessageModulus};
#[cfg(feature = "zk-pok-experimental")]
use crate::zk::{CompactPkePublicParams, ZkComputeLoad};
use crate::{CompactPublicKey, FheBool, FheInt, FheUint};
#[derive(Clone, Serialize, Deserialize)]
pub struct CompactCiphertextList(crate::integer::ciphertext::CompactCiphertextList);
impl Named for CompactCiphertextList {
const NAME: &'static str = "high_level_api::CompactCiphertextList";
}
impl CompactCiphertextList {
pub fn builder(pk: &CompactPublicKey) -> CompactCiphertextListBuilder {
CompactCiphertextListBuilder::new(pk)
}
pub fn expand_with_key(
&self,
sks: &crate::ServerKey,
) -> crate::Result<CompactCiphertextListExpander> {
self.0
.expand(sks.key.pbs_key())
.map(|inner| CompactCiphertextListExpander { inner })
}
pub fn expand(&self) -> crate::Result<CompactCiphertextListExpander> {
if self.0.is_packed() {
global_state::try_with_internal_keys(|maybe_keys| match maybe_keys {
None => Err(crate::high_level_api::errors::UninitializedServerKey.into()),
Some(InternalServerKey::Cpu(cpu_key)) => self
.0
.expand(cpu_key.pbs_key())
.map(|inner| CompactCiphertextListExpander { inner }),
#[cfg(feature = "gpu")]
Some(_) => Err(crate::Error::new("Expected a CPU server key".to_string())),
})
} else {
Ok(CompactCiphertextListExpander {
inner: self.0.expand_without_unpacking(),
})
}
}
}
impl ParameterSetConformant for CompactCiphertextList {
type ParameterSet = CompactCiphertextListConformanceParams;
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
self.0.is_conformant(parameter_set)
}
}
#[cfg(feature = "zk-pok-experimental")]
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactCiphertextList(crate::integer::ciphertext::ProvenCompactCiphertextList);
#[cfg(feature = "zk-pok-experimental")]
impl Named for ProvenCompactCiphertextList {
const NAME: &'static str = "high_level_api::ProvenCompactCiphertextList";
}
#[cfg(feature = "zk-pok-experimental")]
impl ProvenCompactCiphertextList {
pub fn builder(pk: &CompactPublicKey) -> CompactCiphertextListBuilder {
CompactCiphertextListBuilder::new(pk)
}
pub fn verify_and_expand_with_key(
&self,
public_params: &CompactPkePublicParams,
pk: &CompactPublicKey,
sks: &crate::ServerKey,
) -> crate::Result<CompactCiphertextListExpander> {
self.0
.verify_and_expand(public_params, &pk.key.key, sks.key.pbs_key())
.map(|expander| CompactCiphertextListExpander { inner: expander })
}
pub fn verify_and_expand(
&self,
public_params: &CompactPkePublicParams,
pk: &CompactPublicKey,
) -> crate::Result<CompactCiphertextListExpander> {
if self.0.is_packed() {
global_state::try_with_internal_keys(|maybe_keys| match maybe_keys {
None => Err(crate::high_level_api::errors::UninitializedServerKey.into()),
Some(InternalServerKey::Cpu(cpu_key)) => self
.0
.verify_and_expand(public_params, &pk.key.key, cpu_key.pbs_key())
.map(|expander| CompactCiphertextListExpander { inner: expander }),
#[cfg(feature = "gpu")]
Some(_) => Err(crate::Error::new("Expected a CPU server key".to_string())),
})
} else {
self.0
.verify_and_expand_without_unpacking(public_params, &pk.key.key)
.map(|expander| CompactCiphertextListExpander { inner: expander })
}
}
}
fn num_bits_of_blocks(blocks: &[Ciphertext]) -> u32 {
blocks
.iter()
.map(|block| block.message_modulus.0.ilog2())
.sum::<u32>()
}
impl<Id: FheUintId> Expandable for FheUint<Id> {
fn from_expanded_blocks(blocks: &[Ciphertext], kind: DataKind) -> crate::Result<Self> {
match kind {
DataKind::Unsigned(_) => {
let stored_num_bits = num_bits_of_blocks(blocks) as usize;
if stored_num_bits == Id::num_bits() {
Ok(Self::new(crate::integer::RadixCiphertext::from(
blocks.to_vec(),
)))
} else {
Err(crate::Error::new(format!(
"Tried to expand a FheUint{} while a FheUint{} is stored in this slot",
Id::num_bits(),
stored_num_bits
)))
}
}
DataKind::Signed(_) => {
let stored_num_bits = num_bits_of_blocks(blocks) as usize;
Err(crate::Error::new(format!(
"Tried to expand a FheUint{} while a FheInt{} is stored in this slot",
Id::num_bits(),
stored_num_bits
)))
}
DataKind::Boolean => Err(crate::Error::new(format!(
"Tried to expand a FheUint{} while a FheBool is stored in this slot",
Id::num_bits(),
))),
}
}
}
impl<Id: FheIntId> Expandable for FheInt<Id> {
fn from_expanded_blocks(blocks: &[Ciphertext], kind: DataKind) -> crate::Result<Self> {
match kind {
DataKind::Unsigned(_) => {
let stored_num_bits = num_bits_of_blocks(blocks) as usize;
Err(crate::Error::new(format!(
"Tried to expand a FheInt{} while a FheUint{} is stored in this slot",
Id::num_bits(),
stored_num_bits
)))
}
DataKind::Signed(_) => {
let stored_num_bits = num_bits_of_blocks(blocks) as usize;
if stored_num_bits == Id::num_bits() {
Ok(Self::new(crate::integer::SignedRadixCiphertext::from(
blocks.to_vec(),
)))
} else {
Err(crate::Error::new(format!(
"Tried to expand a FheInt{} while a FheInt{} is stored in this slot",
Id::num_bits(),
stored_num_bits
)))
}
}
DataKind::Boolean => Err(crate::Error::new(format!(
"Tried to expand a FheUint{} while a FheBool is stored in this slot",
Id::num_bits(),
))),
}
}
}
impl Expandable for FheBool {
fn from_expanded_blocks(blocks: &[Ciphertext], kind: DataKind) -> crate::Result<Self> {
match kind {
DataKind::Unsigned(_) => {
let stored_num_bits = num_bits_of_blocks(blocks) as usize;
Err(crate::Error::new(format!(
"Tried to expand a FheBool while a FheUint{stored_num_bits} is stored in this slot",
)))
}
DataKind::Signed(_) => {
let stored_num_bits = num_bits_of_blocks(blocks) as usize;
Err(crate::Error::new(format!(
"Tried to expand a FheBool while a FheInt{stored_num_bits} is stored in this slot",
)))
}
DataKind::Boolean => Ok(Self::new(BooleanBlock::new_unchecked(blocks[0].clone()))),
}
}
}
pub struct CompactCiphertextListExpander {
inner: crate::integer::ciphertext::CompactCiphertextListExpander,
}
impl CompactCiphertextListExpander {
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get_kind_of(&self, index: usize) -> Option<crate::FheTypes> {
Some(match self.inner.get_kind_of(index)? {
DataKind::Unsigned(n) => {
let num_bits_per_block = self.inner.message_modulus().0.ilog2() as usize;
let num_bits = n * num_bits_per_block;
match num_bits {
2 => crate::FheTypes::Uint2,
4 => crate::FheTypes::Uint4,
6 => crate::FheTypes::Uint6,
8 => crate::FheTypes::Uint8,
10 => crate::FheTypes::Uint10,
12 => crate::FheTypes::Uint12,
14 => crate::FheTypes::Uint14,
16 => crate::FheTypes::Uint16,
32 => crate::FheTypes::Uint32,
64 => crate::FheTypes::Uint64,
128 => crate::FheTypes::Uint128,
160 => crate::FheTypes::Uint160,
256 => crate::FheTypes::Uint256,
_ => return None,
}
}
DataKind::Signed(n) => {
let num_bits_per_block = self.inner.message_modulus().0.ilog2() as usize;
let num_bits = n * num_bits_per_block;
match num_bits {
2 => crate::FheTypes::Int2,
4 => crate::FheTypes::Int4,
6 => crate::FheTypes::Int6,
8 => crate::FheTypes::Int8,
10 => crate::FheTypes::Int10,
12 => crate::FheTypes::Int12,
14 => crate::FheTypes::Int14,
16 => crate::FheTypes::Int16,
32 => crate::FheTypes::Int32,
64 => crate::FheTypes::Int64,
128 => crate::FheTypes::Int128,
160 => crate::FheTypes::Int160,
256 => crate::FheTypes::Int256,
_ => return None,
}
}
DataKind::Boolean => crate::FheTypes::Bool,
})
}
pub fn get<T>(&self, index: usize) -> Option<crate::Result<T>>
where
T: Expandable,
{
self.inner.get(index)
}
}
fn num_bits_to_strict_num_blocks(
num_bits: usize,
message_modulus: MessageModulus,
) -> crate::Result<usize> {
let bits_per_block = message_modulus.0.ilog2();
if num_bits as u32 % bits_per_block != 0 {
let message = format!("Number of bits must be a multiple of the parameter's MessageModulus.ilog2 ({bits_per_block} here)");
return Err(crate::Error::new(message));
}
Ok(num_bits.div_ceil(bits_per_block as usize))
}
pub struct CompactCiphertextListBuilder {
inner: crate::integer::ciphertext::CompactCiphertextListBuilder,
}
impl CompactCiphertextListBuilder {
pub fn new(pk: &CompactPublicKey) -> Self {
Self {
inner: crate::integer::ciphertext::CompactCiphertextListBuilder::new(&pk.key.key),
}
}
pub fn push<T>(&mut self, value: T) -> &mut Self
where
T: Compactable,
{
self.inner.push(value);
self
}
pub fn extend<T>(&mut self, values: impl Iterator<Item = T>) -> &mut Self
where
T: Compactable,
{
self.inner.extend(values);
self
}
pub fn push_with_num_bits<T>(&mut self, number: T, num_bits: usize) -> crate::Result<&mut Self>
where
T: Compactable + Numeric,
{
let num_blocks =
num_bits_to_strict_num_blocks(num_bits, self.inner.pk.key.message_modulus())?;
self.inner.push_with_num_blocks(number, num_blocks);
Ok(self)
}
pub fn extend_with_num_bits<T>(
&mut self,
values: impl Iterator<Item = T>,
num_bits: usize,
) -> crate::Result<&mut Self>
where
T: Compactable + Numeric,
{
let num_blocks =
num_bits_to_strict_num_blocks(num_bits, self.inner.pk.key.message_modulus())?;
self.inner.extend_with_num_blocks(values, num_blocks);
Ok(self)
}
pub fn build(&self) -> CompactCiphertextList {
CompactCiphertextList(self.inner.build())
}
pub fn build_packed(&self) -> CompactCiphertextList {
self.inner
.build_packed()
.map(CompactCiphertextList)
.expect("Internal error, invalid parameters should not have been allowed")
}
#[cfg(feature = "zk-pok-experimental")]
pub fn build_with_proof(
&self,
public_params: &CompactPkePublicParams,
compute_load: ZkComputeLoad,
) -> crate::Result<ProvenCompactCiphertextList> {
self.inner
.build_with_proof(public_params, compute_load)
.map(ProvenCompactCiphertextList)
}
#[cfg(feature = "zk-pok-experimental")]
pub fn build_with_proof_packed(
&self,
public_params: &CompactPkePublicParams,
compute_load: ZkComputeLoad,
) -> crate::Result<ProvenCompactCiphertextList> {
self.inner
.build_with_proof_packed(public_params, compute_load)
.map(ProvenCompactCiphertextList)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[cfg(feature = "zk-pok-experimental")]
use crate::shortint::parameters::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M64;
#[cfg(feature = "zk-pok-experimental")]
use crate::zk::CompactPkeCrs;
use crate::{set_server_key, FheInt64, FheUint16, FheUint2, FheUint32};
#[test]
fn test_compact_list() {
let config = crate::ConfigBuilder::default().build();
let ck = crate::ClientKey::generate(config);
let sk = crate::ServerKey::new(&ck);
let pk = crate::CompactPublicKey::new(&ck);
set_server_key(sk);
let compact_list = CompactCiphertextList::builder(&pk)
.push(17u32)
.push(-1i64)
.push(false)
.push(true)
.push_with_num_bits(3u8, 2)
.unwrap()
.build_packed();
let serialized = bincode::serialize(&compact_list).unwrap();
let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap();
let expander = compact_list.expand().unwrap();
{
let a: FheUint32 = expander.get(0).unwrap().unwrap();
let b: FheInt64 = expander.get(1).unwrap().unwrap();
let c: FheBool = expander.get(2).unwrap().unwrap();
let d: FheBool = expander.get(3).unwrap().unwrap();
let e: FheUint2 = expander.get(4).unwrap().unwrap();
let a: u32 = a.decrypt(&ck);
assert_eq!(a, 17);
let b: i64 = b.decrypt(&ck);
assert_eq!(b, -1);
let c = c.decrypt(&ck);
assert!(!c);
let d = d.decrypt(&ck);
assert!(d);
let e: u8 = e.decrypt(&ck);
assert_eq!(e, 3);
assert!(expander.get::<FheBool>(5).is_none());
}
{
// Incorrect type
assert!(expander.get::<FheInt64>(0).unwrap().is_err());
// Correct type but wrong number of bits
assert!(expander.get::<FheUint16>(0).unwrap().is_err());
}
}
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_proven_compact_list() {
let config = crate::ConfigBuilder::with_custom_parameters(
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M64,
None,
)
.build();
let ck = crate::ClientKey::generate(config);
let pk = crate::CompactPublicKey::new(&ck);
let sks = crate::ServerKey::new(&ck);
set_server_key(sks);
// Intentionally low to that we test when multiple lists and proofs are needed
let crs = CompactPkeCrs::from_config(config, 32).unwrap();
let compact_list = ProvenCompactCiphertextList::builder(&pk)
.push(17u32)
.push(-1i64)
.push(false)
.push_with_num_bits(3u32, 2)
.unwrap()
.build_with_proof_packed(crs.public_params(), ZkComputeLoad::Proof)
.unwrap();
let serialized = bincode::serialize(&compact_list).unwrap();
let compact_list: ProvenCompactCiphertextList = bincode::deserialize(&serialized).unwrap();
let expander = compact_list
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
{
let a: FheUint32 = expander.get(0).unwrap().unwrap();
let b: FheInt64 = expander.get(1).unwrap().unwrap();
let c: FheBool = expander.get(2).unwrap().unwrap();
let d: FheUint2 = expander.get(3).unwrap().unwrap();
let a: u32 = a.decrypt(&ck);
assert_eq!(a, 17);
let b: i64 = b.decrypt(&ck);
assert_eq!(b, -1);
let c = c.decrypt(&ck);
assert!(!c);
let d: u8 = d.decrypt(&ck);
assert_eq!(d, 3);
assert!(expander.get::<FheBool>(4).is_none());
}
{
// Incorrect type
assert!(expander.get::<FheInt64>(0).unwrap().is_err());
// Correct type but wrong number of bits
assert!(expander.get::<FheUint16>(0).unwrap().is_err());
}
}
}

View File

@@ -1,3 +1,4 @@
use crate::Error;
use std::fmt::{Display, Formatter};
/// Unwrap 'Extension' trait
@@ -38,3 +39,9 @@ impl Display for UninitializedServerKey {
}
impl std::error::Error for UninitializedServerKey {}
impl From<UninitializedServerKey> for Error {
fn from(value: UninitializedServerKey) -> Self {
Self::new(format!("{value}"))
}
}

View File

@@ -16,8 +16,8 @@ pub(in crate::high_level_api) use signed::FheIntId;
pub(in crate::high_level_api) use unsigned::FheUintId;
// These are pub-exported so that their doc can appear in generated rust docs
use crate::shortint::MessageModulus;
pub use signed::{CompactFheInt, CompactFheIntList, CompressedFheInt, FheInt};
pub use unsigned::{CompactFheUint, CompactFheUintList, CompressedFheUint, FheUint};
pub use signed::{CompressedFheInt, FheInt};
pub use unsigned::{CompressedFheUint, FheUint};
pub mod oprf;
mod signed;

View File

@@ -1,234 +0,0 @@
use crate::conformance::{ListSizeConstraint, ParameterSetConformant};
use crate::high_level_api::integers::signed::base::FheIntConformanceParams;
use crate::high_level_api::integers::{FheInt, FheIntId};
use crate::integer::ciphertext::CompactCiphertextList;
use crate::integer::parameters::RadixCompactCiphertextListConformanceParams;
use crate::named::Named;
use crate::prelude::FheTryEncrypt;
use crate::shortint::PBSParameters;
use crate::{CompactPublicKey, ServerKey};
use std::marker::PhantomData;
/// Compact [FheInt]
///
/// Meant to save in storage space / transfer.
///
/// - A Compact type must be expanded using [expand](Self::expand) before it can be used.
/// - It is not possible to 'compact' an existing [FheInt]. Compacting can only be achieved at
/// encryption time by a [CompactPublicKey]
///
/// # Example
///
/// ```rust
/// use tfhe::prelude::*;
/// use tfhe::{generate_keys, CompactFheInt32, CompactPublicKey, ConfigBuilder, FheInt32};
///
/// let (client_key, _) = generate_keys(ConfigBuilder::default());
/// let compact_public_key = CompactPublicKey::try_new(&client_key).unwrap();
///
/// let compact = CompactFheInt32::encrypt(i32::MIN, &compact_public_key);
///
/// let ciphertext = compact.expand();
/// let decrypted: i32 = ciphertext.decrypt(&client_key);
/// assert_eq!(decrypted, i32::MIN);
/// ```
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct CompactFheInt<Id: FheIntId> {
pub(in crate::high_level_api::integers) list: CompactCiphertextList,
pub(in crate::high_level_api::integers) id: Id,
}
impl<Id> CompactFheInt<Id>
where
Id: FheIntId,
{
/// Expand to a [FheInt]
///
/// See [CompactFheInt] example.
pub fn expand(&self) -> FheInt<Id> {
let ct = self
.list
.expand_one::<crate::integer::SignedRadixCiphertext>();
FheInt::new(ct)
}
pub fn into_raw_parts(self) -> (CompactCiphertextList, Id) {
let Self { list, id } = self;
(list, id)
}
pub fn from_raw_parts(list: CompactCiphertextList, id: Id) -> Self {
Self { list, id }
}
}
impl<Id, T> FheTryEncrypt<T, CompactPublicKey> for CompactFheInt<Id>
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,
Id: FheIntId,
{
type Error = crate::Error;
fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let id = Id::default();
let ciphertext = key
.key
.try_encrypt_compact(&[value], Id::num_blocks(key.message_modulus()));
Ok(Self {
list: ciphertext,
id,
})
}
}
impl<Id: FheIntId> Named for CompactFheInt<Id> {
const NAME: &'static str = "high_level_api::CompactFheInt";
}
impl<Id: FheIntId> ParameterSetConformant for CompactFheInt<Id> {
type ParameterSet = FheIntConformanceParams<Id>;
fn is_conformant(&self, params: &FheIntConformanceParams<Id>) -> bool {
let params = params
.params
.to_ct_list_conformance_parameters(ListSizeConstraint::exact_size(1));
self.list.is_conformant(&params)
}
}
/// Compact list of [FheInt]
///
/// Meant to save in storage space / transfer.
///
/// - A Compact type must be expanded using [expand](Self::expand) before it can be used.
/// - It is not possible to 'compact' an existing [FheInt]. Compacting can only be achieved at
/// encryption time by a [CompactPublicKey]
///
/// # Example
///
/// ```rust
/// use tfhe::prelude::*;
/// use tfhe::{generate_keys, CompactFheInt32List, CompactPublicKey, ConfigBuilder, FheInt32};
///
/// let (client_key, _) = generate_keys(ConfigBuilder::default());
/// let compact_public_key = CompactPublicKey::try_new(&client_key).unwrap();
///
/// let clears = vec![i32::MAX, i32::MIN, 0, 1];
/// let compact = CompactFheInt32List::encrypt(&clears, &compact_public_key);
/// assert_eq!(compact.len(), clears.len());
///
/// let ciphertexts = compact.expand();
/// let decrypted: Vec<i32> = ciphertexts
/// .into_iter()
/// .map(|ciphertext| ciphertext.decrypt(&client_key))
/// .collect();
/// assert_eq!(decrypted, clears);
/// ```
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct CompactFheIntList<Id: FheIntId> {
pub(in crate::high_level_api::integers) list: CompactCiphertextList,
pub(in crate::high_level_api::integers) id: Id,
}
impl<Id> CompactFheIntList<Id>
where
Id: FheIntId,
{
/// Returns the number of element in the compact list
pub fn len(&self) -> usize {
self.list.ciphertext_count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn into_raw_parts(self) -> (CompactCiphertextList, Id) {
let Self { list, id } = self;
(list, id)
}
pub fn from_raw_parts(list: CompactCiphertextList, id: Id) -> Self {
Self { list, id }
}
/// Expand to a Vec<[FheInt]>
///
/// See [CompactFheIntList] example.
pub fn expand(&self) -> Vec<FheInt<Id>> {
self.list
.expand::<crate::integer::SignedRadixCiphertext>()
.into_iter()
.map(|ct| FheInt::new(ct))
.collect::<Vec<_>>()
}
}
impl<'a, Id, T> FheTryEncrypt<&'a [T], CompactPublicKey> for CompactFheIntList<Id>
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,
Id: FheIntId,
{
type Error = crate::Error;
fn try_encrypt(values: &'a [T], key: &CompactPublicKey) -> Result<Self, Self::Error> {
let id = Id::default();
let ciphertext = key
.key
.try_encrypt_compact(values, Id::num_blocks(key.message_modulus()));
Ok(Self {
list: ciphertext,
id,
})
}
}
impl<Id: FheIntId> Named for CompactFheIntList<Id> {
const NAME: &'static str = "high_level_api::CompactFheIntList";
}
pub struct CompactFheIntListConformanceParams<Id: FheIntId> {
params: RadixCompactCiphertextListConformanceParams,
_id: PhantomData<Id>,
}
impl<Id: FheIntId, P: Into<PBSParameters>> From<(P, ListSizeConstraint)>
for CompactFheIntListConformanceParams<Id>
{
fn from((params, len_constraint): (P, ListSizeConstraint)) -> Self {
let params = params.into();
Self {
params: RadixCompactCiphertextListConformanceParams {
shortint_params: params.to_shortint_conformance_param(),
num_blocks_per_integer: Id::num_blocks(params.message_modulus()),
num_integers_constraint: len_constraint,
},
_id: PhantomData,
}
}
}
impl<Id: FheIntId> From<(&ServerKey, ListSizeConstraint)>
for CompactFheIntListConformanceParams<Id>
{
fn from((sk, len_constraint): (&ServerKey, ListSizeConstraint)) -> Self {
Self {
params: RadixCompactCiphertextListConformanceParams {
shortint_params: sk.key.pbs_key().key.conformance_params(),
num_blocks_per_integer: Id::num_blocks(sk.key.pbs_key().message_modulus()),
num_integers_constraint: len_constraint,
},
_id: PhantomData,
}
}
}
impl<Id: FheIntId> ParameterSetConformant for CompactFheIntList<Id> {
type ParameterSet = CompactFheIntListConformanceParams<Id>;
fn is_conformant(&self, params: &CompactFheIntListConformanceParams<Id>) -> bool {
self.list.is_conformant(&params.params)
}
}

View File

@@ -1,5 +1,4 @@
mod base;
mod compact;
mod compressed;
mod encrypt;
@@ -10,11 +9,8 @@ mod scalar_ops;
mod static_;
#[cfg(test)]
mod tests;
#[cfg(feature = "zk-pok-experimental")]
mod zk;
pub use base::{FheInt, FheIntId};
pub use compact::{CompactFheInt, CompactFheIntList};
pub use compressed::CompressedFheInt;
expand_pub_use_fhe_type!(

View File

@@ -1,9 +1,4 @@
#[cfg(feature = "zk-pok-experimental")]
use super::zk::{ProvenCompactFheInt, ProvenCompactFheIntList};
use crate::high_level_api::integers::signed::base::{FheInt, FheIntConformanceParams, FheIntId};
use crate::high_level_api::integers::signed::compact::{
CompactFheInt, CompactFheIntList, CompactFheIntListConformanceParams,
};
use crate::high_level_api::integers::signed::compressed::CompressedFheInt;
use crate::high_level_api::IntegerId;
use serde::{Deserialize, Serialize};
@@ -45,27 +40,9 @@ macro_rules! static_int_type {
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compressed FheInt $num_bits>] = CompressedFheInt<[<FheInt $num_bits Id>]>;
#[doc = concat!("A compact signed integer type with ", stringify!($num_bits), " bits")]
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact FheInt $num_bits>] = CompactFheInt<[<FheInt $num_bits Id>]>;
#[doc = concat!("A compact list of signed integer type with ", stringify!($num_bits), " bits")]
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact FheInt $num_bits List>] = CompactFheIntList<[<FheInt $num_bits Id>]>;
// Conformance Params
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<FheInt $num_bits ConformanceParams>] = FheIntConformanceParams<[<FheInt $num_bits Id>]>;
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact FheInt $num_bits ListConformanceParams>] = CompactFheIntListConformanceParams<[<FheInt $num_bits Id>]>;
// Zero-knowledge Stuff
#[cfg(feature = "zk-pok-experimental")]
pub type [<ProvenCompactFheInt $num_bits>] = ProvenCompactFheInt<[<FheInt $num_bits Id>]>;
#[cfg(feature = "zk-pok-experimental")]
pub type [<ProvenCompactFheInt $num_bits List>] = ProvenCompactFheIntList<[<FheInt $num_bits Id>]>;
}
};
}

View File

@@ -1,13 +1,11 @@
use crate::conformance::ListSizeConstraint;
use crate::integer::I256;
use crate::prelude::*;
use crate::safe_deserialization::safe_deserialize_conformant;
use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
use crate::{
generate_keys, set_server_key, ClientKey, CompactFheInt32, CompactFheInt32List,
CompactFheInt32ListConformanceParams, CompactPublicKey, CompressedFheInt16, CompressedFheInt32,
Config, ConfigBuilder, FheInt16, FheInt256, FheInt32, FheInt32ConformanceParams, FheInt64,
FheInt8, FheUint64, FheUint8,
generate_keys, set_server_key, ClientKey, CompactPublicKey, CompressedFheInt16,
CompressedFheInt32, Config, ConfigBuilder, FheInt16, FheInt256, FheInt32,
FheInt32ConformanceParams, FheInt64, FheInt8, FheUint64, FheUint8,
};
use rand::prelude::*;
@@ -578,66 +576,6 @@ fn test_compact_public_key_small() {
assert_eq!(clear, -123i8);
}
#[test]
fn test_compact_public_key_list_big() {
let config = ConfigBuilder::default()
.use_custom_parameters(
crate::shortint::parameters::classic::compact_pk::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS,
None,
)
.build();
test_compact_public_key_list(config);
}
#[test]
fn test_compact_public_key_list_small() {
let config = ConfigBuilder::default()
.use_custom_parameters(
crate::shortint::parameters::classic::compact_pk
::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
None,
)
.build();
test_compact_public_key_list(config);
}
fn test_compact_public_key_list(config: Config) {
let (client_key, server_key) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
let mut rng = rand::thread_rng();
let clear_xs = (0..50).map(|_| rng.gen::<i32>()).collect::<Vec<_>>();
let clear_ys = (0..50).map(|_| rng.gen::<i32>()).collect::<Vec<_>>();
let compacted_xs = CompactFheInt32List::encrypt(&clear_xs, &public_key);
let compacted_ys = CompactFheInt32List::encrypt(&clear_ys, &public_key);
let exs = compacted_xs.expand();
let eys = compacted_ys.expand();
set_server_key(server_key);
let encrypted_results = exs.iter().zip(eys).map(|(x, y)| x + y).collect::<Vec<_>>();
let clear_results = clear_xs
.iter()
.zip(clear_ys)
.map(|(x, y)| x + y)
.collect::<Vec<_>>();
for (encrypted, clear) in encrypted_results.iter().zip(clear_results) {
let decrypted: i32 = encrypted.decrypt(&client_key);
assert_eq!(clear, decrypted);
}
let compact_single = CompactFheInt32::encrypt(clear_xs[0], &public_key);
let a = compact_single.expand();
let decrypted: i32 = a.decrypt(&client_key);
assert_eq!(clear_xs[0], decrypted);
}
fn test_case_leading_trailing_zeros_ones(cks: &ClientKey) {
let mut rng = thread_rng();
for _ in 0..5 {
@@ -742,108 +680,3 @@ fn test_safe_deserialize_conformant_compressed_fhe_int32() {
let decrypted: i32 = deserialized_a.decompress().decrypt(&client_key);
assert_eq!(decrypted, clear_a);
}
#[test]
fn test_safe_deserialize_conformant_compact_fhe_int32() {
let block_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
let (client_key, server_key) =
generate_keys(ConfigBuilder::with_custom_parameters(block_params, None));
set_server_key(server_key.clone());
let pk = CompactPublicKey::new(&client_key);
let clear_a = random::<i32>();
let a = CompactFheInt32::encrypt(clear_a, &pk);
let mut serialized = vec![];
assert!(crate::safe_serialize(&a, &mut serialized, 1 << 20).is_ok());
let params = FheInt32ConformanceParams::from(&server_key);
let deserialized_a =
safe_deserialize_conformant::<CompactFheInt32>(serialized.as_slice(), 1 << 20, &params)
.unwrap();
let decrypted: i32 = deserialized_a.expand().decrypt(&client_key);
assert_eq!(decrypted, clear_a);
let params = FheInt32ConformanceParams::from(block_params);
assert!(deserialized_a.is_conformant(&params));
}
#[test]
fn test_safe_deserialize_conformant_compact_fhe_int32_list() {
let block_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
let (client_key, server_key) =
generate_keys(ConfigBuilder::with_custom_parameters(block_params, None));
set_server_key(server_key.clone());
let pk = CompactPublicKey::new(&client_key);
let clears = [random::<i32>(), random::<i32>(), random::<i32>()];
let compact_list = CompactFheInt32List::encrypt(&clears, &pk);
let mut serialized = vec![];
assert!(crate::safe_serialize(&compact_list, &mut serialized, 1 << 20).is_ok());
let params = CompactFheInt32ListConformanceParams::from((
&server_key,
ListSizeConstraint::exact_size(3),
));
let deserialized_list =
safe_deserialize_conformant::<CompactFheInt32List>(serialized.as_slice(), 1 << 20, &params)
.unwrap();
let expanded_list = deserialized_list.expand();
for (fhe_uint, expected) in expanded_list.iter().zip(clears.into_iter()) {
let decrypted: i32 = fhe_uint.decrypt(&client_key);
assert_eq!(decrypted, expected);
}
let params = CompactFheInt32ListConformanceParams::from((
block_params,
ListSizeConstraint::exact_size(3),
));
assert!(deserialized_list.is_conformant(&params));
}
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_fhe_int_zk() {
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
let params =
crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40;
let config = ConfigBuilder::with_custom_parameters(params, None).build();
let crs = CompactPkeCrs::from_config(config, 32).unwrap();
let ck = ClientKey::generate(config);
let pk = CompactPublicKey::new(&ck);
let msg = random::<i32>();
let proven_compact_fhe_uint = crate::ProvenCompactFheInt32::try_encrypt(
msg,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_uint = proven_compact_fhe_uint
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted: i32 = fhe_uint.decrypt(&ck);
assert_eq!(decrypted, msg);
let messages = (0..4).map(|_| random()).collect::<Vec<i32>>();
let proven_compact_fhe_uint_list = crate::ProvenCompactFheInt32List::try_encrypt(
&messages,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_uints = proven_compact_fhe_uint_list
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = fhe_uints
.iter()
.map(|fb| fb.decrypt(&ck))
.collect::<Vec<i32>>();
assert_eq!(decrypted.as_slice(), &messages);
}

View File

@@ -1,140 +0,0 @@
use crate::core_crypto::commons::math::random::{Deserialize, Serialize};
use crate::core_crypto::prelude::SignedNumeric;
use crate::high_level_api::integers::FheIntId;
use crate::integer::block_decomposition::DecomposableInto;
use crate::integer::{ProvenCompactCiphertextList, SignedRadixCiphertext};
use crate::named::Named;
use crate::zk::{CompactPkePublicParams, ZkComputeLoad, ZkVerificationOutCome};
use crate::{CompactPublicKey, FheInt};
/// A `CompactFheInt` tied to a Zero-Knowledge proof
///
/// The zero-knowledge proof allows to verify that the ciphertext is correctly
/// encrypted.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactFheInt<Id: FheIntId> {
inner: ProvenCompactCiphertextList,
_id: Id,
}
impl<Id: FheIntId> Named for ProvenCompactFheInt<Id> {
const NAME: &'static str = "high_level_api::ProvenCompactFheUintList";
}
impl<Id> ProvenCompactFheInt<Id>
where
Id: FheIntId,
{
/// Encrypts the message while also generating the zero-knowledge proof
pub fn try_encrypt<Clear>(
value: Clear,
public_params: &CompactPkePublicParams,
key: &CompactPublicKey,
load: ZkComputeLoad,
) -> crate::Result<Self>
where
Clear: DecomposableInto<u64> + SignedNumeric,
{
let inner = key.key.key.encrypt_and_prove_radix_compact(
&[value],
Id::num_blocks(key.key.key.key.parameters.message_modulus()),
public_params,
load,
)?;
Ok(Self {
inner,
_id: Id::default(),
})
}
/// Verifies the ciphertext and the proof
///
/// If the proof and ciphertext are valid, it returns an `Ok` with
/// the underlying `FheInt`
pub fn verify_and_expand(
self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<FheInt<Id>> {
let expanded_inner = self
.inner
.verify_and_expand_one::<SignedRadixCiphertext>(public_params, &public_key.key.key)?;
Ok(FheInt::new(expanded_inner))
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
self.inner.verify(public_params, &public_key.key.key)
}
}
/// A `CompactFheIntList` tied to a Zero-Knowledge proof
///
/// The zero-knowledge proof allows to verify that the ciphertext list is correctly
/// encrypted.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactFheIntList<Id: FheIntId> {
inner: ProvenCompactCiphertextList,
_id: Id,
}
impl<Id: FheIntId> Named for ProvenCompactFheIntList<Id> {
const NAME: &'static str = "high_level_api::ProvenCompactFheIntList";
}
impl<Id> ProvenCompactFheIntList<Id>
where
Id: FheIntId,
{
/// Encrypts the message while also generating the zero-knowledge proof
pub fn try_encrypt<Clear>(
values: &[Clear],
public_params: &CompactPkePublicParams,
key: &CompactPublicKey,
load: ZkComputeLoad,
) -> crate::Result<Self>
where
Clear: DecomposableInto<u64> + SignedNumeric,
{
let inner = key.key.key.encrypt_and_prove_radix_compact(
values,
Id::num_blocks(key.key.key.key.parameters.message_modulus()),
public_params,
load,
)?;
Ok(Self {
inner,
_id: Id::default(),
})
}
pub fn len(&self) -> usize {
self.inner.ciphertext_count()
}
/// Verifies the ciphertext and the proof
///
/// If the proof and ciphertext are valid, it returns an `Ok` with
/// the underlying `FheInt`s.
pub fn verify_and_expand(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<Vec<FheInt<Id>>> {
let expanded_inners = self
.inner
.verify_and_expand::<SignedRadixCiphertext>(public_params, &public_key.key.key)?;
Ok(expanded_inners.into_iter().map(FheInt::new).collect())
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
self.inner.verify(public_params, &public_key.key.key)
}
}

View File

@@ -1,477 +0,0 @@
use crate::conformance::{ListSizeConstraint, ParameterSetConformant};
use crate::high_level_api::integers::unsigned::base::{
FheUint, FheUintConformanceParams, FheUintId,
};
use crate::high_level_api::traits::FheTryEncrypt;
use crate::integer::ciphertext::CompactCiphertextList;
use crate::integer::parameters::RadixCompactCiphertextListConformanceParams;
use crate::named::Named;
use crate::shortint::PBSParameters;
use crate::{CompactPublicKey, ServerKey};
use std::marker::PhantomData;
/// Compact [FheUint]
///
/// Meant to save in storage space / transfer.
///
/// - A Compact type must be expanded using [expand](Self::expand) before it can be used.
/// - It is not possible to 'compact' an existing [FheUint]. Compacting can only be achieved at
/// encryption time by a [CompactPublicKey]
///
/// # Example
///
/// ```rust
/// use tfhe::prelude::*;
/// use tfhe::{generate_keys, CompactFheUint32, CompactPublicKey, ConfigBuilder, FheUint32};
///
/// let (client_key, _) = generate_keys(ConfigBuilder::default());
/// let compact_public_key = CompactPublicKey::try_new(&client_key).unwrap();
///
/// let compact = CompactFheUint32::encrypt(u32::MAX, &compact_public_key);
///
/// let ciphertext = compact.expand();
/// let decrypted: u32 = ciphertext.decrypt(&client_key);
/// assert_eq!(decrypted, u32::MAX);
/// ```
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct CompactFheUint<Id: FheUintId> {
pub(in crate::high_level_api::integers) list: CompactCiphertextList,
pub(in crate::high_level_api::integers) id: Id,
}
impl<Id> CompactFheUint<Id>
where
Id: FheUintId,
{
/// Expand to a [FheUint]
///
/// See [CompactFheUint] example.
pub fn expand(&self) -> FheUint<Id> {
let ct: crate::integer::RadixCiphertext = self.list.expand_one();
let mut ct = FheUint::new(ct);
ct.move_to_device_of_server_key_if_set();
ct
}
pub fn into_raw_parts(self) -> (CompactCiphertextList, Id) {
let Self { list, id } = self;
(list, id)
}
pub fn from_raw_parts(list: CompactCiphertextList, id: Id) -> Self {
Self { list, id }
}
}
impl<Id, T> FheTryEncrypt<T, CompactPublicKey> for CompactFheUint<Id>
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,
Id: FheUintId,
{
type Error = crate::Error;
fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let ciphertext = key
.key
.try_encrypt_compact(&[value], Id::num_blocks(key.message_modulus()));
Ok(Self {
list: ciphertext,
id: Id::default(),
})
}
}
impl<Id: FheUintId> Named for CompactFheUint<Id> {
const NAME: &'static str = "high_level_api::CompactFheUint";
}
impl<Id: FheUintId> ParameterSetConformant for CompactFheUint<Id> {
type ParameterSet = FheUintConformanceParams<Id>;
fn is_conformant(&self, params: &FheUintConformanceParams<Id>) -> bool {
let params = params
.params
.to_ct_list_conformance_parameters(ListSizeConstraint::exact_size(1));
self.list.is_conformant(&params)
}
}
/// Compact list of [FheUint]
///
/// Meant to save in storage space / transfer.
///
/// - A Compact type must be expanded using [expand](Self::expand) before it can be used.
/// - It is not possible to 'compact' an existing [FheUint]. Compacting can only be achieved at
/// encryption time by a [CompactPublicKey]
///
/// # Example
///
/// ```rust
/// use tfhe::prelude::*;
/// use tfhe::{generate_keys, CompactFheUint32List, CompactPublicKey, ConfigBuilder, FheUint32};
///
/// let (client_key, _) = generate_keys(ConfigBuilder::default());
/// let compact_public_key = CompactPublicKey::try_new(&client_key).unwrap();
///
/// let clears = vec![u32::MAX, 0, 1];
/// let compact = CompactFheUint32List::encrypt(&clears, &compact_public_key);
/// assert_eq!(compact.len(), clears.len());
///
/// let ciphertexts = compact.expand();
/// let decrypted: Vec<u32> = ciphertexts
/// .into_iter()
/// .map(|ciphertext| ciphertext.decrypt(&client_key))
/// .collect();
/// assert_eq!(decrypted, clears);
/// ```
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct CompactFheUintList<Id: FheUintId> {
pub(in crate::high_level_api::integers) list: CompactCiphertextList,
pub(in crate::high_level_api::integers) id: Id,
}
impl<Id> CompactFheUintList<Id>
where
Id: FheUintId,
{
/// Returns the number of element in the compact list
pub fn len(&self) -> usize {
self.list.ciphertext_count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn into_raw_parts(self) -> (CompactCiphertextList, Id) {
let Self { list, id } = self;
(list, id)
}
pub fn from_raw_parts(list: CompactCiphertextList, id: Id) -> Self {
Self { list, id }
}
/// Expand to a Vec<[FheUint]>
///
/// See [CompactFheUintList] example.
pub fn expand(&self) -> Vec<FheUint<Id>> {
self.list
.expand()
.into_iter()
.map(|ct: crate::integer::RadixCiphertext| {
let mut ct = FheUint::new(ct);
ct.move_to_device_of_server_key_if_set();
ct
})
.collect::<Vec<_>>()
}
}
impl<'a, Id, T> FheTryEncrypt<&'a [T], CompactPublicKey> for CompactFheUintList<Id>
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,
Id: FheUintId,
{
type Error = crate::Error;
fn try_encrypt(values: &'a [T], key: &CompactPublicKey) -> Result<Self, Self::Error> {
let ciphertext = key
.key
.try_encrypt_compact(values, Id::num_blocks(key.message_modulus()));
Ok(Self {
list: ciphertext,
id: Id::default(),
})
}
}
impl<Id: FheUintId> Named for CompactFheUintList<Id> {
const NAME: &'static str = "high_level_api::CompactFheUintList";
}
pub struct CompactFheUintListConformanceParams<Id: FheUintId> {
params: RadixCompactCiphertextListConformanceParams,
_id: PhantomData<Id>,
}
impl<Id: FheUintId, P: Into<PBSParameters>> From<(P, ListSizeConstraint)>
for CompactFheUintListConformanceParams<Id>
{
fn from((params, len_constraint): (P, ListSizeConstraint)) -> Self {
let params = params.into();
let parameter_set = RadixCompactCiphertextListConformanceParams {
shortint_params: params.to_shortint_conformance_param(),
num_blocks_per_integer: Id::num_blocks(params.message_modulus()),
num_integers_constraint: len_constraint,
};
Self {
params: parameter_set,
_id: PhantomData,
}
}
}
impl<Id: FheUintId> From<(&ServerKey, ListSizeConstraint)>
for CompactFheUintListConformanceParams<Id>
{
fn from((sk, len_constraint): (&ServerKey, ListSizeConstraint)) -> Self {
Self {
params: RadixCompactCiphertextListConformanceParams {
shortint_params: sk.key.pbs_key().key.conformance_params(),
num_blocks_per_integer: Id::num_blocks(sk.key.pbs_key().message_modulus()),
num_integers_constraint: len_constraint,
},
_id: PhantomData,
}
}
}
impl<Id: FheUintId> ParameterSetConformant for CompactFheUintList<Id> {
type ParameterSet = CompactFheUintListConformanceParams<Id>;
fn is_conformant(&self, params: &CompactFheUintListConformanceParams<Id>) -> bool {
self.list.is_conformant(&params.params)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::core_crypto::prelude::UnsignedInteger;
use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
use crate::shortint::{CiphertextModulus, PBSOrder};
use crate::{
generate_keys, set_server_key, CompactFheUint8, CompactFheUint8List, ConfigBuilder,
};
use rand::{thread_rng, Rng};
type ParameterAccessor<Ct, T> = dyn Fn(&mut Ct) -> &mut T;
type ParameterModifier<'a, Ct> = dyn Fn(&mut Ct) + 'a;
fn change_parameters<Ct, T: UnsignedInteger>(
func: &ParameterAccessor<Ct, T>,
) -> [Box<ParameterModifier<'_, Ct>>; 3] {
[
Box::new(|ct| *func(ct) = T::ZERO),
Box::new(|ct| *func(ct) = func(ct).wrapping_add(T::ONE)),
Box::new(|ct| *func(ct) = func(ct).wrapping_sub(T::ONE)),
]
}
#[test]
fn test_invalid_generic_compact_integer() {
type Ct = CompactFheUint8;
let config = ConfigBuilder::default().build();
let (client_key, _server_key) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
let ct = CompactFheUint8::try_encrypt(0, &public_key).unwrap();
assert!(ct.is_conformant(&FheUintConformanceParams::from(
PARAM_MESSAGE_2_CARRY_2_KS_PBS
)));
let breaker_lists = [
change_parameters(&|ct: &mut Ct| &mut ct.list.num_blocks_per_integer),
change_parameters(&|ct: &mut Ct| &mut ct.list.ct_list.message_modulus.0),
change_parameters(&|ct: &mut Ct| &mut ct.list.ct_list.carry_modulus.0),
change_parameters(&|ct: &mut Ct| ct.list.ct_list.degree.as_mut()),
change_parameters(&|ct: &mut Ct| {
&mut ct.list.ct_list.ct_list.get_mut_lwe_ciphertext_count().0
}),
change_parameters(&|ct: &mut Ct| &mut ct.list.ct_list.ct_list.get_mut_lwe_size().0),
];
for breaker_list in breaker_lists {
for breaker in breaker_list {
let mut ct_clone = ct.clone();
breaker(&mut ct_clone);
assert!(ct.is_conformant(&FheUintConformanceParams::from(
PARAM_MESSAGE_2_CARRY_2_KS_PBS
)));
}
}
let breakers2: Vec<&ParameterModifier<'_, Ct>> = vec![
&|ct: &mut Ct| ct.list.ct_list.pbs_order = PBSOrder::BootstrapKeyswitch,
&|ct: &mut Ct| {
*ct.list.ct_list.ct_list.get_mut_ciphertext_modulus() =
CiphertextModulus::try_new_power_of_2(1).unwrap();
},
&|ct: &mut Ct| {
*ct.list.ct_list.ct_list.get_mut_ciphertext_modulus() =
CiphertextModulus::try_new(3).unwrap();
},
&|ct: &mut Ct| {
ct.list.ct_list.ct_list.get_mut_container().pop();
},
&|ct: &mut Ct| ct.list.ct_list.ct_list.get_mut_container().push(0),
];
for breaker in breakers2 {
let mut ct_clone = ct.clone();
breaker(&mut ct_clone);
assert!(ct.is_conformant(&FheUintConformanceParams::from(
PARAM_MESSAGE_2_CARRY_2_KS_PBS
)));
}
}
#[test]
fn test_invalid_generic_compact_integer_list() {
type Ct = CompactFheUint8List;
let config = ConfigBuilder::default().build();
let (client_key, _server_key) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
let ct = Ct::try_encrypt(&[0, 1], &public_key).unwrap();
let params = CompactFheUintListConformanceParams::from((
PARAM_MESSAGE_2_CARRY_2_KS_PBS,
ListSizeConstraint::exact_size(2),
));
assert!(ct.is_conformant(&params));
let breaker_lists = [
change_parameters(&|ct: &mut Ct| &mut ct.list.num_blocks_per_integer),
change_parameters(&|ct: &mut Ct| &mut ct.list.ct_list.message_modulus.0),
change_parameters(&|ct: &mut Ct| &mut ct.list.ct_list.carry_modulus.0),
change_parameters(&|ct: &mut Ct| ct.list.ct_list.degree.as_mut()),
change_parameters(&|ct: &mut Ct| {
&mut ct.list.ct_list.ct_list.get_mut_lwe_ciphertext_count().0
}),
change_parameters(&|ct: &mut Ct| &mut ct.list.ct_list.ct_list.get_mut_lwe_size().0),
];
for breaker_list in breaker_lists {
for breaker in breaker_list {
let mut ct_clone = ct.clone();
breaker(&mut ct_clone);
assert!(!ct_clone.is_conformant(&params));
}
}
let breakers2: Vec<&ParameterModifier<'_, Ct>> = vec![
&|ct: &mut Ct| ct.list.ct_list.pbs_order = PBSOrder::BootstrapKeyswitch,
&|ct: &mut Ct| {
*ct.list.ct_list.ct_list.get_mut_ciphertext_modulus() =
CiphertextModulus::try_new_power_of_2(1).unwrap();
},
&|ct: &mut Ct| {
*ct.list.ct_list.ct_list.get_mut_ciphertext_modulus() =
CiphertextModulus::try_new(3).unwrap();
},
&|ct: &mut Ct| {
ct.list.ct_list.ct_list.get_mut_container().pop();
},
&|ct: &mut Ct| ct.list.ct_list.ct_list.get_mut_container().push(0),
];
for breaker in breakers2 {
let mut ct_clone = ct.clone();
breaker(&mut ct_clone);
assert!(!ct_clone.is_conformant(&params));
}
}
#[test]
fn test_valid_generic_compact_integer() {
let config = ConfigBuilder::default().build();
let (client_key, server_key) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
set_server_key(server_key);
let ct = CompactFheUint8::try_encrypt(0, &public_key).unwrap();
assert!(ct.is_conformant(&FheUintConformanceParams::from(
PARAM_MESSAGE_2_CARRY_2_KS_PBS
)));
let mut rng = thread_rng();
for _ in 0..10 {
let mut ct_clone = ct.clone();
ct_clone
.list
.ct_list
.ct_list
.get_mut_container()
.fill_with(|| rng.gen::<u64>());
assert!(ct.is_conformant(&FheUintConformanceParams::from(
PARAM_MESSAGE_2_CARRY_2_KS_PBS
)));
let mut ct_clone_expanded = ct_clone.expand();
ct_clone_expanded += &ct_clone_expanded.clone();
}
}
#[test]
fn test_valid_generic_compact_integer_list() {
let config = ConfigBuilder::default().build();
let (client_key, server_key) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
set_server_key(server_key);
let ct = CompactFheUint8List::try_encrypt(&[0, 1], &public_key).unwrap();
let params = CompactFheUintListConformanceParams::from((
PARAM_MESSAGE_2_CARRY_2_KS_PBS,
ListSizeConstraint::exact_size(2),
));
assert!(ct.is_conformant(&params));
let mut rng = thread_rng();
for _ in 0..10 {
let mut ct_clone = ct.clone();
ct_clone
.list
.ct_list
.ct_list
.get_mut_container()
.fill_with(|| rng.gen::<u64>());
assert!(ct_clone.is_conformant(&params));
let mut ct_clone_expanded = ct_clone.expand();
// We check that this conformant random ciphertext can be used to do operations without
// panicking
for i in &mut ct_clone_expanded {
*i += i.clone();
}
}
}
}

View File

@@ -7,11 +7,9 @@ expand_pub_use_fhe_type!(
};
);
pub use compact::{CompactFheUint, CompactFheUintList};
pub use compressed::CompressedFheUint;
mod base;
mod compact;
mod compressed;
mod static_;
mod wopbs;
@@ -23,5 +21,3 @@ mod overflowing_ops;
pub(crate) mod scalar_ops;
#[cfg(test)]
mod tests;
#[cfg(feature = "zk-pok-experimental")]
mod zk;

View File

@@ -1,11 +1,6 @@
#[cfg(feature = "zk-pok-experimental")]
use super::zk::{ProvenCompactFheUint, ProvenCompactFheUintList};
use crate::high_level_api::integers::unsigned::base::{
FheUint, FheUintConformanceParams, FheUintId,
};
use crate::high_level_api::integers::unsigned::compact::{
CompactFheUint, CompactFheUintList, CompactFheUintListConformanceParams,
};
use crate::high_level_api::integers::unsigned::compressed::CompressedFheUint;
use crate::high_level_api::integers::IntegerId;
use serde::{Deserialize, Serialize};
@@ -46,24 +41,9 @@ macro_rules! static_int_type {
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compressed FheUint $num_bits>] = CompressedFheUint<[<FheUint $num_bits Id>]>;
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact FheUint $num_bits>] = CompactFheUint<[<FheUint $num_bits Id>]>;
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact FheUint $num_bits List>] = CompactFheUintList<[<FheUint $num_bits Id>]>;
// Conformance Params
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<FheUint $num_bits ConformanceParams>] = FheUintConformanceParams<[<FheUint $num_bits Id>]>;
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact FheUint $num_bits ListConformanceParams>] = CompactFheUintListConformanceParams<[<FheUint $num_bits Id>]>;
#[cfg(feature = "zk-pok-experimental")]
pub type [<ProvenCompactFheUint $num_bits>] = ProvenCompactFheUint<[<FheUint $num_bits Id>]>;
#[cfg(feature = "zk-pok-experimental")]
pub type [<ProvenCompactFheUint $num_bits List>] = ProvenCompactFheUintList<[<FheUint $num_bits Id>]>;
}
};
}

View File

@@ -6,10 +6,9 @@ use crate::safe_deserialization::safe_deserialize_conformant;
use crate::shortint::parameters::classic::compact_pk::*;
use crate::shortint::parameters::*;
use crate::{
ClientKey, CompactFheUint32, CompactFheUint32List, CompactFheUint32ListConformanceParams,
CompactPublicKey, CompressedFheUint16, CompressedFheUint256, CompressedFheUint32,
CompressedPublicKey, Config, FheInt16, FheInt32, FheInt8, FheUint128, FheUint16, FheUint256,
FheUint32, FheUint32ConformanceParams,
ClientKey, CompactCiphertextList, CompactCiphertextListConformanceParams, CompactPublicKey,
CompressedFheUint16, CompressedFheUint256, CompressedFheUint32, CompressedPublicKey, FheInt16,
FheInt32, FheInt8, FheUint128, FheUint16, FheUint256, FheUint32, FheUint32ConformanceParams,
};
use rand::prelude::*;
@@ -205,58 +204,6 @@ fn test_compact_public_key_big() {
assert_eq!(clear, 255u8);
}
#[test]
fn test_compact_public_key_list_big() {
let config = ConfigBuilder::default()
.use_custom_parameters(PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS, None)
.build();
test_compact_public_key_list(config);
}
#[test]
fn test_compact_public_key_list_small() {
let config = ConfigBuilder::default()
.use_custom_parameters(PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS, None)
.build();
test_compact_public_key_list(config);
}
fn test_compact_public_key_list(config: Config) {
let (client_key, server_key) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
let mut rng = rand::thread_rng();
let clear_xs = (0..50).map(|_| rng.gen::<u32>()).collect::<Vec<_>>();
let clear_ys = (0..50).map(|_| rng.gen::<u32>()).collect::<Vec<_>>();
let compacted_xs = CompactFheUint32List::encrypt(&clear_xs, &public_key);
let compacted_ys = CompactFheUint32List::encrypt(&clear_ys, &public_key);
let exs = compacted_xs.expand();
let eys = compacted_ys.expand();
set_server_key(server_key);
let encrypted_results = exs.iter().zip(eys).map(|(x, y)| x + y).collect::<Vec<_>>();
let clear_results = clear_xs
.iter()
.zip(clear_ys)
.map(|(x, y)| x + y)
.collect::<Vec<_>>();
for (encrypted, clear) in encrypted_results.iter().zip(clear_results) {
let decrypted: u32 = encrypted.decrypt(&client_key);
assert_eq!(clear, decrypted);
}
let compact_single = CompactFheUint32::encrypt(clear_xs[0], &public_key);
let a = compact_single.expand();
let decrypted: u32 = a.decrypt(&client_key);
assert_eq!(clear_xs[0], decrypted);
}
#[test]
fn test_compact_public_key_small() {
let config = ConfigBuilder::default()
@@ -488,104 +435,33 @@ fn test_safe_deserialize_conformant_compact_fhe_uint32() {
let block_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
let (client_key, server_key) =
generate_keys(ConfigBuilder::with_custom_parameters(block_params, None));
set_server_key(server_key.clone());
let pk = CompactPublicKey::new(&client_key);
let clear_a = random::<u32>();
let a = CompactFheUint32::encrypt(clear_a, &pk);
let mut serialized = vec![];
assert!(crate::safe_serialize(&a, &mut serialized, 1 << 20).is_ok());
let params = FheUint32ConformanceParams::from(&server_key);
let deserialized_a =
safe_deserialize_conformant::<CompactFheUint32>(serialized.as_slice(), 1 << 20, &params)
.unwrap();
let decrypted: u32 = deserialized_a.expand().decrypt(&client_key);
assert_eq!(decrypted, clear_a);
assert!(deserialized_a.is_conformant(&FheUint32ConformanceParams::from(block_params)));
}
#[test]
fn test_safe_deserialize_conformant_compact_fhe_uint32_list() {
let block_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
let (client_key, server_key) =
generate_keys(ConfigBuilder::with_custom_parameters(block_params, None));
set_server_key(server_key.clone());
set_server_key(server_key);
let pk = CompactPublicKey::new(&client_key);
let clears = [random::<u32>(), random::<u32>(), random::<u32>()];
let compact_list = CompactFheUint32List::encrypt(&clears, &pk);
let a = CompactCiphertextList::builder(&pk)
.extend(clears.iter().copied())
.build();
let mut serialized = vec![];
assert!(crate::safe_serialize(&compact_list, &mut serialized, 1 << 20).is_ok());
assert!(crate::safe_serialize(&a, &mut serialized, 1 << 20).is_ok());
let params = CompactFheUint32ListConformanceParams::from((
&server_key,
ListSizeConstraint::exact_size(3),
));
let deserialized_list = safe_deserialize_conformant::<CompactFheUint32List>(
let params = CompactCiphertextListConformanceParams {
shortint_params: block_params.to_shortint_conformance_param(),
num_elements_constraint: ListSizeConstraint::exact_size(clears.len()),
};
let deserialized_a = safe_deserialize_conformant::<CompactCiphertextList>(
serialized.as_slice(),
1 << 20,
&params,
)
.unwrap();
assert!(
deserialized_list.is_conformant(&CompactFheUint32ListConformanceParams::from((
block_params,
ListSizeConstraint::exact_size(3)
)))
);
let expanded_list = deserialized_list.expand();
for (fhe_uint, expected) in expanded_list.iter().zip(clears.into_iter()) {
let decrypted: u32 = fhe_uint.decrypt(&client_key);
assert_eq!(decrypted, expected);
let expander = deserialized_a.expand().unwrap();
for (i, clear) in clears.into_iter().enumerate() {
let encrypted: FheUint32 = expander.get(i).unwrap().unwrap();
let decrypted: u32 = encrypted.decrypt(&client_key);
assert_eq!(decrypted, clear);
}
}
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_fhe_uint_zk() {
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
let params = PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40;
let config = ConfigBuilder::with_custom_parameters(params, None).build();
let crs = CompactPkeCrs::from_config(config, 32).unwrap();
let ck = ClientKey::generate(config);
let pk = CompactPublicKey::new(&ck);
let msg = random::<u32>();
let proven_compact_fhe_uint = crate::ProvenCompactFheUint32::try_encrypt(
msg,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_uint = proven_compact_fhe_uint
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted: u32 = fhe_uint.decrypt(&ck);
assert_eq!(decrypted, msg);
let messages = (0..4).map(|_| random()).collect::<Vec<u32>>();
let proven_compact_fhe_uint_list = crate::ProvenCompactFheUint32List::try_encrypt(
&messages,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_uints = proven_compact_fhe_uint_list
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = fhe_uints
.iter()
.map(|fb| fb.decrypt(&ck))
.collect::<Vec<u32>>();
assert_eq!(decrypted.as_slice(), &messages);
assert!(deserialized_a.is_conformant(&params));
}

View File

@@ -1,140 +0,0 @@
use super::FheUintId;
use crate::core_crypto::commons::math::random::{Deserialize, Serialize};
use crate::core_crypto::prelude::UnsignedNumeric;
use crate::integer::block_decomposition::DecomposableInto;
use crate::integer::{ProvenCompactCiphertextList, RadixCiphertext};
use crate::named::Named;
use crate::zk::{CompactPkePublicParams, ZkComputeLoad, ZkVerificationOutCome};
use crate::{CompactPublicKey, FheUint};
/// A `CompactFheUint` tied to a Zero-Knowledge proof
///
/// The zero-knowledge proof allows to verify that the ciphertext is correctly
/// encrypted.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactFheUint<Id: FheUintId> {
inner: ProvenCompactCiphertextList,
_id: Id,
}
impl<Id: FheUintId> Named for ProvenCompactFheUint<Id> {
const NAME: &'static str = "high_level_api::ProvenCompactFheUint";
}
impl<Id> ProvenCompactFheUint<Id>
where
Id: FheUintId,
{
/// Encrypts the message while also generating the zero-knowledge proof
pub fn try_encrypt<Clear>(
value: Clear,
public_params: &CompactPkePublicParams,
key: &CompactPublicKey,
load: ZkComputeLoad,
) -> crate::Result<Self>
where
Clear: DecomposableInto<u64> + UnsignedNumeric,
{
let inner = key.key.key.encrypt_and_prove_radix_compact(
&[value],
Id::num_blocks(key.key.key.key.parameters.message_modulus()),
public_params,
load,
)?;
Ok(Self {
inner,
_id: Id::default(),
})
}
/// Verifies the ciphertext and the proof
///
/// If the proof and ciphertext are valid, it returns an `Ok` with
/// the underlying `FheUint`
pub fn verify_and_expand(
self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<FheUint<Id>> {
let expanded_inner = self
.inner
.verify_and_expand_one::<RadixCiphertext>(public_params, &public_key.key.key)?;
Ok(FheUint::new(expanded_inner))
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
self.inner.verify(public_params, &public_key.key.key)
}
}
/// A `CompactFheUintList` tied to a Zero-Knowledge proof
///
/// The zero-knowledge proof allows to verify that the ciphertext list is correctly
/// encrypted.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactFheUintList<Id: FheUintId> {
inner: ProvenCompactCiphertextList,
_id: Id,
}
impl<Id: FheUintId> Named for ProvenCompactFheUintList<Id> {
const NAME: &'static str = "high_level_api::ProvenCompactFheUintList";
}
impl<Id> ProvenCompactFheUintList<Id>
where
Id: FheUintId,
{
/// Encrypts the message while also generating the zero-knowledge proof
pub fn try_encrypt<Clear>(
values: &[Clear],
public_params: &CompactPkePublicParams,
key: &CompactPublicKey,
load: ZkComputeLoad,
) -> crate::Result<Self>
where
Clear: DecomposableInto<u64> + UnsignedNumeric,
{
let inner = key.key.key.encrypt_and_prove_radix_compact(
values,
Id::num_blocks(key.key.key.key.parameters.message_modulus()),
public_params,
load,
)?;
Ok(Self {
inner,
_id: Id::default(),
})
}
pub fn len(&self) -> usize {
self.inner.ciphertext_count()
}
/// Verifies the ciphertext and the proof
///
/// If the proof and ciphertext are valid, it returns an `Ok` with
/// the underlying `FheUint`s.
pub fn verify_and_expand(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<Vec<FheUint<Id>>> {
let expanded_inners = self
.inner
.verify_and_expand::<RadixCiphertext>(public_params, &public_key.key.key)?;
Ok(expanded_inners.into_iter().map(FheUint::new).collect())
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
self.inner.verify(public_params, &public_key.key.key)
}
}

View File

@@ -1,6 +1,5 @@
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
use crate::integer::ciphertext::CompactCiphertextList;
use crate::integer::public_key::CompactPublicKey;
use crate::integer::CompressedCompactPublicKey;
use crate::shortint::{EncryptionKeyChoice, MessageModulus};
@@ -216,17 +215,6 @@ impl IntegerCompactPublicKey {
Some(Self { key })
}
pub(in crate::high_level_api) fn try_encrypt_compact<T>(
&self,
values: &[T],
num_blocks: usize,
) -> CompactCiphertextList
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,
{
self.key.encrypt_slice_radix_compact(values, num_blocks)
}
pub fn into_raw_parts(self) -> CompactPublicKey {
self.key
}

View File

@@ -13,21 +13,10 @@ macro_rules! expand_pub_use_fhe_type(
$(
$fhe_type_name,
[<Compressed $fhe_type_name>],
[<Compact $fhe_type_name>],
[<Compact $fhe_type_name List>],
[<$fhe_type_name Id>],
// ConformanceParams
[<$fhe_type_name ConformanceParams>],
[<Compact $fhe_type_name ListConformanceParams>],
)*
};
#[cfg(feature = "zk-pok-experimental")]
pub use $module_path::{
$(
[<ProvenCompact $fhe_type_name>],
[<ProvenCompact $fhe_type_name List>],
)*
};
}
@@ -39,10 +28,7 @@ pub use crate::integer::oprf::SignedRandomizationSpec;
pub use config::{Config, ConfigBuilder};
pub use global_state::{set_server_key, unset_server_key, with_server_key_as_context};
pub use integers::{
CompactFheInt, CompactFheIntList, CompactFheUint, CompactFheUintList, CompressedFheInt,
CompressedFheUint, FheInt, FheUint, IntegerId,
};
pub use integers::{CompressedFheInt, CompressedFheUint, FheInt, FheUint, IntegerId};
#[cfg(feature = "gpu")]
pub use keys::CudaServerKey;
pub use keys::{
@@ -53,12 +39,7 @@ pub use keys::{
#[cfg(test)]
mod tests;
pub use crate::high_level_api::booleans::{
CompactFheBool, CompactFheBoolList, CompactFheBoolListConformanceParams, CompressedFheBool,
FheBool, FheBoolConformanceParams,
};
#[cfg(feature = "zk-pok-experimental")]
pub use crate::high_level_api::booleans::{ProvenCompactFheBool, ProvenCompactFheBoolList};
pub use crate::high_level_api::booleans::{CompressedFheBool, FheBool, FheBoolConformanceParams};
expand_pub_use_fhe_type!(
pub use crate::high_level_api::integers{
FheUint2, FheUint4, FheUint6, FheUint8, FheUint10, FheUint12, FheUint14, FheUint16,
@@ -69,6 +50,12 @@ expand_pub_use_fhe_type!(
};
);
pub use crate::integer::parameters::CompactCiphertextListConformanceParams;
#[cfg(feature = "zk-pok-experimental")]
pub use compact_list::ProvenCompactCiphertextList;
pub use compact_list::{
CompactCiphertextList, CompactCiphertextListBuilder, CompactCiphertextListExpander,
};
pub use safe_serialize::safe_serialize;
mod config;
@@ -81,6 +68,7 @@ mod errors;
mod integers;
pub mod array;
mod compact_list;
pub(in crate::high_level_api) mod details;
/// The tfhe prelude.
pub mod prelude;
@@ -95,6 +83,37 @@ pub enum Device {
CudaGpu,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum FheTypes {
Bool,
Uint2,
Uint4,
Uint6,
Uint8,
Uint10,
Uint12,
Uint14,
Uint16,
Uint32,
Uint64,
Uint128,
Uint160,
Uint256,
Int2,
Int4,
Int6,
Int8,
Int10,
Int12,
Int14,
Int16,
Int32,
Int64,
Int128,
Int160,
Int256,
}
pub mod safe_serialize {
use crate::named::Named;
use serde::Serialize;

View File

@@ -1,42 +1,383 @@
use super::super::parameters::RadixCompactCiphertextListConformanceParams;
use super::IntegerRadixCiphertext;
use crate::conformance::ParameterSetConformant;
use crate::conformance::{ListSizeConstraint, ParameterSetConformant};
use crate::core_crypto::prelude::Numeric;
use crate::integer::block_decomposition::DecomposableInto;
use crate::integer::encryption::{create_clear_radix_block_iterator, KnowsMessageModulus};
use crate::integer::parameters::CompactCiphertextListConformanceParams;
use crate::integer::{BooleanBlock, CompactPublicKey, ServerKey};
use crate::shortint::parameters::CiphertextConformanceParams;
use crate::shortint::{Ciphertext, MessageModulus};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
#[cfg(feature = "zk-pok-experimental")]
use crate::zk::{CompactPkePublicParams, ZkComputeLoad};
fn extract_message_and_carries(packed_blocks: Vec<Ciphertext>, sks: &ServerKey) -> Vec<Ciphertext> {
packed_blocks
.into_par_iter()
.flat_map(|block| {
let mut low_block = block;
let mut high_block = low_block.clone();
rayon::join(
|| {
sks.key.message_extract_assign(&mut low_block);
},
|| {
sks.key.carry_extract_assign(&mut high_block);
},
);
[low_block, high_block]
})
.collect::<Vec<_>>()
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DataKind {
Unsigned(usize),
Signed(usize),
Boolean,
}
impl DataKind {
pub fn num_blocks(self) -> usize {
match self {
Self::Unsigned(n) | Self::Signed(n) => n,
Self::Boolean => 1,
}
}
}
pub trait Compactable {
fn compact_into(
self,
messages: &mut Vec<u64>,
message_modulus: MessageModulus,
num_blocks: Option<usize>,
) -> DataKind;
}
impl Compactable for bool {
fn compact_into(
self,
messages: &mut Vec<u64>,
_message_modulus: MessageModulus,
_num_blocks: Option<usize>,
) -> DataKind {
messages.push(self as u64);
DataKind::Boolean
}
}
impl<T> Compactable for T
where
T: Numeric + DecomposableInto<u64> + std::ops::Shl<usize, Output = T>,
{
fn compact_into(
self,
messages: &mut Vec<u64>,
message_modulus: MessageModulus,
num_blocks: Option<usize>,
) -> DataKind {
let num_blocks =
num_blocks.unwrap_or_else(|| T::BITS.div_ceil(message_modulus.0.ilog2() as usize));
let decomposer = create_clear_radix_block_iterator(self, message_modulus, num_blocks);
messages.extend(decomposer);
// This works because rust always uses two's complement
let is_signed = (T::ONE << (T::BITS - 1)) < T::ZERO;
if is_signed {
DataKind::Signed(num_blocks)
} else {
DataKind::Unsigned(num_blocks)
}
}
}
pub struct CompactCiphertextListBuilder {
messages: Vec<u64>,
info: Vec<DataKind>,
pub(crate) pk: CompactPublicKey,
}
impl CompactCiphertextListBuilder {
pub fn new(pk: &CompactPublicKey) -> Self {
Self {
messages: vec![],
info: vec![],
pk: pk.clone(),
}
}
pub fn push<T>(&mut self, data: T) -> &mut Self
where
T: Compactable,
{
let n = self.messages.len();
let kind = data.compact_into(&mut self.messages, self.pk.key.message_modulus(), None);
assert_eq!(n + kind.num_blocks(), self.messages.len());
if kind.num_blocks() != 0 {
self.info.push(kind);
}
self
}
pub fn push_with_num_blocks<T>(&mut self, data: T, num_blocks: usize) -> &mut Self
where
// The extra `Numeric` bound is to prevent T from being `bool`
T: Compactable + Numeric,
{
if num_blocks == 0 {
return self;
}
let n = self.messages.len();
let kind = data.compact_into(
&mut self.messages,
self.pk.key.message_modulus(),
Some(num_blocks),
);
assert_eq!(n + kind.num_blocks(), self.messages.len());
self.info.push(kind);
self
}
pub fn extend<T>(&mut self, values: impl Iterator<Item = T>) -> &mut Self
where
T: Compactable,
{
for value in values {
self.push(value);
}
self
}
pub fn extend_with_num_blocks<T>(
&mut self,
values: impl Iterator<Item = T>,
num_blocks: usize,
) -> &mut Self
where
T: Compactable + Numeric,
{
for value in values {
self.push_with_num_blocks(value, num_blocks);
}
self
}
pub fn build(&self) -> CompactCiphertextList {
let ct_list = self.pk.key.encrypt_slice(self.messages.as_slice());
CompactCiphertextList {
ct_list,
info: self.info.clone(),
}
}
pub fn build_packed(&self) -> crate::Result<CompactCiphertextList> {
if self.pk.key.parameters.carry_modulus().0 < self.pk.key.parameters.message_modulus().0 {
return Err(crate::Error::new("In order to build a packed compact ciphertext list, parameters must have CarryModulus >= MessageModulus".to_string()));
}
// Here self.messages are decomposed blocks in range [0..message_modulus[
let msg_mod = self.pk.key.message_modulus().0 as u64;
let packed_messaged_iter = self
.messages
.chunks(2)
.map(|two_values| (two_values.get(1).copied().unwrap_or(0) * msg_mod) + two_values[0]);
let ct_list = self
.pk
.key
.encrypt_iter_with_modulus(packed_messaged_iter, msg_mod * msg_mod);
Ok(CompactCiphertextList {
ct_list,
info: self.info.clone(),
})
}
#[cfg(feature = "zk-pok-experimental")]
pub fn build_with_proof(
&self,
public_params: &CompactPkePublicParams,
load: ZkComputeLoad,
) -> crate::Result<ProvenCompactCiphertextList> {
let ct_list = self.pk.key.encrypt_and_prove_slice(
self.messages.as_slice(),
public_params,
load,
self.pk.key.parameters.message_modulus().0 as u64,
)?;
Ok(ProvenCompactCiphertextList {
ct_list,
info: self.info.clone(),
})
}
#[cfg(feature = "zk-pok-experimental")]
pub fn build_with_proof_packed(
&self,
public_params: &CompactPkePublicParams,
load: ZkComputeLoad,
) -> crate::Result<ProvenCompactCiphertextList> {
let msg_mod = self.pk.key.message_modulus().0 as u64;
let packed_messages = self
.messages
.chunks(2)
.map(|two_values| (two_values[1] * msg_mod) + two_values[0])
.collect::<Vec<_>>();
let ct_list = self.pk.key.encrypt_and_prove_slice(
packed_messages.as_slice(),
public_params,
load,
msg_mod * msg_mod,
)?;
Ok(ProvenCompactCiphertextList {
ct_list,
info: self.info.clone(),
})
}
}
pub trait Expandable: Sized {
fn from_expanded_blocks(blocks: &[Ciphertext], kind: DataKind) -> crate::Result<Self>;
}
impl<T> Expandable for T
where
T: IntegerRadixCiphertext,
{
fn from_expanded_blocks(blocks: &[Ciphertext], kind: DataKind) -> crate::Result<Self> {
match (kind, T::IS_SIGNED) {
(DataKind::Unsigned(_), false) | (DataKind::Signed(_), true) => {
Ok(T::from_blocks(blocks.to_vec()))
}
(DataKind::Boolean, _) => {
let signed_or_unsigned_str = if T::IS_SIGNED { "signed" } else { "unsigned" };
Err(crate::Error::new(format!(
"Tried to expand a {signed_or_unsigned_str} radix while boolean is stored"
)))
}
(DataKind::Unsigned(_), true) => Err(crate::Error::new(
"Tried to expand a signed radix while an unsigned radix is stored".to_string(),
)),
(DataKind::Signed(_), false) => Err(crate::Error::new(
"Tried to expand an unsigned radix while a signed radix is stored".to_string(),
)),
}
}
}
impl Expandable for BooleanBlock {
fn from_expanded_blocks(blocks: &[Ciphertext], kind: DataKind) -> crate::Result<Self> {
match kind {
DataKind::Unsigned(_) => Err(crate::Error::new(
"Tried to expand a boolean block while an unsigned radix was stored".to_string(),
)),
DataKind::Signed(_) => Err(crate::Error::new(
"Tried to expand a boolean block while a signed radix was stored".to_string(),
)),
DataKind::Boolean => Ok(Self::new_unchecked(blocks[0].clone())),
}
}
}
pub struct CompactCiphertextListExpander {
expanded_blocks: Vec<Ciphertext>,
info: Vec<DataKind>,
}
impl CompactCiphertextListExpander {
fn new(expanded_blocks: Vec<Ciphertext>, info: Vec<DataKind>) -> Self {
Self {
expanded_blocks,
info,
}
}
pub fn len(&self) -> usize {
self.info.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn blocks_of(&self, index: usize) -> Option<(&[Ciphertext], DataKind)> {
let preceding_infos = self.info.get(..index)?;
let current_info = self.info.get(index).copied()?;
let start_block_index = preceding_infos
.iter()
.copied()
.map(DataKind::num_blocks)
.sum();
let end_block_index = start_block_index + current_info.num_blocks();
self.expanded_blocks
.get(start_block_index..end_block_index)
.map(|block| (block, current_info))
}
pub fn get_kind_of(&self, index: usize) -> Option<DataKind> {
self.info.get(index).copied()
}
pub fn get<T>(&self, index: usize) -> Option<crate::Result<T>>
where
T: Expandable,
{
self.blocks_of(index)
.map(|(blocks, kind)| T::from_expanded_blocks(blocks, kind))
}
pub(crate) fn message_modulus(&self) -> MessageModulus {
self.expanded_blocks[0].message_modulus
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct CompactCiphertextList {
pub(crate) ct_list: crate::shortint::ciphertext::CompactCiphertextList,
// Keep track of the num_blocks, as we allow
// storing many integer that have the same num_blocks
// into ct_list
pub(crate) num_blocks_per_integer: usize,
// Integers stored can have a heterogeneous number of blocks and signedness
// We store this info to safeguard the expansion
pub(crate) info: Vec<DataKind>,
}
impl ParameterSetConformant for CompactCiphertextList {
type ParameterSet = RadixCompactCiphertextListConformanceParams;
type ParameterSet = CompactCiphertextListConformanceParams;
fn is_conformant(&self, params: &RadixCompactCiphertextListConformanceParams) -> bool {
self.num_blocks_per_integer == params.num_blocks_per_integer
&& self
.ct_list
.is_conformant(&params.to_shortint_ct_list_conformance_parameters())
fn is_conformant(&self, params: &CompactCiphertextListConformanceParams) -> bool {
if !params.num_elements_constraint.is_valid(self.info.len()) {
return false;
}
self.is_conformant_with_shortint_params(params.shortint_params)
}
}
impl CompactCiphertextList {
pub fn expand_one<T: IntegerRadixCiphertext>(&self) -> T {
let mut blocks = self.ct_list.expand();
blocks.truncate(self.num_blocks_per_integer);
T::from(blocks)
pub fn is_packed(&self) -> bool {
self.ct_list.degree.get() > self.ct_list.message_modulus.0
}
pub fn builder(pk: &CompactPublicKey) -> CompactCiphertextListBuilder {
CompactCiphertextListBuilder::new(pk)
}
/// Deconstruct a [`CompactCiphertextList`] into its constituents.
pub fn into_raw_parts(self) -> (crate::shortint::ciphertext::CompactCiphertextList, usize) {
let Self {
ct_list,
num_blocks_per_integer,
} = self;
(ct_list, num_blocks_per_integer)
pub fn into_raw_parts(
self,
) -> (
crate::shortint::ciphertext::CompactCiphertextList,
Vec<DataKind>,
) {
let Self { ct_list, info } = self;
(ct_list, info)
}
/// Construct a [`CompactCiphertextList`] from its constituents.
@@ -46,44 +387,52 @@ impl CompactCiphertextList {
/// Panics if the constituents are not compatible with each others.
pub fn from_raw_parts(
ct_list: crate::shortint::ciphertext::CompactCiphertextList,
num_blocks_per_integer: usize,
info: Vec<DataKind>,
) -> Self {
assert_eq!(
ct_list.ct_list.lwe_ciphertext_count().0 % num_blocks_per_integer,
0,
ct_list.ct_list.lwe_ciphertext_count().0,
info.len(),
"CompactCiphertextList LweCiphertextCount is expected \
to be a multiple of {num_blocks_per_integer}, got {:?}",
to be equal to the info vec {} vs {:?}",
info.len(),
ct_list.ct_list.lwe_ciphertext_count()
);
Self {
ct_list,
num_blocks_per_integer,
}
Self { ct_list, info }
}
pub fn ciphertext_count(&self) -> usize {
self.ct_list.ct_list.lwe_ciphertext_count().0 / self.num_blocks_per_integer
self.info.len()
}
pub fn expand<T: IntegerRadixCiphertext>(&self) -> Vec<T> {
let mut all_block_iter = self.ct_list.expand().into_iter();
let num_ct = self.ciphertext_count();
let mut ciphertexts = Vec::with_capacity(num_ct);
pub fn expand(&self, sks: &ServerKey) -> crate::Result<CompactCiphertextListExpander> {
let expanded_blocks = self.ct_list.expand();
for _ in 0..num_ct {
let ct_blocks = all_block_iter
.by_ref()
.take(self.num_blocks_per_integer)
.collect::<Vec<_>>();
if ct_blocks.len() < self.num_blocks_per_integer {
break;
}
let ct = T::from(ct_blocks);
ciphertexts.push(ct);
let mut conformance_params = sks.key.conformance_params();
conformance_params.degree = self.ct_list.degree;
if !self.is_conformant_with_shortint_params(conformance_params) {
return Err(crate::Error::new(
"This compact list is not conformant with the given server key".to_string(),
));
}
ciphertexts
let expanded_blocks = if self.ct_list.degree.get() > self.ct_list.message_modulus.0 - 1 {
extract_message_and_carries(expanded_blocks, sks)
} else {
expanded_blocks
};
Ok(CompactCiphertextListExpander::new(
expanded_blocks,
self.info.clone(),
))
}
/// This simply expands the list without splitting messages if they were packed
/// caller should check using [Self::is_packed] to know if data is packed
pub(crate) fn expand_without_unpacking(&self) -> CompactCiphertextListExpander {
let expanded_blocks = self.ct_list.expand();
CompactCiphertextListExpander::new(expanded_blocks, self.info.clone())
}
pub fn size_elements(&self) -> usize {
@@ -93,4 +442,139 @@ impl CompactCiphertextList {
pub fn size_bytes(&self) -> usize {
self.ct_list.size_bytes()
}
fn is_conformant_with_shortint_params(
&self,
shortint_params: CiphertextConformanceParams,
) -> bool {
let mut num_blocks: usize = self.info.iter().copied().map(DataKind::num_blocks).sum();
if shortint_params.degree.get()
== (shortint_params.message_modulus.0 * shortint_params.carry_modulus.0) - 1
{
num_blocks = num_blocks.div_ceil(2);
}
let shortint_list_params = shortint_params
.to_ct_list_conformance_parameters(ListSizeConstraint::exact_size(num_blocks));
self.ct_list.is_conformant(&shortint_list_params)
}
}
#[cfg(feature = "zk-pok-experimental")]
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactCiphertextList {
pub(crate) ct_list: crate::shortint::ciphertext::ProvenCompactCiphertextList,
// Integers stored can have a heterogeneous number of blocks and signedness
// We store this info to safeguard the expansion
pub(crate) info: Vec<DataKind>,
}
#[cfg(feature = "zk-pok-experimental")]
impl ProvenCompactCiphertextList {
pub fn builder(pk: &CompactPublicKey) -> CompactCiphertextListBuilder {
CompactCiphertextListBuilder::new(pk)
}
pub fn verify_and_expand(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
sks: &ServerKey,
) -> crate::Result<CompactCiphertextListExpander> {
let expanded_ct = self
.ct_list
.verify_and_expand(public_params, &public_key.key)?;
let degree = self.ct_list.proved_lists[0].0.degree;
let message_modulus = self.ct_list.proved_lists[0].0.message_modulus.0;
let mut conformance_params = sks.key.conformance_params();
conformance_params.degree = degree;
for ct in expanded_ct.iter() {
if !ct.is_conformant(&conformance_params) {
return Err(crate::Error::new(
"This compact list is not conformant with the given server key".to_string(),
));
}
}
let expanded_ct = if degree.get() > message_modulus - 1 {
extract_message_and_carries(expanded_ct, sks)
} else {
expanded_ct
};
Ok(CompactCiphertextListExpander::new(
expanded_ct,
self.info.clone(),
))
}
pub fn is_packed(&self) -> bool {
self.ct_list.proved_lists[0].0.degree.get()
> self.ct_list.proved_lists[0].0.message_modulus.0
}
/// This simply expands the list without splitting messages if they were packed
/// caller should check using [Self::is_packed] to know if data is packed
pub(crate) fn verify_and_expand_without_unpacking(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<CompactCiphertextListExpander> {
let expanded_ct = self
.ct_list
.verify_and_expand(public_params, &public_key.key)?;
Ok(CompactCiphertextListExpander::new(
expanded_ct,
self.info.clone(),
))
}
}
#[cfg(feature = "zk-pok_experimental")]
#[cfg(test)]
mod tests {
use crate::integer::ciphertext::CompactCiphertextList;
use crate::integer::{ClientKey, CompactPublicKey, RadixCiphertext, ServerKey};
use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40;
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
use rand::random;
#[test]
fn test_zk_compact_ciphertext_list_encryption_ci_run_filter() {
let params = PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40;
let num_blocks = 4usize;
let modulus = (params.message_modulus.0 as u64)
.checked_pow(num_blocks as u32)
.unwrap();
let crs = CompactPkeCrs::from_shortint_params(params, 512).unwrap();
let cks = ClientKey::new(params);
let sk = ServerKey::new_radix_server_key(&cks);
let pk = CompactPublicKey::new(&cks);
let msgs = (0..512)
.map(|_| random::<u64>() % modulus)
.collect::<Vec<_>>();
let proven_ct = CompactCiphertextList::builder(&pk)
.push_sized_numbers(msgs.iter().copied(), num_blocks)
.build_with_proof_packed(crs.public_params(), ZkComputeLoad::Proof)
.unwrap();
let mut expander = proven_ct
.verify_and_expand(crs.public_params(), &pk, &sk)
.unwrap();
for msg in msgs {
let expanded = expander
.expand_next_as::<RadixCiphertext>()
.unwrap()
.unwrap();
let decrypted = cks.decrypt_radix::<u64>(&expanded);
assert_eq!(msg, decrypted);
}
}
}

View File

@@ -66,11 +66,9 @@ pub mod wopbs;
#[cfg(feature = "gpu")]
pub mod gpu;
#[cfg(feature = "zk-pok-experimental")]
mod zk;
#[cfg(feature = "zk-pok-experimental")]
pub use zk::ProvenCompactCiphertextList;
pub use ciphertext::ProvenCompactCiphertextList;
pub use bigint::i256::I256;
pub use bigint::i512::I512;

View File

@@ -1,8 +1,7 @@
#![allow(clippy::excessive_precision)]
use crate::conformance::ListSizeConstraint;
use crate::shortint::parameters::{
CarryModulus, CiphertextConformanceParams, CiphertextListConformanceParams,
EncryptionKeyChoice, MessageModulus,
CarryModulus, CiphertextConformanceParams, EncryptionKeyChoice, MessageModulus,
};
pub use crate::shortint::parameters::{
DecompositionBaseLog, DecompositionLevelCount, DynamicDistribution, GlweDimension,
@@ -163,11 +162,10 @@ impl RadixCiphertextConformanceParams {
pub fn to_ct_list_conformance_parameters(
&self,
list_constraint: ListSizeConstraint,
) -> RadixCompactCiphertextListConformanceParams {
RadixCompactCiphertextListConformanceParams {
) -> CompactCiphertextListConformanceParams {
CompactCiphertextListConformanceParams {
shortint_params: self.shortint_params,
num_blocks_per_integer: self.num_blocks_per_integer,
num_integers_constraint: list_constraint,
num_elements_constraint: list_constraint,
}
}
@@ -186,17 +184,7 @@ impl RadixCiphertextConformanceParams {
/// Structure to store the expected properties of a ciphertext list
/// Can be used on a server to check if client inputs are well formed
/// before running a computation on them
pub struct RadixCompactCiphertextListConformanceParams {
pub struct CompactCiphertextListConformanceParams {
pub shortint_params: CiphertextConformanceParams,
pub num_blocks_per_integer: usize,
pub num_integers_constraint: ListSizeConstraint,
}
impl RadixCompactCiphertextListConformanceParams {
pub fn to_shortint_ct_list_conformance_parameters(&self) -> CiphertextListConformanceParams {
self.shortint_params.to_ct_list_conformance_parameters(
self.num_integers_constraint
.multiply_group_size(self.num_blocks_per_integer),
)
}
pub num_elements_constraint: ListSizeConstraint,
}

View File

@@ -1,7 +1,7 @@
use crate::core_crypto::prelude::{SignedNumeric, UnsignedNumeric};
use crate::integer::block_decomposition::DecomposableInto;
use crate::integer::ciphertext::{CompactCiphertextList, RadixCiphertext};
use crate::integer::encryption::{create_clear_radix_block_iterator, encrypt_words_radix_impl};
use crate::integer::encryption::encrypt_words_radix_impl;
use crate::integer::{ClientKey, SignedRadixCiphertext};
use crate::shortint::{
CompactPublicKey as ShortintCompactPublicKey,
@@ -59,25 +59,19 @@ impl CompactPublicKey {
)
}
pub fn encrypt_radix_compact<T: DecomposableInto<u64>>(
pub fn encrypt_radix_compact<T: DecomposableInto<u64> + std::ops::Shl<usize, Output = T>>(
&self,
message: T,
num_blocks_per_integer: usize,
) -> CompactCiphertextList {
let clear_block_iter = create_clear_radix_block_iterator(
message,
self.key.parameters.message_modulus(),
num_blocks_per_integer,
);
let ct_list = self.key.encrypt_iter(clear_block_iter);
CompactCiphertextList {
ct_list,
num_blocks_per_integer,
}
CompactCiphertextList::builder(self)
.push_with_num_blocks(message, num_blocks_per_integer)
.build()
}
pub fn encrypt_slice_radix_compact<T: DecomposableInto<u64>>(
pub fn encrypt_slice_radix_compact<
T: DecomposableInto<u64> + std::ops::Shl<usize, Output = T>,
>(
&self,
messages: &[T],
num_blocks: usize,
@@ -85,51 +79,16 @@ impl CompactPublicKey {
self.encrypt_iter_radix_compact(messages.iter().copied(), num_blocks)
}
pub fn encrypt_iter_radix_compact<T: DecomposableInto<u64>>(
pub fn encrypt_iter_radix_compact<
T: DecomposableInto<u64> + std::ops::Shl<usize, Output = T>,
>(
&self,
mut message_iter: impl Iterator<Item = T>,
message_iter: impl Iterator<Item = T>,
num_blocks_per_integer: usize,
) -> CompactCiphertextList {
let mut iterator_chain;
match (message_iter.next(), message_iter.next()) {
(None, None) => panic!("At least one message is required"),
(None, Some(_)) => unreachable!(),
(Some(first_message), None) => {
// Cannot form a chain
return self.encrypt_radix_compact(first_message, num_blocks_per_integer);
}
(Some(first_message), Some(second_message)) => {
let first_iter = create_clear_radix_block_iterator(
first_message,
self.key.parameters.message_modulus(),
num_blocks_per_integer,
);
let second_iter = create_clear_radix_block_iterator(
second_message,
self.key.parameters.message_modulus(),
num_blocks_per_integer,
);
iterator_chain =
Box::new(first_iter.chain(second_iter)) as Box<dyn Iterator<Item = u64>>;
}
}
for message in message_iter {
let other_iter = create_clear_radix_block_iterator(
message,
self.key.parameters.message_modulus(),
num_blocks_per_integer,
);
iterator_chain = Box::new(iterator_chain.chain(other_iter));
}
let ct_list = self.key.encrypt_iter(iterator_chain);
CompactCiphertextList {
ct_list,
num_blocks_per_integer,
}
let mut builder = CompactCiphertextList::builder(self);
builder.extend_with_num_blocks(message_iter, num_blocks_per_integer);
builder.build()
}
pub fn size_elements(&self) -> usize {

View File

@@ -1,6 +1,6 @@
use crate::integer::keycache::KEY_CACHE;
use crate::integer::tests::create_parametrized_test;
use crate::integer::{gen_keys, CompressedPublicKey, IntegerKeyKind, PublicKey};
use crate::integer::{gen_keys, CompressedPublicKey, IntegerKeyKind, PublicKey, RadixCiphertext};
use crate::shortint::parameters::classic::compact_pk::*;
#[cfg(tarpaulin)]
use crate::shortint::parameters::coverage_parameters::*;
@@ -100,7 +100,7 @@ fn small_radix_encrypt_decrypt_compact_128_bits_list(params: ClassicPBSParameter
}
fn radix_encrypt_decrypt_compact_128_bits_list(params: ClassicPBSParameters) {
let (cks, _) = gen_keys(params, IntegerKeyKind::Radix);
let (cks, sks) = gen_keys(params, IntegerKeyKind::Radix);
let pk = crate::integer::public_key::CompactPublicKey::new(&cks);
let mut rng = rand::thread_rng();
@@ -117,13 +117,28 @@ fn radix_encrypt_decrypt_compact_128_bits_list(params: ClassicPBSParameters) {
clear_vec.push(clear);
}
let compact_encrypted_list = pk.encrypt_slice_radix_compact(&clear_vec, num_block);
let mut builder = crate::integer::ciphertext::CompactCiphertextList::builder(&pk);
builder.extend_with_num_blocks(clear_vec.iter().copied(), num_block);
let ciphertext_vec = compact_encrypted_list.expand();
let compact_lists = [builder.build(), builder.build_packed().unwrap()];
for (ciphertext, clear) in ciphertext_vec.iter().zip(clear_vec.iter().copied()) {
let decrypted: u128 = cks.decrypt_radix(ciphertext);
assert_eq!(decrypted, clear);
assert!(!compact_lists[0].is_packed());
assert!(compact_lists[1].is_packed());
for compact_encrypted_list in compact_lists {
let expander = compact_encrypted_list.expand(&sks).unwrap();
let mut ciphertext_vec = Vec::with_capacity(num_ct_for_this_iter);
for i in 0..num_ct_for_this_iter {
let radix = expander.get::<RadixCiphertext>(i).unwrap().unwrap();
assert_eq!(radix.blocks.len(), num_block);
ciphertext_vec.push(radix);
}
for (ciphertext, clear) in ciphertext_vec.iter().zip(clear_vec.iter().copied()) {
let decrypted: u128 = cks.decrypt_radix(ciphertext);
assert_eq!(decrypted, clear);
}
}
}
}

View File

@@ -1,137 +0,0 @@
use crate::integer::block_decomposition::{BlockDecomposer, DecomposableInto};
use crate::integer::encryption::KnowsMessageModulus;
use crate::integer::public_key::CompactPublicKey;
use crate::integer::IntegerRadixCiphertext;
use crate::zk::{CompactPkePublicParams, ZkComputeLoad, ZkVerificationOutCome};
use serde::{Deserialize, Serialize};
impl CompactPublicKey {
pub fn encrypt_and_prove_radix_compact<T: DecomposableInto<u64>>(
&self,
messages: &[T],
num_blocks_per_integer: usize,
public_params: &CompactPkePublicParams,
load: ZkComputeLoad,
) -> crate::Result<ProvenCompactCiphertextList> {
let messages = messages
.iter()
.copied()
.flat_map(|message| {
BlockDecomposer::new(message, self.key.message_modulus().0.ilog2())
.iter_as::<u64>()
.take(num_blocks_per_integer)
})
.collect::<Vec<_>>();
let proved_list = self
.key
.encrypt_and_prove_slice(&messages, public_params, load)?;
Ok(ProvenCompactCiphertextList {
proved_list,
num_blocks_per_integer,
})
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactCiphertextList {
pub(crate) proved_list: crate::shortint::ciphertext::ProvenCompactCiphertextList,
// Keep track of the num_blocks, as we allow
// storing many integer that have the same num_blocks
// into ct_list
pub(crate) num_blocks_per_integer: usize,
}
impl ProvenCompactCiphertextList {
pub fn verify_and_expand_one<T: IntegerRadixCiphertext>(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<T> {
let blocks = self
.proved_list
.verify_and_expand(public_params, &public_key.key)?;
assert_eq!(blocks.len(), self.num_blocks_per_integer);
Ok(T::from_blocks(blocks))
}
pub fn ciphertext_count(&self) -> usize {
self.proved_list.ciphertext_count() / self.num_blocks_per_integer
}
pub fn verify_and_expand<T: IntegerRadixCiphertext>(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<Vec<T>> {
let blocks = self
.proved_list
.verify_and_expand(public_params, &public_key.key)?;
let mut integers = Vec::with_capacity(self.ciphertext_count());
let mut blocks_iter = blocks.into_iter();
for _ in 0..self.ciphertext_count() {
let radix_blocks = blocks_iter
.by_ref()
.take(self.num_blocks_per_integer)
.collect::<Vec<_>>();
integers.push(T::from_blocks(radix_blocks));
}
Ok(integers)
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
self.proved_list.verify(public_params, &public_key.key)
}
}
#[cfg(test)]
mod tests {
use crate::integer::{ClientKey, CompactPublicKey};
use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40;
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
use rand::random;
#[test]
fn test_zk_compact_ciphertext_list_encryption_ci_run_filter() {
let params = PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS_TUNIFORM_2M40;
let num_blocks = 4usize;
let modulus = (params.message_modulus.0 as u64)
.checked_pow(num_blocks as u32)
.unwrap();
let crs = CompactPkeCrs::from_shortint_params(params, 512).unwrap();
let cks = ClientKey::new(params);
let pk = CompactPublicKey::new(&cks);
let msgs = (0..512)
.map(|_| random::<u64>() % modulus)
.collect::<Vec<_>>();
let proven_ct = pk
.encrypt_and_prove_radix_compact(
&msgs,
num_blocks,
crs.public_params(),
ZkComputeLoad::Proof,
)
.unwrap();
assert!(proven_ct.verify(crs.public_params(), &pk).is_valid());
let expanded = proven_ct
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = expanded
.iter()
.map(|ciphertext| cks.decrypt_radix::<u64>(ciphertext))
.collect::<Vec<_>>();
assert_eq!(msgs, decrypted);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -159,76 +159,47 @@ mod test_integer {
use crate::prelude::*;
use crate::safe_deserialization::{safe_deserialize_conformant, safe_serialize};
use crate::shortint::parameters::{
PARAM_MESSAGE_1_CARRY_1_KS_PBS, PARAM_MESSAGE_2_CARRY_2_KS_PBS,
PARAM_MESSAGE_3_CARRY_3_KS_PBS,
PARAM_MESSAGE_2_CARRY_2_KS_PBS, PARAM_MESSAGE_3_CARRY_3_KS_PBS,
};
use crate::{
CompactFheUint8, CompactFheUint8List, CompactFheUint8ListConformanceParams,
CompactPublicKey, FheUint8ConformanceParams,
set_server_key, CompactCiphertextList, CompactCiphertextListConformanceParams,
CompactPublicKey, FheUint8,
};
#[test]
fn safe_deserialization_ct() {
let (client_key, server_key) = generate_keys(ConfigBuilder::default().build());
let public_key = CompactPublicKey::new(&client_key);
let msg = 27u8;
let ct = CompactFheUint8::try_encrypt(msg, &public_key).unwrap();
let mut buffer = vec![];
safe_serialize(&ct, &mut buffer, 1 << 40).unwrap();
let params = FheUint8ConformanceParams::from(PARAM_MESSAGE_1_CARRY_1_KS_PBS);
assert!(safe_deserialize_conformant::<CompactFheUint8>(
buffer.as_slice(),
1 << 20,
&params
)
.is_err());
let params = FheUint8ConformanceParams::from(&server_key);
let ct2 =
safe_deserialize_conformant::<CompactFheUint8>(buffer.as_slice(), 1 << 20, &params)
.unwrap();
let dec: u8 = ct2.expand().decrypt(&client_key);
assert_eq!(msg, dec);
}
#[test]
fn safe_deserialization_ct_list() {
let (client_key, server_key) = generate_keys(ConfigBuilder::default().build());
let (client_key, sks) = generate_keys(ConfigBuilder::default().build());
set_server_key(sks);
let public_key = CompactPublicKey::new(&client_key);
let msg = [27u8, 10, 3];
let ct_list = CompactFheUint8List::try_encrypt(&msg, &public_key).unwrap();
let ct_list = CompactCiphertextList::builder(&public_key)
.push(27u8)
.push(10u8)
.push(3u8)
.build();
let mut buffer = vec![];
safe_serialize(&ct_list, &mut buffer, 1 << 40).unwrap();
let to_param_set = |list_size_constraint| {
CompactFheUint8ListConformanceParams::from((
PARAM_MESSAGE_2_CARRY_2_KS_PBS,
list_size_constraint,
))
let to_param_set = |list_size_constraint| CompactCiphertextListConformanceParams {
shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS.to_shortint_conformance_param(),
num_elements_constraint: list_size_constraint,
};
for param_set in [
CompactFheUint8ListConformanceParams::from((
PARAM_MESSAGE_3_CARRY_3_KS_PBS,
ListSizeConstraint::exact_size(3),
)),
CompactCiphertextListConformanceParams {
shortint_params: PARAM_MESSAGE_3_CARRY_3_KS_PBS.to_shortint_conformance_param(),
num_elements_constraint: ListSizeConstraint::exact_size(3),
},
to_param_set(ListSizeConstraint::exact_size(2)),
to_param_set(ListSizeConstraint::exact_size(4)),
to_param_set(ListSizeConstraint::try_size_in_range(1, 2).unwrap()),
to_param_set(ListSizeConstraint::try_size_in_range(4, 5).unwrap()),
] {
assert!(safe_deserialize_conformant::<CompactFheUint8List>(
assert!(safe_deserialize_conformant::<CompactCiphertextList>(
buffer.as_slice(),
1 << 20,
&param_set
@@ -242,28 +213,36 @@ mod test_integer {
ListSizeConstraint::try_size_in_range(3, 4).unwrap(),
ListSizeConstraint::try_size_in_range(2, 4).unwrap(),
] {
let params = CompactFheUint8ListConformanceParams::from((&server_key, len_constraint));
assert!(safe_deserialize_conformant::<CompactFheUint8List>(
let params = CompactCiphertextListConformanceParams {
shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS.to_shortint_conformance_param(),
num_elements_constraint: len_constraint,
};
assert!(safe_deserialize_conformant::<CompactCiphertextList>(
buffer.as_slice(),
1 << 20,
&params,
)
.is_ok());
}
let params = CompactFheUint8ListConformanceParams::from((
&server_key,
ListSizeConstraint::exact_size(3),
));
let ct2 =
safe_deserialize_conformant::<CompactFheUint8List>(buffer.as_slice(), 1 << 20, &params)
.unwrap();
let params = CompactCiphertextListConformanceParams {
shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS.to_shortint_conformance_param(),
num_elements_constraint: ListSizeConstraint::exact_size(3),
};
let ct2 = safe_deserialize_conformant::<CompactCiphertextList>(
buffer.as_slice(),
1 << 20,
&params,
)
.unwrap();
let dec: Vec<u8> = ct2
.expand()
.iter()
.map(|a| a.decrypt(&client_key))
.collect();
let mut cts = Vec::with_capacity(3);
let expander = ct2.expand().unwrap();
for i in 0..3 {
cts.push(expander.get::<FheUint8>(i).unwrap().unwrap());
}
let dec: Vec<u8> = cts.iter().map(|a| a.decrypt(&client_key)).collect();
assert_eq!(&msg[..], &dec);
}

View File

@@ -175,7 +175,12 @@ mod tests {
.collect::<Vec<_>>();
let proven_ct = pk
.encrypt_and_prove_slice(&msgs, crs.public_params(), ZkComputeLoad::Proof)
.encrypt_and_prove_slice(
&msgs,
crs.public_params(),
ZkComputeLoad::Proof,
params.message_modulus.0 as u64,
)
.unwrap();
assert!(proven_ct.verify(crs.public_params(), &pk).is_valid());

View File

@@ -27,16 +27,24 @@ pub struct CompactPublicKey {
fn to_plaintext_iterator(
message_iter: impl Iterator<Item = u64>,
encryption_modulus: u64,
parameters: &ShortintParameterSet,
) -> impl Iterator<Item = Plaintext<u64>> {
let message_modulus = parameters.message_modulus().0 as u64;
let carry_modulus = parameters.carry_modulus().0 as u64;
let full_modulus = message_modulus * carry_modulus;
assert!(
encryption_modulus <= full_modulus,
"Encryption modulus cannot exceed the plaintext modulus"
);
message_iter.map(move |message| {
//The delta is the one defined by the parameters
let delta = (1_u64 << 63) / (message_modulus * carry_modulus);
let delta = (1_u64 << 63) / (full_modulus);
//The input is reduced modulus the message_modulus
let m = message % message_modulus;
let m = message % encryption_modulus;
let shifted_message = m * delta;
// encode the message
@@ -144,9 +152,13 @@ impl CompactPublicKey {
}
pub fn encrypt(&self, message: u64) -> Ciphertext {
let plain = to_plaintext_iterator(once(message), &self.parameters)
.next()
.unwrap();
let plain = to_plaintext_iterator(
once(message),
self.parameters.message_modulus().0 as u64,
&self.parameters,
)
.next()
.unwrap();
// This allocates the required ct
let mut encrypted_ct = LweCiphertextOwned::new(
@@ -229,14 +241,49 @@ impl CompactPublicKey {
Ok(ProvenCiphertext { ciphertext, proof })
}
/// Encrypts the messages contained in the slice into a compact ciphertext list
///
/// See [Self::encrypt_iter] for more details
pub fn encrypt_slice(&self, messages: &[u64]) -> CompactCiphertextList {
self.encrypt_iter(messages.iter().copied())
self.encrypt_slice_with_modulus(messages, self.parameters.message_modulus().0 as u64)
}
/// Encrypts the messages coming from the iterator into a compact ciphertext list
///
/// Values of the messages should be in range [0..message_modulus[
/// (a modulo operation is applied to each input)
pub fn encrypt_iter(&self, messages: impl Iterator<Item = u64>) -> CompactCiphertextList {
let plaintext_container = to_plaintext_iterator(messages, &self.parameters)
.map(|plaintext| plaintext.0)
.collect::<Vec<_>>();
self.encrypt_iter_with_modulus(messages, self.parameters.message_modulus().0 as u64)
}
/// Encrypts the messages contained in the slice into a compact ciphertext list
///
/// See [Self::encrypt_iter_with_modulus] for more details
pub fn encrypt_slice_with_modulus(
&self,
messages: &[u64],
encryption_modulus: u64,
) -> CompactCiphertextList {
self.encrypt_iter_with_modulus(messages.iter().copied(), encryption_modulus)
}
/// Encrypts the messages coming from the iterator into a compact ciphertext list
///
/// Values of the messages should be in range [0..encryption_modulus[
/// (a modulo operation is applied to each input)
///
/// # Panic
///
/// - This will panic is encryption modulus is greater that message_modulus * carry_modulus
pub fn encrypt_iter_with_modulus(
&self,
messages: impl Iterator<Item = u64>,
encryption_modulus: u64,
) -> CompactCiphertextList {
let plaintext_container =
to_plaintext_iterator(messages, encryption_modulus, &self.parameters)
.map(|plaintext| plaintext.0)
.collect::<Vec<_>>();
let plaintext_list = PlaintextList::from_container(plaintext_container);
let mut ct_list = LweCompactCiphertextListOwned::new(
@@ -285,7 +332,7 @@ impl CompactPublicKey {
let message_modulus = self.parameters.message_modulus();
CompactCiphertextList {
ct_list,
degree: Degree::new(message_modulus.0 - 1),
degree: Degree::new(encryption_modulus as usize - 1),
message_modulus,
carry_modulus: self.parameters.carry_modulus(),
pbs_order: self.pbs_order,
@@ -299,15 +346,24 @@ impl CompactPublicKey {
messages: &[u64],
public_params: &CompactPkePublicParams,
load: ZkComputeLoad,
encryption_modulus: u64,
) -> crate::Result<ProvenCompactCiphertextList> {
let plaintext_modulus =
(self.parameters.message_modulus().0 * self.parameters.carry_modulus().0) as u64;
let delta = (1u64 << 63) / plaintext_modulus;
assert!(encryption_modulus <= plaintext_modulus);
// This is the maximum number of lwe that can share the same mask in lwe compact pk
// encryption
let max_ciphertext_per_bin = self.key.lwe_dimension().0;
// This is the maximum of lwe message a single proof can prove
let max_num_message = public_params.k;
let num_lists = messages.len().div_ceil(max_num_message);
// One of the two is the limiting factor for how much we can pack messages
let message_chunk_size = max_num_message.min(max_ciphertext_per_bin);
let num_lists = messages.len().div_ceil(message_chunk_size);
let mut proved_lists = Vec::with_capacity(num_lists);
for message_chunk in messages.chunks(max_num_message) {
for message_chunk in messages.chunks(message_chunk_size) {
let mut ct_list = LweCompactCiphertextListOwned::new(
0u64,
self.key.lwe_dimension().to_lwe_size(),
@@ -362,7 +418,7 @@ impl CompactPublicKey {
let message_modulus = self.parameters.message_modulus();
let ciphertext = CompactCiphertextList {
ct_list,
degree: Degree::new(message_modulus.0 - 1),
degree: Degree::new(encryption_modulus as usize - 1),
message_modulus,
carry_modulus: self.parameters.carry_modulus(),
pbs_order: self.pbs_order,

View File

@@ -30,32 +30,6 @@
disabled
/>
<input
type="button"
id="compactPublicKeyTest32BitSmall"
value="Compact Public Key Test 32 Bits Small"
disabled
/>
<input
type="button"
id="compactPublicKeyTest32BitBig"
value="Compact Public Key Test 32 Bits Big"
disabled
/>
<input
type="button"
id="compactPublicKeyTest256BitSmall"
value="Compact Public Key Test 256 Bits Small"
disabled
/>
<input
type="button"
id="compactPublicKeyTest256BitBig"
value="Compact Public Key Test 256 Bits Big"
disabled
/>
<input
type="button"
id="compressedCompactPublicKeyTest256BitSmall"

View File

@@ -25,10 +25,6 @@ async function setup() {
const demoNames = [
"publicKeyTest",
"compressedPublicKeyTest",
"compactPublicKeyTest32BitBig",
"compactPublicKeyTest32BitSmall",
"compactPublicKeyTest256BitBig",
"compactPublicKeyTest256BitSmall",
"compressedCompactPublicKeyTest256BitBig",
"compressedCompactPublicKeyTest256BitSmall",
"compactPublicKeyZeroKnowledge",

View File

@@ -1,21 +1,5 @@
import { runTestAttachedToButton } from "./common.mjs";
it("Compact Public Key Test Big 32 Bit", async () => {
await runTestAttachedToButton("compactPublicKeyTest32BitBig");
});
it("Compact Public Key Test Small 32 Bit", async () => {
await runTestAttachedToButton("compactPublicKeyTest32BitSmall");
});
it("Compact Public Key Test Small 256 Bit", async () => {
await runTestAttachedToButton("compactPublicKeyTest256BitSmall");
});
it("Compact Public Key Test Big 256 Bit", async () => {
await runTestAttachedToButton("compactPublicKeyTest256BitBig");
});
it("Compressed Compact Public Key Test Small 256 Bit", async () => {
await runTestAttachedToButton("compressedCompactPublicKeyTest256BitSmall");
});

View File

@@ -12,16 +12,15 @@ import init, {
TfheCompactPublicKey,
TfheConfigBuilder,
FheUint8,
CompactFheUint32List,
CompactFheUint256List,
ZkComputeLoad,
ProvenCompactFheUint64,
ProvenCompactFheUint64List,
CompactPkeCrs,
CompactCiphertextList,
ProvenCompactCiphertextList,
Shortint,
CompactFheUint64,
} from "./pkg/tfhe.js";
const U32_MAX = 4294967295;
function assert(cond, text) {
if (cond) return;
if (console.assert.useDebugger) debugger;
@@ -93,75 +92,6 @@ async function publicKeyTest() {
assert_eq(decrypted, 255);
}
const U32_MAX = 4294967295;
async function compactPublicKeyTest32BitOnConfig(config) {
console.time("ClientKey Gen");
let clientKey = TfheClientKey.generate(config);
console.timeEnd("ClientKey Gen");
console.time("CompactPublicKey Gen");
let publicKey = TfheCompactPublicKey.new(clientKey);
console.timeEnd("CompactPublicKey Gen");
let serialized_pk = publicKey.serialize();
console.log("Serialized CompactPublicKey size: ", serialized_pk.length);
let values = [0, 1, 2394, U32_MAX];
console.time("CompactFheUint32List Encrypt");
let compact_list = CompactFheUint32List.encrypt_with_compact_public_key(
values,
publicKey,
);
console.timeEnd("CompactFheUint32List Encrypt");
{
console.time("CompactFheUint32List Expand");
let encrypted_list = compact_list.expand();
console.timeEnd("CompactFheUint32List Expand");
assert_eq(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert_eq(decrypted, values[i]);
}
}
let serialized_list = compact_list.serialize();
console.log("Serialized CompactFheUint32List size: ", serialized_list.length);
let deserialized_list = CompactFheUint32List.deserialize(serialized_list);
let encrypted_list = deserialized_list.expand();
assert_eq(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert_eq(decrypted, values[i]);
}
}
async function compactPublicKeyTest32BitBig() {
const block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS,
);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
await compactPublicKeyTest32BitOnConfig(config);
}
async function compactPublicKeyTest32BitSmall() {
const block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
await compactPublicKeyTest32BitOnConfig(config);
}
async function compactPublicKeyBench32BitOnConfig(config) {
const bench_loops = 100;
let bench_results = {};
@@ -185,23 +115,17 @@ async function compactPublicKeyBench32BitOnConfig(config) {
console.log("CompactPublicKey Gen bench: ", timing_1, " ms");
bench_results["compact_public_key_gen_32bit_mean"] = timing_1;
let values = [0, 1, 2, 2394, U32_MAX];
// Encrypt compact CT list for serialization for later
console.time("CompactFheUint32List Encrypt");
let compact_list = CompactFheUint32List.encrypt_with_compact_public_key(
values,
publicKey,
);
console.timeEnd("CompactFheUint32List Encrypt");
let values = [0, 1, 2, 2394, U32_MAX].map(BigInt);
// Bench the encryption for bench_loops iterations
start = performance.now();
let compact_list;
for (let i = 0; i < bench_loops; i++) {
let _ = CompactFheUint32List.encrypt_with_compact_public_key(
values,
publicKey,
);
let builder = CompactCiphertextList.builder(publicKey);
for (let value of values) {
builder.push_u256(value);
}
compact_list = builder.build();
}
end = performance.now();
const timing_2 = (end - start) / bench_loops;
@@ -250,74 +174,13 @@ async function compactPublicKeyBench32BitSmall() {
);
}
async function compactPublicKeyTest256BitOnConfig(config) {
console.time("ClientKey Gen");
let clientKey = TfheClientKey.generate(config);
console.timeEnd("ClientKey Gen");
console.time("CompactPublicKey Gen");
let publicKey = TfheCompactPublicKey.new(clientKey);
console.timeEnd("CompactPublicKey Gen");
let serialized_pk = publicKey.serialize();
console.log("Serialized CompactPublicKey size: ", serialized_pk.length);
let values = [0, 1, 2394, U32_MAX].map((e) => BigInt(e));
console.time("CompactFheUint256List Encrypt");
let compact_list = CompactFheUint256List.encrypt_with_compact_public_key(
values,
publicKey,
);
console.timeEnd("CompactFheUint256List Encrypt");
{
console.time("CompactFheUint256List Expand");
let encrypted_list = compact_list.expand();
console.timeEnd("CompactFheUint256List Expand");
assert_eq(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert_eq(decrypted, values[i]);
}
function generateRandomBigInt(bitLen) {
let result = BigInt(0);
for (let i = 0; i < bitLen; i++) {
result << 1n;
result |= BigInt(Math.random() < 0.5);
}
let serialized_list = compact_list.serialize();
console.log(
"Serialized CompactFheUint256List size: ",
serialized_list.length,
);
let deserialized_list = CompactFheUint256List.deserialize(serialized_list);
let encrypted_list = deserialized_list.expand();
assert_eq(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert_eq(decrypted, values[i]);
}
}
async function compactPublicKeyTest256BitBig() {
const block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS,
);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
await compactPublicKeyTest256BitOnConfig(config);
}
async function compactPublicKeyTest256BitSmall() {
const block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
);
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
await compactPublicKeyTest256BitOnConfig(config);
return result;
}
async function compressedCompactPublicKeyTest256BitOnConfig(config) {
@@ -339,51 +202,40 @@ async function compressedCompactPublicKeyTest256BitOnConfig(config) {
publicKey = publicKey.decompress();
console.timeEnd("CompressedCompactPublicKey Decompression");
let values = [0, 1, 2394, U32_MAX].map((e) => BigInt(e));
let clear_u2 = 3;
let clear_i32 = -3284;
let clear_bool = true;
let clear_u256 = generateRandomBigInt(256);
console.time("CompactFheUint256List Encrypt");
let compact_list = CompactFheUint256List.encrypt_with_compact_public_key(
values,
publicKey,
);
console.timeEnd("CompactFheUint256List Encrypt");
let builder = CompactCiphertextList.builder(publicKey);
builder.push_u2(clear_u2);
builder.push_i32(clear_i32);
builder.push_boolean(clear_bool);
builder.push_u256(clear_u256);
{
console.time("CompactFheUint256List Expand");
let encrypted_list = compact_list.expand();
console.timeEnd("CompactFheUint256List Expand");
let num_bits_encrypted = 2 + 4 + 1 + 256;
console.log("Numb bits in compact list: ", num_bits_encrypted);
assert_eq(encrypted_list.length, values.length);
console.time("CompactCiphertextList Encrypt");
let list = builder.build();
console.timeEnd("CompactCiphertextList Encrypt");
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert_eq(decrypted, values[i]);
}
}
let serialized_list = compact_list.serialize();
console.log(
"Serialized CompactFheUint256List size: ",
serialized_list.length,
let serialized = list.safe_serialize(BigInt(10000000));
console.log("Serialized CompactCiphertextList size: ", serialized.length);
let deserialized = CompactCiphertextList.safe_deserialize(
serialized,
BigInt(10000000),
);
let deserialized_list = CompactFheUint256List.deserialize(serialized_list);
let encrypted_list = deserialized_list.expand();
assert_eq(encrypted_list.length, values.length);
let expander = deserialized.expand();
for (let i = 0; i < values.length; i++) {
let decrypted = encrypted_list[i].decrypt(clientKey);
assert_eq(decrypted, values[i]);
}
}
assert_eq(expander.get_uint2(0).decrypt(clientKey), clear_u2);
function generateRandomBigInt(bitLen) {
let result = BigInt(0);
for (let i = 0; i < bitLen; i++) {
result << 1n;
result |= BigInt(Math.random() < 0.5);
}
return result;
assert_eq(expander.get_int32(1).decrypt(clientKey), clear_i32);
assert_eq(expander.get_bool(2).decrypt(clientKey), clear_bool);
assert_eq(expander.get_uint256(3).decrypt(clientKey), clear_u256);
}
async function compactPublicKeyZeroKnowledge() {
@@ -407,12 +259,10 @@ async function compactPublicKeyZeroKnowledge() {
{
let input = generateRandomBigInt(64);
let start = performance.now();
let encrypted = ProvenCompactFheUint64.encrypt_with_compact_public_key(
input,
public_params,
publicKey,
ZkComputeLoad.Proof,
);
let builder = CompactCiphertextList.builder(publicKey);
builder.push_u64(input);
let list = builder.build_with_proof(public_params, ZkComputeLoad.Proof);
let end = performance.now();
console.log(
"Time to encrypt + prove CompactFheUint64: ",
@@ -420,13 +270,11 @@ async function compactPublicKeyZeroKnowledge() {
" ms",
);
let bytes = encrypted.serialize();
console.log("ProvenCompactFheUint64 size:", bytes.length);
assert_eq(encrypted.verifies(public_params, publicKey), true);
let bytes = list.serialize();
console.log("CompactCiphertextList size:", bytes.length);
start = performance.now();
let expanded = encrypted.verify_and_expand(public_params, publicKey);
let expander = list.verify_and_expand(public_params, publicKey);
end = performance.now();
console.log(
"Time to verify + expand CompactFheUint64: ",
@@ -434,8 +282,7 @@ async function compactPublicKeyZeroKnowledge() {
" ms",
);
let decrypted = expanded.decrypt(clientKey);
assert_eq(decrypted, input);
assert_eq(expander.get_uint64(0).decrypt(clientKey), input);
}
{
@@ -446,10 +293,12 @@ async function compactPublicKeyZeroKnowledge() {
generateRandomBigInt(64),
];
let start = performance.now();
let encrypted = ProvenCompactFheUint64List.encrypt_with_compact_public_key(
inputs,
let builder = CompactCiphertextList.builder(publicKey);
for (let input of inputs) {
builder.push_u64(input);
}
let encrypted = builder.build_with_proof(
public_params,
publicKey,
ZkComputeLoad.Proof,
);
let end = performance.now();
@@ -458,10 +307,9 @@ async function compactPublicKeyZeroKnowledge() {
end - start,
" ms",
);
assert_eq(encrypted.verifies(public_params, publicKey), true);
start = performance.now();
let expanded_list = encrypted.verify_and_expand(public_params, publicKey);
let expander = encrypted.verify_and_expand(public_params, publicKey);
end = performance.now();
console.log(
"Time to verify + expand CompactFheUint64: ",
@@ -470,8 +318,7 @@ async function compactPublicKeyZeroKnowledge() {
);
for (let i = 0; i < inputs.length; i++) {
let decrypted = expanded_list[i].decrypt(clientKey);
assert_eq(decrypted, inputs[i]);
assert_eq(expander.get_uint64(i).decrypt(clientKey), inputs[i]);
}
}
}
@@ -521,21 +368,17 @@ async function compactPublicKeyBench256BitOnConfig(config) {
let values = [0, 1, 2, 2394, U32_MAX].map((e) => BigInt(e));
// Encrypt compact CT list for serialization for later
console.time("CompactFheUint256List Encrypt");
let compact_list = CompactFheUint256List.encrypt_with_compact_public_key(
values,
publicKey,
);
console.timeEnd("CompactFheUint256List Encrypt");
// Bench the encryption for bench_loops iterations
start = performance.now();
let compact_list;
for (let i = 0; i < bench_loops; i++) {
let _ = CompactFheUint256List.encrypt_with_compact_public_key(
values,
publicKey,
);
console.time("CompactFheUint256List Encrypt");
let builder = CompactCiphertextList.builder(publicKey);
for (let value of values) {
builder.push_u256(value);
}
compact_list = builder.build();
console.timeEnd("CompactFheUint256List Encrypt");
}
end = performance.now();
const timing_2 = (end - start) / bench_loops;
@@ -678,12 +521,10 @@ async function compactPublicKeyZeroKnowledgeBench() {
let input = generateRandomBigInt(64);
const start = performance.now();
let _ = ProvenCompactFheUint64.encrypt_with_compact_public_key(
input,
public_params,
publicKey,
loadChoice,
);
let builder = ProvenCompactCiphertextList.builder(publicKey);
builder.push_u64(input);
let list = builder.build_with_proof(public_params);
const end = performance.now();
timing += end - start;
}
@@ -708,10 +549,6 @@ async function main() {
return Comlink.proxy({
publicKeyTest,
compressedPublicKeyTest,
compactPublicKeyTest32BitSmall,
compactPublicKeyTest32BitBig,
compactPublicKeyTest256BitSmall,
compactPublicKeyTest256BitBig,
compressedCompactPublicKeyTest256BitSmall,
compressedCompactPublicKeyTest256BitBig,
compactPublicKeyZeroKnowledge,