mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
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:
129
tfhe/c_api_tests/test_high_level_compact_list.c
Normal file
129
tfhe/c_api_tests/test_high_level_compact_list.c
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
308
tfhe/src/c_api/high_level_api/compact_list.rs
Normal file
308
tfhe/src/c_api/high_level_api/compact_list.rs
Normal 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)));
|
||||
})
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
¶ms
|
||||
)
|
||||
.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
|
||||
|
||||
@@ -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(¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(¶ms)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ¶ms)
|
||||
.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,
|
||||
¶ms,
|
||||
)
|
||||
.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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
492
tfhe/src/high_level_api/compact_list.rs
Normal file
492
tfhe/src/high_level_api/compact_list.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(¶ms.params)
|
||||
}
|
||||
}
|
||||
@@ -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!(
|
||||
|
||||
@@ -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>]>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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, ¶ms)
|
||||
.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(¶ms));
|
||||
}
|
||||
|
||||
#[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, ¶ms)
|
||||
.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(¶ms));
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(¶ms.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(¶ms));
|
||||
|
||||
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(¶ms));
|
||||
}
|
||||
}
|
||||
|
||||
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(¶ms));
|
||||
}
|
||||
}
|
||||
|
||||
#[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(¶ms));
|
||||
|
||||
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(¶ms));
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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>]>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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, ¶ms)
|
||||
.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,
|
||||
¶ms,
|
||||
)
|
||||
.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(¶ms));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(¶ms.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
¶ms
|
||||
)
|
||||
.is_err());
|
||||
|
||||
let params = FheUint8ConformanceParams::from(&server_key);
|
||||
let ct2 =
|
||||
safe_deserialize_conformant::<CompactFheUint8>(buffer.as_slice(), 1 << 20, ¶ms)
|
||||
.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,
|
||||
¶m_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,
|
||||
¶ms,
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
let params = CompactFheUint8ListConformanceParams::from((
|
||||
&server_key,
|
||||
ListSizeConstraint::exact_size(3),
|
||||
));
|
||||
|
||||
let ct2 =
|
||||
safe_deserialize_conformant::<CompactFheUint8List>(buffer.as_slice(), 1 << 20, ¶ms)
|
||||
.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,
|
||||
¶ms,
|
||||
)
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -25,10 +25,6 @@ async function setup() {
|
||||
const demoNames = [
|
||||
"publicKeyTest",
|
||||
"compressedPublicKeyTest",
|
||||
"compactPublicKeyTest32BitBig",
|
||||
"compactPublicKeyTest32BitSmall",
|
||||
"compactPublicKeyTest256BitBig",
|
||||
"compactPublicKeyTest256BitSmall",
|
||||
"compressedCompactPublicKeyTest256BitBig",
|
||||
"compressedCompactPublicKeyTest256BitSmall",
|
||||
"compactPublicKeyZeroKnowledge",
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user