feat(core): Add Compact Public Key

- Based on "TFHE Public-Key Encryption Revisited "
  https://eprint.iacr.org/2023/603.pdf

Co-authored-by: tmontaigu <thomas.montaigu@laposte.net>
This commit is contained in:
Arthur Meyre
2023-06-06 10:28:58 +02:00
committed by tmontaigu
parent 200c8a177a
commit 3508019cd2
91 changed files with 5849 additions and 474 deletions

1
.gitignore vendored
View File

@@ -12,3 +12,4 @@ target/
# Some of our bench outputs
/tfhe/benchmarks_parameters
/tfhe/shortint_key_sizes.csv

View File

@@ -232,7 +232,13 @@ test_boolean: install_rs_build_toolchain
--features=$(TARGET_ARCH_FEATURE),boolean -p tfhe -- boolean::
.PHONY: test_c_api # Run the tests for the C API
test_c_api: build_c_api
test_c_api: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api \
-p tfhe \
c_api
"$(MAKE)" build_c_api
./scripts/c_api_tests.sh
.PHONY: test_shortint_ci # Run the tests for shortint ci
@@ -307,13 +313,15 @@ format_doc_latex:
@printf "\n===============================\n"
.PHONY: check_compile_tests # Build tests in debug without running them
check_compile_tests: build_c_api
check_compile_tests:
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --no-run \
--features=$(TARGET_ARCH_FEATURE),experimental,boolean,shortint,integer,internal-keycache \
-p tfhe
@if [[ "$(OS)" == "Linux" || "$(OS)" == "Darwin" ]]; then \
./scripts/c_api_tests.sh --build-only; \
fi
@if [[ "$(OS)" == "Linux" || "$(OS)" == "Darwin" ]]; then \
"$(MAKE)" build_c_api; \
./scripts/c_api_tests.sh --build-only; \
fi
.PHONY: build_nodejs_test_docker # Build a docker image with tools to run nodejs tests for wasm API
build_nodejs_test_docker:

View File

@@ -83,7 +83,12 @@ experimental-force_fft_algo_dif4 = []
__c_api = ["cbindgen", "bincode"]
boolean-c-api = ["boolean", "__c_api"]
shortint-c-api = ["shortint", "__c_api"]
high-level-c-api = ["boolean", "shortint", "integer", "__c_api"]
high-level-c-api = [
"boolean-c-api",
"shortint-c-api",
"integer",
"__c_api"
]
__wasm_api = [
"wasm-bindgen",
@@ -200,6 +205,10 @@ required-features = ["integer", "internal-keycache"]
name = "shortint_key_sizes"
required-features = ["shortint", "internal-keycache"]
[[example]]
name = "integer_compact_pk_ct_sizes"
required-features = ["integer", "internal-keycache"]
[[example]]
name = "micro_bench_and"
required-features = ["boolean"]

View File

@@ -9,22 +9,24 @@ int uint128_client_key(const ClientKey *client_key) {
FheUint128 *lhs = NULL;
FheUint128 *rhs = NULL;
FheUint128 *result = NULL;
U128 lhs_clear = {10, 20};
U128 rhs_clear = {1, 2};
U128 result_clear = {0};
ok = fhe_uint128_try_encrypt_with_client_key_u128(10, 20, client_key, &lhs);
ok = fhe_uint128_try_encrypt_with_client_key_u128(lhs_clear, client_key, &lhs);
assert(ok == 0);
ok = fhe_uint128_try_encrypt_with_client_key_u128(1, 2, client_key, &rhs);
ok = fhe_uint128_try_encrypt_with_client_key_u128(rhs_clear, client_key, &rhs);
assert(ok == 0);
ok = fhe_uint128_sub(lhs, rhs, &result);
assert(ok == 0);
uint64_t w0, w1;
ok = fhe_uint128_decrypt(result, client_key, &w0, &w1);
ok = fhe_uint128_decrypt(result, client_key, &result_clear);
assert(ok == 0);
assert(w0 == 9);
assert(w1 == 18);
assert(result_clear.w0 == 9);
assert(result_clear.w1 == 18);
fhe_uint128_destroy(lhs);
fhe_uint128_destroy(rhs);
@@ -37,22 +39,24 @@ int uint128_encrypt_trivial(const ClientKey *client_key) {
FheUint128 *lhs = NULL;
FheUint128 *rhs = NULL;
FheUint128 *result = NULL;
ok = fhe_uint128_try_encrypt_trivial_u128(10, 20, &lhs);
U128 lhs_clear = {10, 20};
U128 rhs_clear = {1, 2};
U128 result_clear = {0};
ok = fhe_uint128_try_encrypt_trivial_u128(lhs_clear, &lhs);
assert(ok == 0);
ok = fhe_uint128_try_encrypt_trivial_u128(1, 2, &rhs);
ok = fhe_uint128_try_encrypt_trivial_u128(rhs_clear, &rhs);
assert(ok == 0);
ok = fhe_uint128_sub(lhs, rhs, &result);
assert(ok == 0);
uint64_t w0, w1;
ok = fhe_uint128_decrypt(result, client_key, &w0, &w1);
ok = fhe_uint128_decrypt(result, client_key, &result_clear);
assert(ok == 0);
assert(w0 == 9);
assert(w1 == 18);
assert(result_clear.w0 == 9);
assert(result_clear.w1 == 18);
fhe_uint128_destroy(lhs);
fhe_uint128_destroy(rhs);
@@ -65,22 +69,24 @@ int uint128_public_key(const ClientKey *client_key, const PublicKey *public_key)
FheUint128 *lhs = NULL;
FheUint128 *rhs = NULL;
FheUint128 *result = NULL;
U128 lhs_clear = {10, 20};
U128 rhs_clear = {1, 2};
U128 result_clear = {0};
ok = fhe_uint128_try_encrypt_with_public_key_u128(1, 2, public_key, &lhs);
ok = fhe_uint128_try_encrypt_with_public_key_u128(lhs_clear, public_key, &lhs);
assert(ok == 0);
ok = fhe_uint128_try_encrypt_with_public_key_u128(10, 20, public_key, &rhs);
ok = fhe_uint128_try_encrypt_with_public_key_u128(rhs_clear, public_key, &rhs);
assert(ok == 0);
ok = fhe_uint128_add(lhs, rhs, &result);
assert(ok == 0);
uint64_t w0, w1;
ok = fhe_uint128_decrypt(result, client_key, &w0, &w1);
ok = fhe_uint128_decrypt(result, client_key, &result_clear);
assert(ok == 0);
assert(w0 == 11);
assert(w1 == 22);
assert(result_clear.w0 == 11);
assert(result_clear.w1 == 22);
fhe_uint128_destroy(lhs);
fhe_uint128_destroy(rhs);

View File

@@ -10,14 +10,9 @@ int uint256_client_key(const ClientKey *client_key) {
FheUint256 *rhs = NULL;
FheUint256 *result = NULL;
FheUint64 *cast_result = NULL;
U256 *lhs_clear = NULL;
U256 *rhs_clear = NULL;
U256 *result_clear = NULL;
ok = u256_from_u64_words(1, 2, 3, 4, &lhs_clear);
assert(ok == 0);
ok = u256_from_u64_words(5, 6, 7, 8, &rhs_clear);
assert(ok == 0);
U256 lhs_clear = {1 , 2, 3, 4};
U256 rhs_clear = {5, 6, 7, 8};
U256 result_clear = { 0 };
ok = fhe_uint256_try_encrypt_with_client_key_u256(lhs_clear, client_key, &lhs);
assert(ok == 0);
@@ -31,14 +26,10 @@ int uint256_client_key(const ClientKey *client_key) {
ok = fhe_uint256_decrypt(result, client_key, &result_clear);
assert(ok == 0);
uint64_t w0, w1, w2, w3;
ok = u256_to_u64_words(result_clear, &w0, &w1, &w2, &w3);
assert(ok == 0);
assert(w0 == 6);
assert(w1 == 8);
assert(w2 == 10);
assert(w3 == 12);
assert(result_clear.w0 == 6);
assert(result_clear.w1 == 8);
assert(result_clear.w2 == 10);
assert(result_clear.w3 == 12);
// try some casting
ok = fhe_uint256_cast_into_fhe_uint64(result, &cast_result);
@@ -48,9 +39,6 @@ int uint256_client_key(const ClientKey *client_key) {
assert(ok == 0);
assert(u64_clear == 6);
u256_destroy(lhs_clear);
u256_destroy(rhs_clear);
u256_destroy(result_clear);
fhe_uint256_destroy(lhs);
fhe_uint256_destroy(rhs);
fhe_uint256_destroy(result);
@@ -63,14 +51,9 @@ int uint256_encrypt_trivial(const ClientKey *client_key) {
FheUint256 *lhs = NULL;
FheUint256 *rhs = NULL;
FheUint256 *result = NULL;
U256 *lhs_clear = NULL;
U256 *rhs_clear = NULL;
U256 *result_clear = NULL;
ok = u256_from_u64_words(1, 2, 3, 4, &lhs_clear);
assert(ok == 0);
ok = u256_from_u64_words(5, 6, 7, 8, &rhs_clear);
assert(ok == 0);
U256 lhs_clear = {1 , 2, 3, 4};
U256 rhs_clear = {5, 6, 7, 8};
U256 result_clear = { 0 };
ok = fhe_uint256_try_encrypt_trivial_u256(lhs_clear, &lhs);
assert(ok == 0);
@@ -84,18 +67,11 @@ int uint256_encrypt_trivial(const ClientKey *client_key) {
ok = fhe_uint256_decrypt(result, client_key, &result_clear);
assert(ok == 0);
uint64_t w0, w1, w2, w3;
ok = u256_to_u64_words(result_clear, &w0, &w1, &w2, &w3);
assert(ok == 0);
assert(result_clear.w0 == 6);
assert(result_clear.w1 == 8);
assert(result_clear.w2 == 10);
assert(result_clear.w3 == 12);
assert(w0 == 6);
assert(w1 == 8);
assert(w2 == 10);
assert(w3 == 12);
u256_destroy(lhs_clear);
u256_destroy(rhs_clear);
u256_destroy(result_clear);
fhe_uint256_destroy(lhs);
fhe_uint256_destroy(rhs);
fhe_uint256_destroy(result);
@@ -107,14 +83,9 @@ int uint256_public_key(const ClientKey *client_key, const PublicKey *public_key)
FheUint256 *lhs = NULL;
FheUint256 *rhs = NULL;
FheUint256 *result = NULL;
U256 *lhs_clear = NULL;
U256 *rhs_clear = NULL;
U256 *result_clear = NULL;
ok = u256_from_u64_words(5, 6, 7, 8, &lhs_clear);
assert(ok == 0);
ok = u256_from_u64_words(1, 2, 3, 4, &rhs_clear);
assert(ok == 0);
U256 lhs_clear = {5, 6, 7, 8};
U256 rhs_clear = {1 , 2, 3, 4};
U256 result_clear = { 0 };
ok = fhe_uint256_try_encrypt_with_public_key_u256(lhs_clear, public_key, &lhs);
assert(ok == 0);
@@ -128,18 +99,11 @@ int uint256_public_key(const ClientKey *client_key, const PublicKey *public_key)
ok = fhe_uint256_decrypt(result, client_key, &result_clear);
assert(ok == 0);
uint64_t w0, w1, w2, w3;
ok = u256_to_u64_words(result_clear, &w0, &w1, &w2, &w3);
assert(ok == 0);
assert(result_clear.w0 == 4);
assert(result_clear.w1 == 4);
assert(result_clear.w2 == 4);
assert(result_clear.w3 == 4);
assert(w0 == 4);
assert(w1 == 4);
assert(w2 == 4);
assert(w3 == 4);
u256_destroy(lhs_clear);
u256_destroy(rhs_clear);
u256_destroy(result_clear);
fhe_uint256_destroy(lhs);
fhe_uint256_destroy(rhs);
fhe_uint256_destroy(result);

View File

@@ -0,0 +1,213 @@
#include <tfhe.h>
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
int uint256_client_key(const ClientKey *client_key) {
int ok;
FheUint256 *lhs = NULL;
FheUint256 *rhs = NULL;
FheUint256 *result = NULL;
FheUint64 *cast_result = NULL;
U256 lhs_clear = {1, 2, 3, 4};
U256 rhs_clear = {5, 6, 7, 8};
U256 result_clear = {0};
ok = fhe_uint256_try_encrypt_with_client_key_u256(lhs_clear, client_key, &lhs);
assert(ok == 0);
ok = fhe_uint256_try_encrypt_with_client_key_u256(rhs_clear, client_key, &rhs);
assert(ok == 0);
ok = fhe_uint256_add(lhs, rhs, &result);
assert(ok == 0);
ok = fhe_uint256_decrypt(result, client_key, &result_clear);
assert(ok == 0);
assert(result_clear.w0 == 6);
assert(result_clear.w1 == 8);
assert(result_clear.w2 == 10);
assert(result_clear.w3 == 12);
// try some casting
ok = fhe_uint256_cast_into_fhe_uint64(result, &cast_result);
assert(ok == 0);
uint64_t u64_clear;
ok = fhe_uint64_decrypt(cast_result, client_key, &u64_clear);
assert(ok == 0);
assert(u64_clear == 6);
fhe_uint256_destroy(lhs);
fhe_uint256_destroy(rhs);
fhe_uint256_destroy(result);
fhe_uint64_destroy(cast_result);
return ok;
}
int uint256_encrypt_trivial(const ClientKey *client_key) {
int ok;
FheUint256 *lhs = NULL;
FheUint256 *rhs = NULL;
FheUint256 *result = NULL;
U256 lhs_clear = {1, 2, 3, 4};
U256 rhs_clear = {5, 6, 7, 8};
U256 result_clear = {0};
ok = fhe_uint256_try_encrypt_trivial_u256(lhs_clear, &lhs);
assert(ok == 0);
ok = fhe_uint256_try_encrypt_trivial_u256(rhs_clear, &rhs);
assert(ok == 0);
ok = fhe_uint256_add(lhs, rhs, &result);
assert(ok == 0);
ok = fhe_uint256_decrypt(result, client_key, &result_clear);
assert(ok == 0);
assert(result_clear.w0 == 6);
assert(result_clear.w1 == 8);
assert(result_clear.w2 == 10);
assert(result_clear.w3 == 12);
fhe_uint256_destroy(lhs);
fhe_uint256_destroy(rhs);
fhe_uint256_destroy(result);
return ok;
}
int uint256_public_key(const ClientKey *client_key,
const CompressedCompactPublicKey *compressed_public_key) {
int ok;
CompactPublicKey *public_key = NULL;
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}};
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];
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);
ok = fhe_uint256_try_encrypt_with_compact_public_key_u256(clears[1], public_key, &rhs);
assert(ok == 0);
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);
}
compact_public_key_destroy(public_key);
return ok;
}
int main(void) {
int ok = 0;
{
ConfigBuilder *builder;
Config *config;
config_builder_all_disabled(&builder);
config_builder_enable_custom_integers(&builder, SHORTINT_PARAM_MESSAGE_2_CARRY_2_COMPACT_PK);
config_builder_build(builder, &config);
ClientKey *client_key = NULL;
ServerKey *server_key = NULL;
CompressedCompactPublicKey *compressed_public_key = NULL;
generate_keys(config, &client_key, &server_key);
compressed_compact_public_key_new(client_key, &compressed_public_key);
set_server_key(server_key);
uint256_client_key(client_key);
uint256_encrypt_trivial(client_key);
uint256_public_key(client_key, compressed_public_key);
client_key_destroy(client_key);
compressed_compact_public_key_destroy(compressed_public_key);
server_key_destroy(server_key);
}
{
ConfigBuilder *builder;
Config *config;
config_builder_all_disabled(&builder);
config_builder_enable_custom_integers(&builder,
SHORTINT_PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
config_builder_build(builder, &config);
ClientKey *client_key = NULL;
ServerKey *server_key = NULL;
CompressedCompactPublicKey *compressed_public_key = NULL;
generate_keys(config, &client_key, &server_key);
compressed_compact_public_key_new(client_key, &compressed_public_key);
set_server_key(server_key);
uint256_client_key(client_key);
uint256_encrypt_trivial(client_key);
uint256_public_key(client_key, compressed_public_key);
client_key_destroy(client_key);
compressed_compact_public_key_destroy(compressed_public_key);
server_key_destroy(server_key);
}
return ok;
}

View File

@@ -43,6 +43,8 @@ void micro_bench_and() {
destroy_boolean_client_key(cks);
destroy_boolean_server_key(sks);
destroy_boolean_ciphertext(ct_left);
destroy_boolean_ciphertext(ct_right);
}
int main(void) {

View File

@@ -8,16 +8,13 @@
void test_predefined_keygen_w_serde(void) {
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
ShortintCiphertext *ct = NULL;
Buffer ct_ser_buffer = {.pointer = NULL, .length = 0};
ShortintCiphertext *deser_ct = NULL;
ShortintCompressedCiphertext *cct = NULL;
ShortintCompressedCiphertext *deser_cct = NULL;
ShortintCiphertext *decompressed_ct = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
ShortintPBSParameters params = SHORTINT_PARAM_MESSAGE_2_CARRY_2;
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
@@ -67,7 +64,6 @@ void test_predefined_keygen_w_serde(void) {
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_parameters(params);
destroy_shortint_ciphertext(ct);
destroy_shortint_ciphertext(deser_ct);
destroy_shortint_compressed_ciphertext(cct);
@@ -79,11 +75,8 @@ void test_predefined_keygen_w_serde(void) {
void test_server_key_trivial_encrypt(void) {
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
ShortintCiphertext *ct = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
ShortintPBSParameters params = SHORTINT_PARAM_MESSAGE_2_CARRY_2;
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
@@ -99,24 +92,32 @@ void test_server_key_trivial_encrypt(void) {
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_parameters(params);
destroy_shortint_ciphertext(ct);
}
void test_custom_keygen(void) {
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
int params_ok = shortint_create_parameters(10, 1, 1024, 10e-100, 10e-100, 2, 3, 2, 3, 2, 2, 64,
ShortintEncryptionKeyChoiceBig, &params);
assert(params_ok == 0);
ShortintPBSParameters params = {
.lwe_dimension = 10,
.glwe_dimension = 1,
.polynomial_size = 1024,
.lwe_modular_std_dev = 10e-100,
.glwe_modular_std_dev = 10e-100,
.pbs_base_log = 2,
.pbs_level = 3,
.ks_base_log = 2,
.ks_level = 3,
.message_modulus = 2,
.carry_modulus = 2,
.modulus_power_of_2_exponent = 64,
.encryption_key_choice = ShortintEncryptionKeyChoiceBig,
};
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
destroy_shortint_parameters(params);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
}
@@ -125,12 +126,9 @@ void test_public_keygen(ShortintPublicKeyKind pk_kind) {
ShortintClientKey *cks = NULL;
ShortintPublicKey *pks = NULL;
ShortintPublicKey *pks_deser = NULL;
ShortintParameters *params = NULL;
ShortintCiphertext *ct = NULL;
Buffer pks_ser_buff = {.pointer = NULL, .length = 0};
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
ShortintPBSParameters params = SHORTINT_PARAM_MESSAGE_2_CARRY_2;
int gen_keys_ok = shortint_gen_client_key(params, &cks);
assert(gen_keys_ok == 0);
@@ -156,7 +154,6 @@ void test_public_keygen(ShortintPublicKeyKind pk_kind) {
assert(result == 2);
destroy_shortint_parameters(params);
destroy_shortint_client_key(cks);
destroy_shortint_public_key(pks);
destroy_shortint_public_key(pks_deser);
@@ -168,11 +165,8 @@ void test_compressed_public_keygen(ShortintPublicKeyKind pk_kind) {
ShortintClientKey *cks = NULL;
ShortintCompressedPublicKey *cpks = NULL;
ShortintPublicKey *pks = NULL;
ShortintParameters *params = NULL;
ShortintCiphertext *ct = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
ShortintPBSParameters params = SHORTINT_PARAM_MESSAGE_2_CARRY_2;
int gen_keys_ok = shortint_gen_client_key(params, &cks);
assert(gen_keys_ok == 0);
@@ -203,7 +197,6 @@ void test_compressed_public_keygen(ShortintPublicKeyKind pk_kind) {
assert(result == 2);
destroy_shortint_parameters(params);
destroy_shortint_client_key(cks);
destroy_shortint_compressed_public_key(cpks);
destroy_shortint_public_key(pks);

View File

@@ -41,10 +41,7 @@ void test_shortint_pbs_2_bits_message(void) {
ShortintPBSLookupTable *accumulator = NULL;
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
ShortintPBSParameters params = SHORTINT_PARAM_MESSAGE_2_CARRY_2;
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
@@ -111,17 +108,13 @@ void test_shortint_pbs_2_bits_message(void) {
destroy_shortint_pbs_accumulator(accumulator);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_parameters(params);
}
void test_shortint_bivariate_pbs_2_bits_message(void) {
ShortintBivariatePBSLookupTable *accumulator = NULL;
ShortintClientKey *cks = NULL;
ShortintServerKey *sks = NULL;
ShortintParameters *params = NULL;
int get_params_ok = shortint_get_parameters(2, 2, &params);
assert(get_params_ok == 0);
ShortintPBSParameters params = SHORTINT_PARAM_MESSAGE_2_CARRY_2;
int gen_keys_ok = shortint_gen_keys_with_parameters(params, &cks, &sks);
assert(gen_keys_ok == 0);
@@ -187,7 +180,6 @@ void test_shortint_bivariate_pbs_2_bits_message(void) {
destroy_shortint_bivariate_pbs_accumulator(accumulator);
destroy_shortint_client_key(cks);
destroy_shortint_server_key(sks);
destroy_shortint_parameters(params);
}
int main(void) {

View File

@@ -419,10 +419,10 @@ void test_server_key(void) {
ShortintCompressedServerKey *deser_csks = NULL;
Buffer sks_ser_buffer = {.pointer = NULL, .length = 0};
ShortintServerKey *deser_sks = NULL;
ShortintParameters *params = NULL;
ShortintClientKey *cks_small = NULL;
ShortintServerKey *sks_small = NULL;
ShortintParameters *params_small = NULL;
ShortintPBSParameters params = { 0 };
ShortintPBSParameters params_small = { 0 };
const uint32_t message_bits = 2;
const uint32_t carry_bits = 2;
@@ -736,8 +736,6 @@ void test_server_key(void) {
destroy_shortint_client_key(deser_cks);
destroy_shortint_compressed_server_key(deser_csks);
destroy_shortint_server_key(deser_sks);
destroy_shortint_parameters(params);
destroy_shortint_parameters(params_small);
destroy_buffer(&cks_ser_buffer);
destroy_buffer(&csks_ser_buffer);
destroy_buffer(&sks_ser_buffer);

View File

@@ -0,0 +1,92 @@
use rand::Rng;
use tfhe::core_crypto::commons::numeric::Numeric;
use tfhe::integer::block_decomposition::{DecomposableInto, RecomposableFrom};
use tfhe::integer::public_key::{CompactPublicKeyBig, CompactPublicKeySmall};
use tfhe::integer::{gen_keys, U256};
use tfhe::shortint::keycache::NamedParam;
use tfhe::shortint::parameters::*;
pub fn main() {
fn size_func<Scalar: Numeric + DecomposableInto<u64> + RecomposableFrom<u64> + From<u32>>() {
let mut rng = rand::thread_rng();
let num_bits = Scalar::BITS;
let params = PARAM_MESSAGE_2_CARRY_2_COMPACT_PK;
{
println!("Sizes for: {} and {num_bits} bits", params.name());
let (cks, _) = gen_keys(params);
let pk = CompactPublicKeyBig::new(&cks);
println!("PK size: {} bytes", bincode::serialize(&pk).unwrap().len());
let num_block =
(num_bits as f64 / (params.message_modulus.0 as f64).log(2.0)).ceil() as usize;
const MAX_CT: usize = 20;
let mut clear_vec = Vec::with_capacity(MAX_CT);
// 5 inputs to a smart contract
let num_ct_for_this_iter = 5;
clear_vec.truncate(0);
for _ in 0..num_ct_for_this_iter {
let clear = rng.gen::<u32>();
clear_vec.push(Scalar::from(clear));
}
let compact_encrypted_list = pk.encrypt_slice_radix_compact(&clear_vec, num_block);
println!(
"Compact CT list for {num_ct_for_this_iter} CTs: {} bytes",
bincode::serialize(&compact_encrypted_list).unwrap().len()
);
let ciphertext_vec = compact_encrypted_list.expand();
for (ciphertext, clear) in ciphertext_vec.iter().zip(clear_vec.iter().copied()) {
let decrypted: Scalar = cks.decrypt_radix(ciphertext);
assert_eq!(decrypted, clear);
}
}
let params = PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK;
{
println!("Sizes for: {} and {num_bits} bits", params.name());
let (cks, _) = gen_keys(params);
let pk = CompactPublicKeySmall::new(&cks);
println!("PK size: {} bytes", bincode::serialize(&pk).unwrap().len());
let num_block =
(num_bits as f64 / (params.message_modulus.0 as f64).log(2.0)).ceil() as usize;
const MAX_CT: usize = 20;
let mut clear_vec = Vec::with_capacity(MAX_CT);
// 5 inputs to a smart contract
let num_ct_for_this_iter = 5;
clear_vec.truncate(0);
for _ in 0..num_ct_for_this_iter {
let clear = rng.gen::<u32>();
clear_vec.push(Scalar::from(clear));
}
let compact_encrypted_list = pk.encrypt_slice_radix_compact(&clear_vec, num_block);
println!(
"Compact CT list for {num_ct_for_this_iter} CTs: {} bytes",
bincode::serialize(&compact_encrypted_list).unwrap().len()
);
let ciphertext_vec = compact_encrypted_list.expand();
for (ciphertext, clear) in ciphertext_vec.iter().zip(clear_vec.iter().copied()) {
let decrypted: Scalar = cks.decrypt_radix(ciphertext);
assert_eq!(decrypted, clear);
}
}
}
size_func::<u32>();
size_func::<U256>();
}

View File

@@ -2,17 +2,24 @@ const test = require('node:test');
const assert = require('node:assert').strict;
const { performance } = require('perf_hooks');
const {
ShortintParametersName,
ShortintParameters,
TfheClientKey,
TfhePublicKey,
TfheCompressedPublicKey,
TfheCompactPublicKey,
TfheCompressedServerKey,
TfheConfigBuilder,
CompressedFheUint8,
FheUint8,
FheUint32,
CompactFheUint32,
CompactFheUint32List,
CompressedFheUint128,
FheUint128,
CompressedFheUint256,
CompactFheUint256,
CompactFheUint256List,
FheUint256
} = require("../pkg/tfhe.js");
@@ -306,3 +313,243 @@ test('hlapi_public_key_encrypt_decrypt_uint256_small', (t) => {
assert.deepStrictEqual(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);
}
test('hlapi_compact_public_key_encrypt_decrypt_uint32_big_single', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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);
}
test('hlapi_compact_public_key_encrypt_decrypt_uint32_small_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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);
}
test('hlapi_compact_public_key_encrypt_decrypt_uint256_big_single', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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);
}
test('hlapi_compact_public_key_encrypt_decrypt_uint256_small_single_compact', (t) => {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(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);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(block_params)
.build();
hlapi_compact_public_key_encrypt_decrypt_uint256_list_compact(config);
});

View File

@@ -94,7 +94,7 @@ impl ServerKey {
}
pub fn bootstrapping_key_size_bytes(&self) -> usize {
self.bootstrapping_key_size_elements() * std::mem::size_of::<concrete_fft::c64>()
std::mem::size_of_val(self.bootstrapping_key.as_view().data())
}
pub fn key_switching_key_size_elements(&self) -> usize {
@@ -102,7 +102,7 @@ impl ServerKey {
}
pub fn key_switching_key_size_bytes(&self) -> usize {
self.key_switching_key_size_elements() * std::mem::size_of::<u64>()
std::mem::size_of_val(self.key_switching_key.as_ref())
}
}

View File

@@ -72,6 +72,20 @@ define_enable_default_fn!(integers);
#[cfg(feature = "integer")]
define_enable_default_fn!(integers @small);
#[no_mangle]
pub unsafe extern "C" fn config_builder_enable_custom_integers(
builder: *mut *mut ConfigBuilder,
shortint_block_parameters: crate::c_api::shortint::parameters::ShortintPBSParameters,
) -> ::std::os::raw::c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(builder).unwrap();
let inner = Box::from_raw(*builder)
.0
.enable_custom_integers(shortint_block_parameters.into(), None);
*builder = Box::into_raw(Box::new(ConfigBuilder(inner)));
})
}
/// Takes ownership of the builder
#[no_mangle]
pub unsafe extern "C" fn config_builder_build(

View File

@@ -1,10 +1,11 @@
use crate::c_api::high_level_api::keys::{ClientKey, PublicKey};
use crate::c_api::high_level_api::keys::{ClientKey, CompactPublicKey, PublicKey};
use crate::high_level_api::prelude::*;
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, MulAssign,
Neg, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
use crate::c_api::high_level_api::u128::U128;
use crate::c_api::high_level_api::u256::U256;
use crate::c_api::utils::*;
use std::os::raw::c_int;
@@ -67,6 +68,49 @@ macro_rules! create_integer_wrapper_type {
})
}
}
// 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>]);
#[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)));
}
})
}
}
};
}
@@ -84,52 +128,65 @@ impl_decrypt_on_type!(FheUint8, u8);
impl_try_encrypt_trivial_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
impl_try_encrypt_with_client_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
impl_try_encrypt_with_public_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
impl_try_encrypt_with_compact_public_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint8{crate::high_level_api::CompressedFheUint8}, u8);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint8List{crate::high_level_api::CompactFheUint8List}, u8);
impl_decrypt_on_type!(FheUint10, u16);
impl_try_encrypt_trivial_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
impl_try_encrypt_with_client_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
impl_try_encrypt_with_public_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
impl_try_encrypt_with_compact_public_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint10{crate::high_level_api::CompressedFheUint10}, u16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint10List{crate::high_level_api::CompactFheUint10List}, u16);
impl_decrypt_on_type!(FheUint12, u16);
impl_try_encrypt_trivial_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
impl_try_encrypt_with_client_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
impl_try_encrypt_with_public_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
impl_try_encrypt_with_compact_public_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint12{crate::high_level_api::CompressedFheUint12}, u16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint12List{crate::high_level_api::CompactFheUint12List}, u16);
impl_decrypt_on_type!(FheUint14, u16);
impl_try_encrypt_trivial_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
impl_try_encrypt_with_client_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
impl_try_encrypt_with_public_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
impl_try_encrypt_with_compact_public_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint14{crate::high_level_api::CompressedFheUint14}, u16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint14List{crate::high_level_api::CompactFheUint14List}, u16);
impl_decrypt_on_type!(FheUint16, u16);
impl_try_encrypt_trivial_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
impl_try_encrypt_with_client_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
impl_try_encrypt_with_public_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
impl_try_encrypt_with_compact_public_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint16{crate::high_level_api::CompressedFheUint16}, u16);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint16List{crate::high_level_api::CompactFheUint16List}, u16);
impl_decrypt_on_type!(FheUint32, u32);
impl_try_encrypt_trivial_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
impl_try_encrypt_with_client_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
impl_try_encrypt_with_public_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
impl_try_encrypt_with_compact_public_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint32{crate::high_level_api::CompressedFheUint32}, u32);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint32List{crate::high_level_api::CompactFheUint32List}, u32);
impl_decrypt_on_type!(FheUint64, u64);
impl_try_encrypt_trivial_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
impl_try_encrypt_with_client_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
impl_try_encrypt_with_public_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
impl_try_encrypt_with_compact_public_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint64{crate::high_level_api::CompressedFheUint64}, u64);
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint64List{crate::high_level_api::CompactFheUint64List}, u64);
#[no_mangle]
pub unsafe extern "C" fn fhe_uint128_try_encrypt_trivial_u128(
low_word: u64,
high_word: u64,
value: U128,
result: *mut *mut FheUint128,
) -> c_int {
catch_panic(|| {
let value = ((high_word as u128) << 64u128) | low_word as u128;
let value = u128::from(value);
let inner = <crate::high_level_api::FheUint128>::try_encrypt_trivial(value).unwrap();
@@ -139,15 +196,14 @@ pub unsafe extern "C" fn fhe_uint128_try_encrypt_trivial_u128(
#[no_mangle]
pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_client_key_u128(
low_word: u64,
high_word: u64,
value: U128,
client_key: *const ClientKey,
result: *mut *mut FheUint128,
) -> c_int {
catch_panic(|| {
let client_key = get_ref_checked(client_key).unwrap();
let value = ((high_word as u128) << 64u128) | low_word as u128;
let value = u128::from(value);
let inner = <crate::high_level_api::FheUint128>::try_encrypt(value, &client_key.0).unwrap();
@@ -157,15 +213,14 @@ pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_client_key_u128(
#[no_mangle]
pub unsafe extern "C" fn compressed_fhe_uint128_try_encrypt_with_client_key_u128(
low_word: u64,
high_word: u64,
value: U128,
client_key: *const ClientKey,
result: *mut *mut CompressedFheUint128,
) -> c_int {
catch_panic(|| {
let client_key = get_ref_checked(client_key).unwrap();
let value = ((high_word as u128) << 64u128) | low_word as u128;
let value = u128::from(value);
let inner =
<crate::high_level_api::CompressedFheUint128>::try_encrypt(value, &client_key.0)
@@ -177,15 +232,14 @@ pub unsafe extern "C" fn compressed_fhe_uint128_try_encrypt_with_client_key_u128
#[no_mangle]
pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_public_key_u128(
low_word: u64,
high_word: u64,
value: U128,
public_key: *const PublicKey,
result: *mut *mut FheUint128,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let value = ((high_word as u128) << 64u128) | low_word as u128;
let value = u128::from(value);
let inner = <crate::high_level_api::FheUint128>::try_encrypt(value, &public_key.0).unwrap();
@@ -193,12 +247,48 @@ pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_public_key_u128(
})
}
#[no_mangle]
pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_compact_public_key_u128(
value: U128,
public_key: *const CompactPublicKey,
result: *mut *mut FheUint128,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let value = u128::from(value);
let inner = <crate::high_level_api::FheUint128>::try_encrypt(value, &public_key.0).unwrap();
*result = Box::into_raw(Box::new(FheUint128(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_fhe_uint256_list_try_encrypt_with_compact_public_key_u128(
input: *const U128,
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(u128::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 fhe_uint128_decrypt(
encrypted_value: *const FheUint128,
client_key: *const ClientKey,
low_word: *mut u64,
high_word: *mut u64,
result: *mut U128,
) -> c_int {
catch_panic(|| {
let client_key = get_ref_checked(client_key).unwrap();
@@ -206,18 +296,18 @@ pub unsafe extern "C" fn fhe_uint128_decrypt(
let inner: u128 = encrypted_value.0.decrypt(&client_key.0);
*low_word = (inner & (u64::MAX as u128)) as u64;
*high_word = (inner >> 64) as u64;
*result = U128::from(inner);
})
}
#[no_mangle]
pub unsafe extern "C" fn fhe_uint256_try_encrypt_trivial_u256(
value: *const U256,
value: U256,
result: *mut *mut FheUint256,
) -> c_int {
catch_panic(|| {
let inner = <crate::high_level_api::FheUint256>::try_encrypt_trivial((*value).0).unwrap();
let value = crate::integer::U256::from(value);
let inner = <crate::high_level_api::FheUint256>::try_encrypt_trivial(value).unwrap();
*result = Box::into_raw(Box::new(FheUint256(inner)));
})
@@ -225,15 +315,15 @@ pub unsafe extern "C" fn fhe_uint256_try_encrypt_trivial_u256(
#[no_mangle]
pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_client_key_u256(
value: *const U256,
value: U256,
client_key: *const ClientKey,
result: *mut *mut FheUint256,
) -> c_int {
catch_panic(|| {
let client_key = get_ref_checked(client_key).unwrap();
let inner =
<crate::high_level_api::FheUint256>::try_encrypt((*value).0, &client_key.0).unwrap();
let value = crate::integer::U256::from(value);
let inner = <crate::high_level_api::FheUint256>::try_encrypt(value, &client_key.0).unwrap();
*result = Box::into_raw(Box::new(FheUint256(inner)));
})
@@ -241,15 +331,16 @@ pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_client_key_u256(
#[no_mangle]
pub unsafe extern "C" fn compressed_fhe_uint256_try_encrypt_with_client_key_u256(
value: *const U256,
value: U256,
client_key: *const ClientKey,
result: *mut *mut CompressedFheUint256,
) -> c_int {
catch_panic(|| {
let client_key = get_ref_checked(client_key).unwrap();
let value = crate::integer::U256::from(value);
let inner =
<crate::high_level_api::CompressedFheUint256>::try_encrypt((*value).0, &client_key.0)
<crate::high_level_api::CompressedFheUint256>::try_encrypt(value, &client_key.0)
.unwrap();
*result = Box::into_raw(Box::new(CompressedFheUint256(inner)));
@@ -258,32 +349,72 @@ pub unsafe extern "C" fn compressed_fhe_uint256_try_encrypt_with_client_key_u256
#[no_mangle]
pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_public_key_u256(
value: *const U256,
value: U256,
public_key: *const PublicKey,
result: *mut *mut FheUint256,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let inner =
<crate::high_level_api::FheUint256>::try_encrypt((*value).0, &public_key.0).unwrap();
let value = crate::integer::U256::from(value);
let inner = <crate::high_level_api::FheUint256>::try_encrypt(value, &public_key.0).unwrap();
*result = Box::into_raw(Box::new(FheUint256(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_compact_public_key_u256(
value: U256,
public_key: *const CompactPublicKey,
result: *mut *mut FheUint256,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
let value = crate::integer::U256::from(value);
let inner = <crate::high_level_api::FheUint256>::try_encrypt(value, &public_key.0).unwrap();
*result = Box::into_raw(Box::new(FheUint256(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 fhe_uint256_decrypt(
encrypted_value: *const FheUint256,
client_key: *const ClientKey,
result: *mut *mut U256,
result: *mut U256,
) -> c_int {
catch_panic(|| {
let client_key = get_ref_checked(client_key).unwrap();
let encrypted_value = get_ref_checked(encrypted_value).unwrap();
let inner: crate::integer::U256 = encrypted_value.0.decrypt(&client_key.0);
*result = Box::into_raw(Box::new(U256(inner)));
*result = U256::from(inner);
})
}

View File

@@ -3,14 +3,20 @@ use std::os::raw::c_int;
pub struct ClientKey(pub(crate) crate::high_level_api::ClientKey);
pub struct PublicKey(pub(crate) crate::high_level_api::PublicKey);
pub struct CompactPublicKey(pub(crate) crate::high_level_api::CompactPublicKey);
pub struct CompressedCompactPublicKey(pub(crate) crate::high_level_api::CompressedCompactPublicKey);
pub struct ServerKey(pub(crate) crate::high_level_api::ServerKey);
impl_destroy_on_type!(ClientKey);
impl_destroy_on_type!(PublicKey);
impl_destroy_on_type!(CompactPublicKey);
impl_destroy_on_type!(CompressedCompactPublicKey);
impl_destroy_on_type!(ServerKey);
impl_serialize_deserialize_on_type!(ClientKey);
impl_serialize_deserialize_on_type!(PublicKey);
impl_serialize_deserialize_on_type!(CompactPublicKey);
impl_serialize_deserialize_on_type!(CompressedCompactPublicKey);
impl_serialize_deserialize_on_type!(ServerKey);
#[no_mangle]
@@ -69,3 +75,43 @@ pub unsafe extern "C" fn public_key_new(
*result_public_key = Box::into_raw(Box::new(PublicKey(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_public_key_new(
client_key: *const ClientKey,
result_public_key: *mut *mut CompactPublicKey,
) -> c_int {
catch_panic(|| {
let client_key = get_ref_checked(client_key).unwrap();
let inner = crate::high_level_api::CompactPublicKey::new(&client_key.0);
*result_public_key = Box::into_raw(Box::new(CompactPublicKey(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compressed_compact_public_key_new(
client_key: *const ClientKey,
result_public_key: *mut *mut CompressedCompactPublicKey,
) -> c_int {
catch_panic(|| {
let client_key = get_ref_checked(client_key).unwrap();
let inner = crate::high_level_api::CompressedCompactPublicKey::new(&client_key.0);
*result_public_key = Box::into_raw(Box::new(CompressedCompactPublicKey(inner)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compressed_compact_public_key_decompress(
public_key: *const CompressedCompactPublicKey,
result_public_key: *mut *mut CompactPublicKey,
) -> c_int {
catch_panic(|| {
let public_key = get_ref_checked(public_key).unwrap();
*result_public_key = Box::into_raw(Box::new(CompactPublicKey(
public_key.0.clone().decompress(),
)));
})
}

View File

@@ -7,4 +7,6 @@ pub mod config;
pub mod integers;
pub mod keys;
#[cfg(feature = "integer")]
pub mod u128;
#[cfg(feature = "integer")]
pub mod u256;

View File

@@ -0,0 +1,20 @@
#[repr(C)]
#[derive(Copy, Clone)]
pub struct U128 {
pub w0: u64,
pub w1: u64,
}
impl From<u128> for U128 {
fn from(value: u128) -> Self {
let w0 = (value & (u64::MAX as u128)) as u64;
let w1 = (value >> 64) as u64;
Self { w0, w1 }
}
}
impl From<U128> for u128 {
fn from(value: U128) -> Self {
((value.w1 as u128) << 64u128) | value.w0 as u128
}
}

View File

@@ -1,47 +1,30 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
pub struct U256(pub(in crate::c_api) crate::integer::U256);
impl_destroy_on_type!(U256);
/// w0 is the least significant, w4 is the most significant
#[no_mangle]
pub unsafe extern "C" fn u256_from_u64_words(
w0: u64,
w1: u64,
w2: u64,
w3: u64,
result: *mut *mut U256,
) -> c_int {
catch_panic(|| {
let inner = crate::integer::U256::from((w0, w1, w2, w3));
*result = Box::into_raw(Box::new(U256(inner)));
})
#[repr(C)]
#[derive(Copy, Clone)]
pub struct U256 {
pub w0: u64,
pub w1: u64,
pub w2: u64,
pub w3: u64,
}
/// w0 is the least significant, w4 is the most significant
#[no_mangle]
pub unsafe extern "C" fn u256_to_u64_words(
input: *const U256,
w0: *mut u64,
w1: *mut u64,
w2: *mut u64,
w3: *mut u64,
) -> c_int {
catch_panic(|| {
let input = get_ref_checked(input).unwrap();
impl From<crate::integer::U256> for U256 {
fn from(value: crate::integer::U256) -> Self {
Self {
w0: value.0[0],
w1: value.0[1],
w2: value.0[2],
w3: value.0[3],
}
}
}
check_ptr_is_non_null_and_aligned(w0).unwrap();
check_ptr_is_non_null_and_aligned(w1).unwrap();
check_ptr_is_non_null_and_aligned(w2).unwrap();
check_ptr_is_non_null_and_aligned(w3).unwrap();
*w0 = input.0 .0[0];
*w1 = input.0 .0[1];
*w2 = input.0 .0[2];
*w3 = input.0 .0[3];
})
impl From<U256> for crate::integer::U256 {
fn from(value: U256) -> Self {
Self([value.w0, value.w1, value.w2, value.w3])
}
}
/// Creates a U256 from little endian bytes
@@ -51,7 +34,7 @@ pub unsafe extern "C" fn u256_to_u64_words(
pub unsafe extern "C" fn u256_from_little_endian_bytes(
input: *const u8,
len: usize,
result: *mut *mut U256,
result: *mut U256,
) -> c_int {
catch_panic(|| {
let mut inner = crate::integer::U256::default();
@@ -59,7 +42,7 @@ pub unsafe extern "C" fn u256_from_little_endian_bytes(
let input = std::slice::from_raw_parts(input, len);
inner.copy_from_le_byte_slice(input);
*result = Box::into_raw(Box::new(U256(inner)));
*result = U256::from(inner)
})
}
@@ -70,7 +53,7 @@ pub unsafe extern "C" fn u256_from_little_endian_bytes(
pub unsafe extern "C" fn u256_from_big_endian_bytes(
input: *const u8,
len: usize,
result: *mut *mut U256,
result: *mut U256,
) -> c_int {
catch_panic(|| {
let mut inner = crate::integer::U256::default();
@@ -78,38 +61,32 @@ pub unsafe extern "C" fn u256_from_big_endian_bytes(
let input = std::slice::from_raw_parts(input, len);
inner.copy_from_be_byte_slice(input);
*result = Box::into_raw(Box::new(U256(inner)));
*result = U256::from(inner)
})
}
/// len must be 32
#[no_mangle]
pub unsafe extern "C" fn u256_little_endian_bytes(
input: *const U256,
input: U256,
result: *mut u8,
len: usize,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let input = get_ref_checked(input).unwrap();
let bytes = std::slice::from_raw_parts_mut(result, len);
input.0.copy_to_le_byte_slice(bytes);
crate::integer::U256::from(input).copy_to_le_byte_slice(bytes);
})
}
/// len must be 32
#[no_mangle]
pub unsafe extern "C" fn u256_big_endian_bytes(
input: *const U256,
result: *mut u8,
len: usize,
) -> c_int {
pub unsafe extern "C" fn u256_big_endian_bytes(input: U256, result: *mut u8, len: usize) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
let input = get_ref_checked(input).unwrap();
let bytes = std::slice::from_raw_parts_mut(result, len);
input.0.copy_to_be_byte_slice(bytes);
crate::integer::U256::from(input).copy_to_be_byte_slice(bytes);
})
}

View File

@@ -59,6 +59,49 @@ macro_rules! impl_try_encrypt_with_public_key_on_type {
};
}
macro_rules! impl_try_encrypt_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>](
value: $input_type,
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 inner = <$wrapped_type>::try_encrypt(value, &public_key.0).unwrap();
*result = Box::into_raw(Box::new($wrapper_type(inner)));
})
}
}
};
}
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)));
})
}
}
};
}
macro_rules! impl_try_encrypt_trivial_on_type {
($wrapper_type:ty{$wrapped_type:ty}, $input_type:ty) => {
::paste::paste! {

View File

@@ -10,7 +10,7 @@ pub struct ShortintClientKey(pub(in crate::c_api) shortint::client_key::ClientKe
#[no_mangle]
pub unsafe extern "C" fn shortint_gen_client_key(
shortint_parameters: *const super::parameters::ShortintParameters,
shortint_parameters: super::parameters::ShortintPBSParameters,
result_client_key: *mut *mut ShortintClientKey,
) -> c_int {
catch_panic(|| {
@@ -20,9 +20,10 @@ pub unsafe extern "C" fn shortint_gen_client_key(
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
*result_client_key = std::ptr::null_mut();
let shortint_parameters = get_ref_checked(shortint_parameters).unwrap();
let shortint_parameters: crate::shortint::parameters::ClassicPBSParameters =
shortint_parameters.into();
let client_key = shortint::client_key::ClientKey::new(shortint_parameters.0.to_owned());
let client_key = shortint::client_key::ClientKey::new(shortint_parameters);
let heap_allocated_client_key = Box::new(ShortintClientKey(client_key));

View File

@@ -1,7 +1,6 @@
use crate::c_api::utils::*;
use std::os::raw::c_int;
use super::parameters::ShortintParameters;
use super::{
ShortintBivariatePBSLookupTable, ShortintCiphertext, ShortintClientKey,
ShortintCompressedCiphertext, ShortintCompressedPublicKey, ShortintCompressedServerKey,
@@ -57,17 +56,6 @@ pub unsafe extern "C" fn destroy_shortint_compressed_public_key(
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_parameters(
shortint_parameters: *mut ShortintParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(shortint_parameters).unwrap();
drop(Box::from_raw(shortint_parameters));
})
}
#[no_mangle]
pub unsafe extern "C" fn destroy_shortint_ciphertext(
shortint_ciphertext: *mut ShortintCiphertext,

View File

@@ -19,7 +19,7 @@ pub use server_key::{ShortintCompressedServerKey, ShortintServerKey};
#[no_mangle]
pub unsafe extern "C" fn shortint_gen_keys_with_parameters(
shortint_parameters: *const parameters::ShortintParameters,
shortint_parameters: parameters::ShortintPBSParameters,
result_client_key: *mut *mut ShortintClientKey,
result_server_key: *mut *mut ShortintServerKey,
) -> c_int {
@@ -32,9 +32,10 @@ pub unsafe extern "C" fn shortint_gen_keys_with_parameters(
*result_client_key = std::ptr::null_mut();
*result_server_key = std::ptr::null_mut();
let shortint_parameters = get_ref_checked(shortint_parameters).unwrap();
let shortint_parameters: crate::shortint::parameters::ClassicPBSParameters =
shortint_parameters.into();
let client_key = shortint::client_key::ClientKey::new(shortint_parameters.0);
let client_key = shortint::client_key::ClientKey::new(shortint_parameters);
let server_key = shortint::server_key::ServerKey::new(&client_key);
let heap_allocated_client_key = Box::new(ShortintClientKey(client_key));

View File

@@ -8,6 +8,7 @@ use std::os::raw::c_int;
use crate::shortint;
#[repr(C)]
#[derive(Copy, Clone)]
pub enum ShortintEncryptionKeyChoice {
ShortintEncryptionKeyChoiceBig,
ShortintEncryptionKeyChoiceSmall,
@@ -26,13 +27,185 @@ impl From<ShortintEncryptionKeyChoice> for crate::shortint::parameters::Encrypti
}
}
pub struct ShortintParameters(pub(in crate::c_api) shortint::parameters::ClassicPBSParameters);
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ShortintPBSParameters {
pub lwe_dimension: usize,
pub glwe_dimension: usize,
pub polynomial_size: usize,
pub lwe_modular_std_dev: f64,
pub glwe_modular_std_dev: f64,
pub pbs_base_log: usize,
pub pbs_level: usize,
pub ks_base_log: usize,
pub ks_level: usize,
pub message_modulus: usize,
pub carry_modulus: usize,
pub modulus_power_of_2_exponent: usize,
pub encryption_key_choice: ShortintEncryptionKeyChoice,
}
impl From<ShortintPBSParameters> for crate::shortint::ClassicPBSParameters {
fn from(c_params: ShortintPBSParameters) -> Self {
Self {
lwe_dimension: LweDimension(c_params.lwe_dimension),
glwe_dimension: GlweDimension(c_params.glwe_dimension),
polynomial_size: PolynomialSize(c_params.polynomial_size),
lwe_modular_std_dev: StandardDev(c_params.lwe_modular_std_dev),
glwe_modular_std_dev: StandardDev(c_params.glwe_modular_std_dev),
pbs_base_log: DecompositionBaseLog(c_params.pbs_base_log),
pbs_level: DecompositionLevelCount(c_params.pbs_level),
ks_base_log: DecompositionBaseLog(c_params.ks_base_log),
ks_level: DecompositionLevelCount(c_params.ks_level),
message_modulus: crate::shortint::parameters::MessageModulus(c_params.message_modulus),
carry_modulus: crate::shortint::parameters::CarryModulus(c_params.carry_modulus),
ciphertext_modulus: crate::shortint::parameters::CiphertextModulus::try_new_power_of_2(
c_params.modulus_power_of_2_exponent,
)
.unwrap(),
encryption_key_choice: c_params.encryption_key_choice.into(),
}
}
}
impl From<crate::shortint::ClassicPBSParameters> for ShortintPBSParameters {
fn from(rust_params: crate::shortint::ClassicPBSParameters) -> Self {
Self::convert(rust_params)
}
}
impl ShortintEncryptionKeyChoice {
// From::from cannot be marked as const, so we have to have
// our own function
const fn convert(rust_choice: crate::shortint::EncryptionKeyChoice) -> Self {
match rust_choice {
crate::shortint::EncryptionKeyChoice::Big => Self::ShortintEncryptionKeyChoiceBig,
crate::shortint::EncryptionKeyChoice::Small => Self::ShortintEncryptionKeyChoiceSmall,
}
}
}
const fn convert_modulus(rust_modulus: crate::shortint::CiphertextModulus) -> usize {
if rust_modulus.is_native_modulus() {
64 // shortints are on 64 bits
} else {
assert!(rust_modulus.is_power_of_two());
let modulus = rust_modulus.get_custom_modulus();
let exponent = modulus.ilog2() as usize;
assert!(exponent <= 64);
exponent
}
}
impl ShortintPBSParameters {
const fn convert(rust_params: crate::shortint::ClassicPBSParameters) -> Self {
Self {
lwe_dimension: rust_params.lwe_dimension.0,
glwe_dimension: rust_params.glwe_dimension.0,
polynomial_size: rust_params.polynomial_size.0,
lwe_modular_std_dev: rust_params.lwe_modular_std_dev.0,
glwe_modular_std_dev: rust_params.glwe_modular_std_dev.0,
pbs_base_log: rust_params.pbs_base_log.0,
pbs_level: rust_params.pbs_level.0,
ks_base_log: rust_params.ks_base_log.0,
ks_level: rust_params.ks_level.0,
message_modulus: rust_params.message_modulus.0,
carry_modulus: rust_params.carry_modulus.0,
modulus_power_of_2_exponent: convert_modulus(rust_params.ciphertext_modulus),
encryption_key_choice: ShortintEncryptionKeyChoice::convert(
rust_params.encryption_key_choice,
),
}
}
}
macro_rules! expose_as_shortint_pbs_parameters(
(
$(
$param_name:ident
),*
$(,)?
) => {
::paste::paste!{
$(
#[no_mangle]
pub static [<SHORTINT_ $param_name>]: ShortintPBSParameters =
ShortintPBSParameters::convert(crate::shortint::parameters::$param_name);
)*
}
// Test that converting a param from its rust struct
// to the c struct and then to the rust struct again
// yields the same values as the original struct
//
// This is what will essentially happen in the real code
#[test]
fn test_shortint_pbs_parameters_roundtrip_c_rust() {
$(
// 1 scope for each parameters
{
let rust_params = crate::shortint::parameters::$param_name;
let c_params = ShortintPBSParameters::from(rust_params);
let rust_params_from_c = crate::shortint::parameters::ClassicPBSParameters::from(c_params);
assert_eq!(rust_params, rust_params_from_c);
}
)*
}
}
);
expose_as_shortint_pbs_parameters!(
PARAM_MESSAGE_1_CARRY_0,
PARAM_MESSAGE_1_CARRY_1,
PARAM_MESSAGE_2_CARRY_0,
PARAM_MESSAGE_1_CARRY_2,
PARAM_MESSAGE_2_CARRY_1,
PARAM_MESSAGE_3_CARRY_0,
PARAM_MESSAGE_1_CARRY_3,
PARAM_MESSAGE_2_CARRY_2,
PARAM_MESSAGE_3_CARRY_1,
PARAM_MESSAGE_4_CARRY_0,
PARAM_MESSAGE_1_CARRY_4,
PARAM_MESSAGE_2_CARRY_3,
PARAM_MESSAGE_3_CARRY_2,
PARAM_MESSAGE_4_CARRY_1,
PARAM_MESSAGE_5_CARRY_0,
PARAM_MESSAGE_1_CARRY_5,
PARAM_MESSAGE_2_CARRY_4,
PARAM_MESSAGE_3_CARRY_3,
PARAM_MESSAGE_4_CARRY_2,
PARAM_MESSAGE_5_CARRY_1,
PARAM_MESSAGE_6_CARRY_0,
PARAM_MESSAGE_1_CARRY_6,
PARAM_MESSAGE_2_CARRY_5,
PARAM_MESSAGE_3_CARRY_4,
PARAM_MESSAGE_4_CARRY_3,
PARAM_MESSAGE_5_CARRY_2,
PARAM_MESSAGE_6_CARRY_1,
PARAM_MESSAGE_7_CARRY_0,
PARAM_MESSAGE_1_CARRY_7,
PARAM_MESSAGE_2_CARRY_6,
PARAM_MESSAGE_3_CARRY_5,
PARAM_MESSAGE_4_CARRY_4,
PARAM_MESSAGE_5_CARRY_3,
PARAM_MESSAGE_6_CARRY_2,
PARAM_MESSAGE_7_CARRY_1,
PARAM_MESSAGE_8_CARRY_0,
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK,
// Small params
PARAM_SMALL_MESSAGE_1_CARRY_1,
PARAM_SMALL_MESSAGE_2_CARRY_2,
PARAM_SMALL_MESSAGE_3_CARRY_3,
PARAM_SMALL_MESSAGE_4_CARRY_4,
PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK,
);
#[no_mangle]
pub unsafe extern "C" fn shortint_get_parameters(
message_bits: u32,
carry_bits: u32,
result: *mut *mut ShortintParameters,
result: *mut ShortintPBSParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
@@ -76,12 +249,8 @@ pub unsafe extern "C" fn shortint_get_parameters(
_ => None,
};
match params {
Some(params) => {
let params = Box::new(ShortintParameters(params));
*result = Box::into_raw(params);
}
None => *result = std::ptr::null_mut(),
if let Some(params) = params {
*result = params.into();
}
})
}
@@ -90,7 +259,7 @@ pub unsafe extern "C" fn shortint_get_parameters(
pub unsafe extern "C" fn shortint_get_parameters_small(
message_bits: u32,
carry_bits: u32,
result: *mut *mut ShortintParameters,
result: *mut ShortintPBSParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
@@ -102,62 +271,8 @@ pub unsafe extern "C" fn shortint_get_parameters_small(
_ => None,
};
match params {
Some(params) => {
let params = Box::new(ShortintParameters(params));
*result = Box::into_raw(params);
}
None => *result = std::ptr::null_mut(),
if let Some(params) = params {
*result = params.into();
}
})
}
#[no_mangle]
pub unsafe extern "C" fn shortint_create_parameters(
lwe_dimension: usize,
glwe_dimension: usize,
polynomial_size: usize,
lwe_modular_std_dev: f64,
glwe_modular_std_dev: f64,
pbs_base_log: usize,
pbs_level: usize,
ks_base_log: usize,
ks_level: usize,
message_modulus: usize,
carry_modulus: usize,
modulus_power_of_2_exponent: usize,
encryption_key_choice: ShortintEncryptionKeyChoice,
result: *mut *mut ShortintParameters,
) -> c_int {
catch_panic(|| {
check_ptr_is_non_null_and_aligned(result).unwrap();
// 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 heap_allocated_parameters = Box::new(ShortintParameters(
shortint::parameters::ClassicPBSParameters {
lwe_dimension: LweDimension(lwe_dimension),
glwe_dimension: GlweDimension(glwe_dimension),
polynomial_size: PolynomialSize(polynomial_size),
lwe_modular_std_dev: StandardDev(lwe_modular_std_dev),
glwe_modular_std_dev: StandardDev(glwe_modular_std_dev),
pbs_base_log: DecompositionBaseLog(pbs_base_log),
pbs_level: DecompositionLevelCount(pbs_level),
ks_base_log: DecompositionBaseLog(ks_base_log),
ks_level: DecompositionLevelCount(ks_level),
message_modulus: crate::shortint::parameters::MessageModulus(message_modulus),
carry_modulus: crate::shortint::parameters::CarryModulus(carry_modulus),
ciphertext_modulus:
crate::shortint::parameters::CiphertextModulus::try_new_power_of_2(
modulus_power_of_2_exponent,
)
.unwrap(),
encryption_key_choice: encryption_key_choice.into(),
},
));
*result = Box::into_raw(heap_allocated_parameters);
})
}

View File

@@ -0,0 +1,110 @@
//! Module with primitives pertaining to [`LweCompactCiphertextList`] expansion.
use crate::core_crypto::algorithms::polynomial_algorithms::polynomial_wrapping_monic_monomial_mul_assign;
use crate::core_crypto::commons::parameters::MonomialDegree;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use rayon::prelude::*;
/// Expand an [`LweCompactCiphertextList`] into an [`LweCiphertextList`].
///
/// Consider using [`par_expand_lwe_compact_ciphertext_list`] for better performance.
pub fn expand_lwe_compact_ciphertext_list<Scalar, InputCont, OutputCont>(
output_lwe_ciphertext_list: &mut LweCiphertextList<OutputCont>,
input_lwe_compact_ciphertext_list: &LweCompactCiphertextList<InputCont>,
) where
Scalar: UnsignedInteger,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
output_lwe_ciphertext_list.entity_count()
== input_lwe_compact_ciphertext_list.lwe_ciphertext_count().0
);
assert!(output_lwe_ciphertext_list.lwe_size() == input_lwe_compact_ciphertext_list.lwe_size());
let (input_mask_list, input_body_list) =
input_lwe_compact_ciphertext_list.get_mask_and_body_list();
let lwe_dimension = input_mask_list.lwe_dimension();
let max_ciphertext_per_bin = lwe_dimension.0;
for (input_mask, (mut output_ct_chunk, input_body_chunk)) in input_mask_list.iter().zip(
output_lwe_ciphertext_list
.chunks_mut(max_ciphertext_per_bin)
.zip(input_body_list.chunks(max_ciphertext_per_bin)),
) {
for (ct_idx, (mut out_ct, input_body)) in output_ct_chunk
.iter_mut()
.zip(input_body_chunk.iter())
.enumerate()
{
let (mut out_mask, out_body) = out_ct.get_mut_mask_and_body();
out_mask.as_mut().copy_from_slice(input_mask.as_ref());
let mut out_mask_as_polynomial = Polynomial::from_container(out_mask.as_mut());
// This the Psi_jl from the paper, it's equivalent to a multiplication in the X^N + 1
// ring for our choice of i == n
polynomial_wrapping_monic_monomial_mul_assign(
&mut out_mask_as_polynomial,
MonomialDegree(lwe_dimension.0 - (ct_idx + 1)),
);
*out_body.data = *input_body.data;
}
}
}
/// Parallel version of [`expand_lwe_compact_ciphertext_list`].
pub fn par_expand_lwe_compact_ciphertext_list<Scalar, InputCont, OutputCont>(
output_lwe_ciphertext_list: &mut LweCiphertextList<OutputCont>,
input_lwe_compact_ciphertext_list: &LweCompactCiphertextList<InputCont>,
) where
Scalar: UnsignedInteger + Send + Sync,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
{
assert!(
output_lwe_ciphertext_list.entity_count()
== input_lwe_compact_ciphertext_list.lwe_ciphertext_count().0
);
assert!(output_lwe_ciphertext_list.lwe_size() == input_lwe_compact_ciphertext_list.lwe_size());
let (input_mask_list, input_body_list) =
input_lwe_compact_ciphertext_list.get_mask_and_body_list();
let lwe_dimension = input_mask_list.lwe_dimension();
let max_ciphertext_per_bin = lwe_dimension.0;
input_mask_list
.par_iter()
.zip(
output_lwe_ciphertext_list
.par_chunks_mut(max_ciphertext_per_bin)
.zip(input_body_list.par_chunks(max_ciphertext_per_bin)),
)
.for_each(|(input_mask, (mut output_ct_chunk, input_body_chunk))| {
output_ct_chunk
.par_iter_mut()
.zip(input_body_chunk.par_iter())
.enumerate()
.for_each(|(ct_idx, (mut out_ct, input_body))| {
let (mut out_mask, out_body) = out_ct.get_mut_mask_and_body();
out_mask.as_mut().copy_from_slice(input_mask.as_ref());
let mut out_mask_as_polynomial = Polynomial::from_container(out_mask.as_mut());
// This is the Psi_jl from the paper, it's equivalent to a multiplication in the
// X^N + 1 ring for our choice of i == n
polynomial_wrapping_monic_monomial_mul_assign(
&mut out_mask_as_polynomial,
MonomialDegree(lwe_dimension.0 - (ct_idx + 1)),
);
*out_body.data = *input_body.data;
});
});
}

View File

@@ -0,0 +1,149 @@
//! Module containing primitives pertaining to [`LWE compact public key
//! generation`](`LweCompactPublicKey`).
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::ciphertext_modulus::CiphertextModulus;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
use slice_algorithms::*;
/// Fill an [`LWE compact public key`](`LweCompactPublicKey`) with an actual public key constructed
/// from a private [`LWE secret key`](`LweSecretKey`).
pub fn generate_lwe_compact_public_key<Scalar, InputKeyCont, OutputKeyCont, Gen>(
lwe_secret_key: &LweSecretKey<InputKeyCont>,
output: &mut LweCompactPublicKey<OutputKeyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.ciphertext_modulus().is_native_modulus(),
"This operation only supports native moduli"
);
assert!(
lwe_secret_key.lwe_dimension() == output.lwe_dimension(),
"Mismatched LweDimension between input LweSecretKey {:?} \
and ouptut LweCompactPublicKey {:?}",
lwe_secret_key.lwe_dimension(),
output.lwe_dimension()
);
let (mut mask, mut body) = output.get_mut_mask_and_body();
generator.fill_slice_with_random_mask(mask.as_mut());
slice_semi_reverse_negacyclic_convolution(
body.as_mut(),
mask.as_ref(),
lwe_secret_key.as_ref(),
);
generator
.unsigned_torus_slice_wrapping_add_random_noise_assign(body.as_mut(), noise_parameters);
}
/// Allocate a new [`LWE compact public key`](`LweCompactPublicKey`) and fill it with an actual
/// public key constructed from a private [`LWE secret key`](`LweSecretKey`).
///
/// See [`encrypt_lwe_ciphertext_with_compact_public_key`] for usage.
pub fn allocate_and_generate_new_lwe_compact_public_key<Scalar, InputKeyCont, Gen>(
lwe_secret_key: &LweSecretKey<InputKeyCont>,
noise_parameters: impl DispersionParameter,
ciphertext_modulus: CiphertextModulus<Scalar>,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LweCompactPublicKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut pk = LweCompactPublicKeyOwned::new(
Scalar::ZERO,
lwe_secret_key.lwe_dimension(),
ciphertext_modulus,
);
generate_lwe_compact_public_key(lwe_secret_key, &mut pk, noise_parameters, generator);
pk
}
/// Fill a [`seeded LWE compact public key`](`LweCompactPublicKey`) with an actual public key
/// constructed from a private [`LWE secret key`](`LweSecretKey`).
pub fn generate_seeded_lwe_compact_public_key<Scalar, InputKeyCont, OutputKeyCont, NoiseSeeder>(
lwe_secret_key: &LweSecretKey<InputKeyCont>,
output: &mut SeededLweCompactPublicKey<OutputKeyCont>,
noise_parameters: impl DispersionParameter,
noise_seeder: &mut NoiseSeeder,
) where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
OutputKeyCont: ContainerMut<Element = Scalar>,
// Maybe Sized allows to pass Box<dyn Seeder>.
NoiseSeeder: Seeder + ?Sized,
{
assert!(
output.ciphertext_modulus().is_native_modulus(),
"This operation only supports native moduli"
);
assert!(
lwe_secret_key.lwe_dimension() == output.lwe_dimension(),
"Mismatched LweDimension between input LweSecretKey {:?} \
and ouptut LweCompactPublicKey {:?}",
lwe_secret_key.lwe_dimension(),
output.lwe_dimension()
);
let mut generator = EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
output.compression_seed().seed,
noise_seeder,
);
let mut tmp_mask = vec![Scalar::ZERO; output.lwe_dimension().0];
generator.fill_slice_with_random_mask(tmp_mask.as_mut());
let mut body = output.get_mut_body();
slice_semi_reverse_negacyclic_convolution(
body.as_mut(),
tmp_mask.as_ref(),
lwe_secret_key.as_ref(),
);
generator
.unsigned_torus_slice_wrapping_add_random_noise_assign(body.as_mut(), noise_parameters);
}
/// Allocate a new [`seeded LWE compact public key`](`SeededLweCompactPublicKey`) and fill it with
/// an actual public key constructed from a private [`LWE secret key`](`LweSecretKey`).
pub fn allocate_and_generate_new_seeded_lwe_compact_public_key<Scalar, InputKeyCont, NoiseSeeder>(
lwe_secret_key: &LweSecretKey<InputKeyCont>,
noise_parameters: impl DispersionParameter,
ciphertext_modulus: CiphertextModulus<Scalar>,
noise_seeder: &mut NoiseSeeder,
) -> SeededLweCompactPublicKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
InputKeyCont: Container<Element = Scalar>,
// Maybe Sized allows to pass Box<dyn Seeder>.
NoiseSeeder: Seeder + ?Sized,
{
let mut pk = SeededLweCompactPublicKey::new(
Scalar::ZERO,
lwe_secret_key.lwe_dimension(),
noise_seeder.seed().into(),
ciphertext_modulus,
);
generate_seeded_lwe_compact_public_key(lwe_secret_key, &mut pk, noise_parameters, noise_seeder);
pk
}

View File

@@ -1483,3 +1483,791 @@ where
seeded_ct
}
/// Encrypt an input plaintext in an output [`LWE ciphertext`](`LweCiphertext`) using an
/// [`LWE compact public key`](`LweCompactPublicKey`). The ciphertext can be decrypted using the
/// [`LWE secret key`](`LweSecretKey`) that was used to generate the public key.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(2048);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// let lwe_compact_public_key = allocate_and_generate_new_lwe_compact_public_key(
/// &lwe_secret_key,
/// glwe_modular_std_dev,
/// ciphertext_modulus,
/// &mut encryption_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let plaintext = Plaintext(msg << 60);
///
/// // Create a new LweCiphertext
/// let mut lwe = LweCiphertext::new(0u64, lwe_dimension.to_lwe_size(), ciphertext_modulus);
///
/// encrypt_lwe_ciphertext_with_compact_public_key(
/// &lwe_compact_public_key,
/// &mut lwe,
/// plaintext,
/// glwe_modular_std_dev,
/// glwe_modular_std_dev,
/// &mut secret_generator,
/// &mut encryption_generator,
/// );
///
/// let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_secret_key, &lwe);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// let rounded = decomposer.closest_representable(decrypted_plaintext.0);
///
/// // Remove the encoding
/// let cleartext = rounded >> 60;
///
/// // Check we recovered the original message
/// assert_eq!(cleartext, msg);
/// ```
pub fn encrypt_lwe_ciphertext_with_compact_public_key<
Scalar,
KeyCont,
OutputCont,
SecretGen,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
mask_noise_parameters: impl DispersionParameter,
body_noise_parameters: impl DispersionParameter,
secret_generator: &mut SecretRandomGenerator<SecretGen>,
encryption_generator: &mut EncryptionRandomGenerator<EncryptionGen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
SecretGen: ByteRandomGenerator,
EncryptionGen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
"Mismatch between LweDimension of output cipertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_compact_public_key.lwe_dimension()
);
assert!(
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
"Mismatch between CiphertextModulus of output cipertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.ciphertext_modulus(),
lwe_compact_public_key.ciphertext_modulus()
);
assert!(
output.ciphertext_modulus().is_native_modulus(),
"This operation only supports native moduli"
);
let mut binary_random_vector = vec![Scalar::ZERO; lwe_compact_public_key.lwe_dimension().0];
secret_generator.fill_slice_with_random_uniform_binary(&mut binary_random_vector);
let (mut ct_mask, ct_body) = output.get_mut_mask_and_body();
let (pk_mask, pk_body) = lwe_compact_public_key.get_mask_and_body();
slice_semi_reverse_negacyclic_convolution(
ct_mask.as_mut(),
pk_mask.as_ref(),
&binary_random_vector,
);
// Noise from Chi_1 for the mask part of the encryption
encryption_generator.unsigned_torus_slice_wrapping_add_random_noise_assign(
ct_mask.as_mut(),
mask_noise_parameters,
);
*ct_body.data = slice_wrapping_dot_product(pk_body.as_ref(), &binary_random_vector);
// Noise from Chi_2 for the body part of the encryption
*ct_body.data =
(*ct_body.data).wrapping_add(encryption_generator.random_noise(body_noise_parameters));
*ct_body.data = (*ct_body.data).wrapping_add(encoded.0);
}
/// Encrypt an input plaintext list in an output [`LWE compact ciphertext
/// list`](`LweCompactCiphertextList`) using an [`LWE compact public key`](`LweCompactPublicKey`).
/// The expanded ciphertext list can be decrypted using the [`LWE secret key`](`LweSecretKey`) that
/// was used to generate the public key.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(2048);
/// let lwe_ciphertext_count = LweCiphertextCount(lwe_dimension.0 * 4);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// let lwe_compact_public_key = allocate_and_generate_new_lwe_compact_public_key(
/// &lwe_secret_key,
/// glwe_modular_std_dev,
/// ciphertext_modulus,
/// &mut encryption_generator,
/// );
///
/// let mut input_plaintext_list = PlaintextList::new(0u64, PlaintextCount(lwe_ciphertext_count.0));
/// input_plaintext_list
/// .iter_mut()
/// .enumerate()
/// .for_each(|(idx, x)| {
/// *x.0 = (idx as u64 % 16) << 60;
/// });
///
/// // Create a new LweCompactCiphertextList
/// let mut output_compact_ct_list = LweCompactCiphertextList::new(
/// 0u64,
/// lwe_dimension.to_lwe_size(),
/// lwe_ciphertext_count,
/// ciphertext_modulus,
/// );
///
/// encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
/// &lwe_compact_public_key,
/// &mut output_compact_ct_list,
/// &input_plaintext_list,
/// glwe_modular_std_dev,
/// glwe_modular_std_dev,
/// &mut secret_generator,
/// &mut encryption_generator,
/// );
///
/// let mut output_plaintext_list = input_plaintext_list.clone();
/// output_plaintext_list.as_mut().fill(0u64);
///
/// let lwe_ciphertext_list = output_compact_ct_list.expand_into_lwe_ciphertext_list();
///
/// decrypt_lwe_ciphertext_list(
/// &lwe_secret_key,
/// &lwe_ciphertext_list,
/// &mut output_plaintext_list,
/// );
///
/// let signed_decomposer =
/// SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// // Round the plaintexts
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
///
/// // Check we recovered the original messages
/// assert_eq!(input_plaintext_list, output_plaintext_list);
/// ```
pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
Scalar,
KeyCont,
InputCont,
OutputCont,
SecretGen,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
mask_noise_parameters: impl DispersionParameter,
body_noise_parameters: impl DispersionParameter,
secret_generator: &mut SecretRandomGenerator<SecretGen>,
encryption_generator: &mut EncryptionRandomGenerator<EncryptionGen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
SecretGen: ByteRandomGenerator,
EncryptionGen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
"Mismatch between LweDimension of output cipertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_compact_public_key.lwe_dimension()
);
assert!(
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
"Mismatch between CiphertextModulus of output cipertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.ciphertext_modulus(),
lwe_compact_public_key.ciphertext_modulus()
);
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between LweCiphertextCount of output cipertext and \
PlaintextCount of input list. Got {:?} in output, and {:?} in input plaintext list.",
output.lwe_ciphertext_count(),
encoded.plaintext_count()
);
assert!(
output.ciphertext_modulus().is_native_modulus(),
"This operation only supports native moduli"
);
let (mut output_mask_list, mut output_body_list) = output.get_mut_mask_and_body_list();
let (pk_mask, pk_body) = lwe_compact_public_key.get_mask_and_body();
let lwe_mask_count = output_mask_list.lwe_mask_count();
let lwe_dimension = output_mask_list.lwe_dimension();
let mut binary_random_vector = vec![Scalar::ZERO; output_mask_list.lwe_mask_list_size()];
secret_generator.fill_slice_with_random_uniform_binary(&mut binary_random_vector);
let max_ciphertext_per_bin = lwe_dimension.0;
let gen_iter = encryption_generator
.fork_lwe_compact_ciphertext_list_to_bin::<Scalar>(lwe_mask_count, lwe_dimension)
.expect("Failed to split generator into lwe compact ciphertext bins");
// Loop over the ciphertext "bins"
output_mask_list
.iter_mut()
.zip(
output_body_list
.chunks_mut(max_ciphertext_per_bin)
.zip(encoded.chunks(max_ciphertext_per_bin))
.zip(binary_random_vector.chunks(max_ciphertext_per_bin))
.zip(gen_iter),
)
.for_each(
|(
mut output_mask,
(
((mut output_body_chunk, input_plaintext_chunk), binary_random_slice),
mut loop_generator,
),
)| {
// output_body_chunk may not be able to fit the full convolution result so we create
// a temp buffer to compute the full convolution
let mut pk_body_convolved = vec![Scalar::ZERO; lwe_dimension.0];
slice_semi_reverse_negacyclic_convolution(
output_mask.as_mut(),
pk_mask.as_ref(),
binary_random_slice,
);
// Fill the temp buffer with b convolved with r
slice_semi_reverse_negacyclic_convolution(
pk_body_convolved.as_mut_slice(),
pk_body.as_ref(),
binary_random_slice,
);
// Noise from Chi_1 for the mask part of the encryption
loop_generator.unsigned_torus_slice_wrapping_add_random_noise_assign(
output_mask.as_mut(),
mask_noise_parameters,
);
// Fill the body chunk afterwards manually as it most likely will be smaller than
// the full convolution result. b convolved r + Delta * m + e2
// taking noise from Chi_2 for the body part of the encryption
output_body_chunk
.iter_mut()
.zip(pk_body_convolved.iter().zip(input_plaintext_chunk.iter()))
.for_each(|(dst, (&src, plaintext))| {
*dst.data = src
.wrapping_add(loop_generator.random_noise(body_noise_parameters))
.wrapping_add(*plaintext.0)
});
},
);
}
/// Parallel variant of [`encrypt_lwe_compact_ciphertext_list_with_compact_public_key`]. Encrypt an
/// input plaintext list in an output [`LWE compact ciphertext list`](`LweCompactCiphertextList`)
/// using an [`LWE compact public key`](`LweCompactPublicKey`). The expanded ciphertext list can be
/// decrypted using the [`LWE secret key`](`LweSecretKey`) that was used to generate the public key.
///
/// # Example
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCiphertext creation
/// let lwe_dimension = LweDimension(2048);
/// let lwe_ciphertext_count = LweCiphertextCount(lwe_dimension.0 * 4);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the LweSecretKey
/// let lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
///
/// let lwe_compact_public_key = allocate_and_generate_new_lwe_compact_public_key(
/// &lwe_secret_key,
/// glwe_modular_std_dev,
/// ciphertext_modulus,
/// &mut encryption_generator,
/// );
///
/// let mut input_plaintext_list = PlaintextList::new(0u64, PlaintextCount(lwe_ciphertext_count.0));
/// input_plaintext_list
/// .iter_mut()
/// .enumerate()
/// .for_each(|(idx, x)| {
/// *x.0 = (idx as u64 % 16) << 60;
/// });
///
/// // Create a new LweCompactCiphertextList
/// let mut output_compact_ct_list = LweCompactCiphertextList::new(
/// 0u64,
/// lwe_dimension.to_lwe_size(),
/// lwe_ciphertext_count,
/// ciphertext_modulus,
/// );
///
/// par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
/// &lwe_compact_public_key,
/// &mut output_compact_ct_list,
/// &input_plaintext_list,
/// glwe_modular_std_dev,
/// glwe_modular_std_dev,
/// &mut secret_generator,
/// &mut encryption_generator,
/// );
///
/// let mut output_plaintext_list = input_plaintext_list.clone();
/// output_plaintext_list.as_mut().fill(0u64);
///
/// let lwe_ciphertext_list = output_compact_ct_list.par_expand_into_lwe_ciphertext_list();
///
/// decrypt_lwe_ciphertext_list(
/// &lwe_secret_key,
/// &lwe_ciphertext_list,
/// &mut output_plaintext_list,
/// );
///
/// let signed_decomposer =
/// SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// // Round the plaintexts
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
///
/// // Check we recovered the original messages
/// assert_eq!(input_plaintext_list, output_plaintext_list);
/// ```
pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
Scalar,
KeyCont,
InputCont,
OutputCont,
SecretGen,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
mask_noise_parameters: impl DispersionParameter + Sync,
body_noise_parameters: impl DispersionParameter + Sync,
secret_generator: &mut SecretRandomGenerator<SecretGen>,
encryption_generator: &mut EncryptionRandomGenerator<EncryptionGen>,
) where
Scalar: UnsignedTorus + Sync + Send,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
SecretGen: ByteRandomGenerator,
EncryptionGen: ParallelByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
"Mismatch between LweDimension of output cipertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_compact_public_key.lwe_dimension()
);
assert!(
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
"Mismatch between CiphertextModulus of output cipertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.ciphertext_modulus(),
lwe_compact_public_key.ciphertext_modulus()
);
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between LweCiphertextCount of output cipertext and \
PlaintextCount of input list. Got {:?} in output, and {:?} in input plaintext list.",
output.lwe_ciphertext_count(),
encoded.plaintext_count()
);
assert!(
output.ciphertext_modulus().is_native_modulus(),
"This operation only supports native moduli"
);
let (mut output_mask_list, mut output_body_list) = output.get_mut_mask_and_body_list();
let (pk_mask, pk_body) = lwe_compact_public_key.get_mask_and_body();
let lwe_mask_count = output_mask_list.lwe_mask_count();
let lwe_dimension = output_mask_list.lwe_dimension();
let mut binary_random_vector = vec![Scalar::ZERO; output_mask_list.lwe_mask_list_size()];
secret_generator.fill_slice_with_random_uniform_binary(&mut binary_random_vector);
let max_ciphertext_per_bin = lwe_dimension.0;
let gen_iter = encryption_generator
.par_fork_lwe_compact_ciphertext_list_to_bin::<Scalar>(lwe_mask_count, lwe_dimension)
.expect("Failed to split generator into lwe compact ciphertext bins");
// Loop over the ciphertext "bins"
output_mask_list
.par_iter_mut()
.zip(
output_body_list
.par_chunks_mut(max_ciphertext_per_bin)
.zip(encoded.par_chunks(max_ciphertext_per_bin))
.zip(binary_random_vector.par_chunks(max_ciphertext_per_bin))
.zip(gen_iter),
)
.for_each(
|(
mut output_mask,
(
((mut output_body_chunk, input_plaintext_chunk), binary_random_slice),
mut loop_generator,
),
)| {
// output_body_chunk may not be able to fit the full convolution result so we create
// a temp buffer to compute the full convolution
let mut pk_body_convolved = vec![Scalar::ZERO; lwe_dimension.0];
rayon::join(
|| {
slice_semi_reverse_negacyclic_convolution(
output_mask.as_mut(),
pk_mask.as_ref(),
binary_random_slice,
);
},
|| {
// Fill the temp buffer with b convolved with r
slice_semi_reverse_negacyclic_convolution(
pk_body_convolved.as_mut_slice(),
pk_body.as_ref(),
binary_random_slice,
);
},
);
// Noise from Chi_1 for the mask part of the encryption
loop_generator.unsigned_torus_slice_wrapping_add_random_noise_assign(
output_mask.as_mut(),
mask_noise_parameters,
);
// Fill the body chunk afterwards manually as it most likely will be smaller than
// the full convolution result. b convolved r + Delta * m + e2
// taking noise from Chi_2 for the body part of the encryption
output_body_chunk
.iter_mut()
.zip(pk_body_convolved.iter().zip(input_plaintext_chunk.iter()))
.for_each(|(dst, (&src, plaintext))| {
*dst.data = src
.wrapping_add(loop_generator.random_noise(body_noise_parameters))
.wrapping_add(*plaintext.0)
});
},
);
}
#[cfg(test)]
mod test {
use crate::core_crypto::commons::test_tools;
use crate::core_crypto::prelude::*;
use crate::core_crypto::commons::generators::{
DeterministicSeeder, EncryptionRandomGenerator, SecretRandomGenerator,
};
use crate::core_crypto::commons::math::random::ActivatedRandomGenerator;
#[test]
fn test_compact_public_key_encryption() {
use rand::Rng;
let lwe_dimension = LweDimension(2048);
let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
let ciphertext_modulus = CiphertextModulus::new_native();
let mut secret_random_generator = test_tools::new_secret_random_generator();
let mut encryption_random_generator = test_tools::new_encryption_random_generator();
let mut thread_rng = rand::thread_rng();
for _ in 0..10_000 {
let lwe_sk =
LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_random_generator);
let mut compact_lwe_pk =
LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
generate_lwe_compact_public_key(
&lwe_sk,
&mut compact_lwe_pk,
glwe_modular_std_dev,
&mut encryption_random_generator,
);
let msg: u64 = thread_rng.gen();
let msg = msg % 16;
let plaintext = Plaintext(msg << 60);
let mut output_ct = LweCiphertext::new(
0u64,
lwe_dimension.to_lwe_size(),
CiphertextModulus::new_native(),
);
encrypt_lwe_ciphertext_with_compact_public_key(
&compact_lwe_pk,
&mut output_ct,
plaintext,
glwe_modular_std_dev,
glwe_modular_std_dev,
&mut secret_random_generator,
&mut encryption_random_generator,
);
let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_sk, &output_ct);
let signed_decomposer =
SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
let cleartext = signed_decomposer.closest_representable(decrypted_plaintext.0) >> 60;
assert_eq!(cleartext, msg);
}
}
#[test]
fn test_par_compact_lwe_list_public_key_encryption_equivalence() {
use rand::Rng;
let lwe_dimension = LweDimension(2048);
let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
let ciphertext_modulus = CiphertextModulus::new_native();
let mut thread_rng = rand::thread_rng();
for _ in 0..100 {
// We'll encrypt between 1 and 4 * lwe_dimension ciphertexts
let ct_count: usize = thread_rng.gen();
let ct_count = ct_count % (lwe_dimension.0 * 4) + 1;
let lwe_ciphertext_count = LweCiphertextCount(ct_count);
println!("{lwe_dimension:?} {ct_count:?}");
let seed = test_tools::random_seed();
let mut input_plaintext_list =
PlaintextList::new(0u64, PlaintextCount(lwe_ciphertext_count.0));
input_plaintext_list.iter_mut().for_each(|x| {
let msg: u64 = thread_rng.gen();
*x.0 = (msg % 16) << 60;
});
let par_lwe_ct_list = {
let mut deterministic_seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
let mut secret_random_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(
deterministic_seeder.seed(),
);
let mut encryption_random_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
deterministic_seeder.seed(),
&mut deterministic_seeder,
);
let lwe_sk =
LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_random_generator);
let mut compact_lwe_pk =
LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
generate_lwe_compact_public_key(
&lwe_sk,
&mut compact_lwe_pk,
glwe_modular_std_dev,
&mut encryption_random_generator,
);
let mut output_compact_ct_list = LweCompactCiphertextList::new(
0u64,
lwe_dimension.to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
&compact_lwe_pk,
&mut output_compact_ct_list,
&input_plaintext_list,
glwe_modular_std_dev,
glwe_modular_std_dev,
&mut secret_random_generator,
&mut encryption_random_generator,
);
let mut output_plaintext_list = input_plaintext_list.clone();
output_plaintext_list.as_mut().fill(0u64);
let lwe_ciphertext_list =
output_compact_ct_list.par_expand_into_lwe_ciphertext_list();
decrypt_lwe_ciphertext_list(
&lwe_sk,
&lwe_ciphertext_list,
&mut output_plaintext_list,
);
let signed_decomposer =
SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
output_plaintext_list
.iter_mut()
.for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
assert_eq!(input_plaintext_list, output_plaintext_list);
lwe_ciphertext_list
};
let ser_lwe_ct_list = {
let mut deterministic_seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
let mut secret_random_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(
deterministic_seeder.seed(),
);
let mut encryption_random_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
deterministic_seeder.seed(),
&mut deterministic_seeder,
);
let lwe_sk =
LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_random_generator);
let mut compact_lwe_pk =
LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
generate_lwe_compact_public_key(
&lwe_sk,
&mut compact_lwe_pk,
glwe_modular_std_dev,
&mut encryption_random_generator,
);
let mut output_compact_ct_list = LweCompactCiphertextList::new(
0u64,
lwe_dimension.to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
&compact_lwe_pk,
&mut output_compact_ct_list,
&input_plaintext_list,
glwe_modular_std_dev,
glwe_modular_std_dev,
&mut secret_random_generator,
&mut encryption_random_generator,
);
let mut output_plaintext_list = input_plaintext_list.clone();
output_plaintext_list.as_mut().fill(0u64);
let lwe_ciphertext_list = output_compact_ct_list.expand_into_lwe_ciphertext_list();
decrypt_lwe_ciphertext_list(
&lwe_sk,
&lwe_ciphertext_list,
&mut output_plaintext_list,
);
let signed_decomposer =
SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
output_plaintext_list
.iter_mut()
.for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
assert_eq!(input_plaintext_list, output_plaintext_list);
lwe_ciphertext_list
};
assert_eq!(ser_lwe_ct_list, par_lwe_ct_list);
}
}
}

View File

@@ -9,6 +9,8 @@ pub mod glwe_sample_extraction;
pub mod glwe_secret_key_generation;
pub mod lwe_bootstrap_key_conversion;
pub mod lwe_bootstrap_key_generation;
pub mod lwe_compact_ciphertext_list_expansion;
pub mod lwe_compact_public_key_generation;
pub mod lwe_encryption;
pub mod lwe_keyswitch;
pub mod lwe_keyswitch_key_generation;
@@ -31,6 +33,7 @@ pub mod seeded_glwe_ciphertext_list_decompression;
pub mod seeded_lwe_bootstrap_key_decompression;
pub mod seeded_lwe_ciphertext_decompression;
pub mod seeded_lwe_ciphertext_list_decompression;
pub mod seeded_lwe_compact_public_key_decompression;
pub mod seeded_lwe_keyswitch_key_decompression;
pub mod seeded_lwe_multi_bit_bootstrap_key_decompression;
pub mod seeded_lwe_public_key_decompression;
@@ -48,6 +51,8 @@ pub use glwe_sample_extraction::*;
pub use glwe_secret_key_generation::*;
pub use lwe_bootstrap_key_conversion::*;
pub use lwe_bootstrap_key_generation::*;
pub use lwe_compact_ciphertext_list_expansion::*;
pub use lwe_compact_public_key_generation::*;
pub use lwe_encryption::*;
pub use lwe_keyswitch::*;
pub use lwe_keyswitch_key_generation::*;
@@ -68,6 +73,7 @@ pub use seeded_glwe_ciphertext_list_decompression::*;
pub use seeded_lwe_bootstrap_key_decompression::*;
pub use seeded_lwe_ciphertext_decompression::*;
pub use seeded_lwe_ciphertext_list_decompression::*;
pub use seeded_lwe_compact_public_key_decompression::*;
pub use seeded_lwe_keyswitch_key_decompression::*;
pub use seeded_lwe_multi_bit_bootstrap_key_decompression::*;
pub use seeded_lwe_public_key_decompression::*;

View File

@@ -0,0 +1,49 @@
//! Module with primitives pertaining to [`SeededLweCompactPublicKey`] decompression.
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::math::random::RandomGenerator;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Convenience function to share the core logic of the decompression algorithm for
/// [`SeededLweCompactPublicKey`] between all functions needing it.
pub fn decompress_seeded_lwe_compact_public_key_with_existing_generator<
Scalar,
InputCont,
OutputCont,
Gen,
>(
output_cpk: &mut LweCompactPublicKey<OutputCont>,
input_seeded_cpk: &SeededLweCompactPublicKey<InputCont>,
generator: &mut RandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
decompress_seeded_glwe_ciphertext_with_existing_generator(
&mut output_cpk.as_mut_glwe_ciphertext(),
&input_seeded_cpk.as_seeded_glwe_ciphertext(),
generator,
);
}
/// Decompress a [`SeededLweCompactPublicKey`], without consuming it, into a standard
/// [`LweCompactPublicKey`].
pub fn decompress_seeded_lwe_compact_public_key<Scalar, InputCont, OutputCont, Gen>(
output_cpk: &mut LweCompactPublicKey<OutputCont>,
input_seeded_cpk: &SeededLweCompactPublicKey<InputCont>,
) where
Scalar: UnsignedTorus,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut generator = RandomGenerator::<Gen>::new(input_seeded_cpk.compression_seed().seed);
decompress_seeded_lwe_compact_public_key_with_existing_generator::<_, _, _, Gen>(
output_cpk,
input_seeded_cpk,
&mut generator,
)
}

View File

@@ -1,6 +1,8 @@
//! Module providing algorithms to perform computations on raw slices.
use crate::core_crypto::algorithms::polynomial_algorithms::polynomial_wrapping_add_mul_assign;
use crate::core_crypto::commons::numeric::UnsignedInteger;
use crate::core_crypto::entities::Polynomial;
/// Compute a dot product between two slices containing unsigned integers.
///
@@ -309,3 +311,54 @@ where
lhs.iter_mut()
.for_each(|lhs| *lhs = (*lhs).wrapping_div(rhs));
}
/// Primitive for compact LWE public key
///
/// Here $i$ from section 3 of <https://eprint.iacr.org/2023/603> is taken equal to $n$.
/// ```
/// use tfhe::core_crypto::algorithms::slice_algorithms::*;
/// let lhs = vec![1u8, 2u8, 3u8];
/// let rhs = vec![4u8, 5u8, 6u8];
/// let mut output = vec![0u8; 3];
/// slice_semi_reverse_negacyclic_convolution(&mut output, &lhs, &rhs);
/// assert_eq!(&output, &[(-17i8) as u8, 5, 32]);
/// ```
pub fn slice_semi_reverse_negacyclic_convolution<Scalar>(
output: &mut [Scalar],
lhs: &[Scalar],
rhs: &[Scalar],
) where
Scalar: UnsignedInteger,
{
assert!(
lhs.len() == rhs.len(),
"lhs (len: {}) and rhs (len: {}) must have the same length",
lhs.len(),
rhs.len()
);
assert!(
output.len() == lhs.len(),
"output (len: {}) and lhs (len: {}) must have the same length",
output.len(),
lhs.len()
);
// Apply phi_1 to the rhs term
let mut phi_1_rhs: Vec<_> = rhs.to_vec();
phi_1_rhs.reverse();
let phi_1_rhs_as_polynomial = Polynomial::from_container(phi_1_rhs.as_slice());
// Clear output as we'll add the multiplication result
output.fill(Scalar::ZERO);
let mut output_as_polynomial = Polynomial::from_container(output);
let lhs_as_polynomial = Polynomial::from_container(lhs);
// Apply the classic negacyclic convolution via polynomial mul in the X^N + 1 ring, with the
// phi_1 rhs it is equivalent to the operator we need
polynomial_wrapping_add_mul_assign(
&mut output_as_polynomial,
&lhs_as_polynomial,
&phi_1_rhs_as_polynomial,
);
}

View File

@@ -0,0 +1,83 @@
use super::*;
use crate::core_crypto::commons::generators::{
DeterministicSeeder, EncryptionRandomGenerator, SecretRandomGenerator,
};
use crate::core_crypto::commons::math::random::ActivatedRandomGenerator;
fn test_seeded_lwe_cpk_gen_equivalence<Scalar: UnsignedTorus>(
ciphertext_modulus: CiphertextModulus<Scalar>,
) {
// DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
// computations
// Define parameters for LweCompactPublicKey creation
let lwe_dimension = LweDimension(1024);
let lwe_modular_std_dev = StandardDev(0.00000004990272175010415);
// Create the PRNG
let mut seeder = new_seeder();
let seeder = seeder.as_mut();
let mask_seed = seeder.seed();
let deterministic_seeder_seed = seeder.seed();
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
const NB_TEST: usize = 10;
for _ in 0..NB_TEST {
// Create the LweSecretKey
let input_lwe_secret_key =
allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
let mut cpk = LweCompactPublicKey::new(Scalar::ZERO, lwe_dimension, ciphertext_modulus);
let mut deterministic_seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(deterministic_seeder_seed);
let mut encryption_generator = EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
mask_seed,
&mut deterministic_seeder,
);
generate_lwe_compact_public_key(
&input_lwe_secret_key,
&mut cpk,
lwe_modular_std_dev,
&mut encryption_generator,
);
assert!(check_content_respects_mod(&cpk, ciphertext_modulus));
let mut seeded_cpk = SeededLweCompactPublicKey::new(
Scalar::ZERO,
lwe_dimension,
mask_seed.into(),
ciphertext_modulus,
);
let mut deterministic_seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(deterministic_seeder_seed);
generate_seeded_lwe_compact_public_key(
&input_lwe_secret_key,
&mut seeded_cpk,
lwe_modular_std_dev,
&mut deterministic_seeder,
);
assert!(check_content_respects_mod(&seeded_cpk, ciphertext_modulus));
let decompressed_cpk = seeded_cpk.decompress_into_lwe_compact_public_key();
assert_eq!(cpk, decompressed_cpk);
}
}
#[test]
fn test_seeded_lwe_cpk_gen_equivalence_u32_native_mod() {
test_seeded_lwe_cpk_gen_equivalence::<u32>(CiphertextModulus::new_native())
}
#[test]
fn test_seeded_lwe_cpk_gen_equivalence_u64_naive_mod() {
test_seeded_lwe_cpk_gen_equivalence::<u64>(CiphertextModulus::new_native())
}

View File

@@ -827,3 +827,67 @@ fn test_u128_encryption() {
}
}
}
fn lwe_compact_public_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(
params: TestParams<Scalar>,
) {
let lwe_dimension = LweDimension(params.polynomial_size.0);
let glwe_modular_std_dev = params.glwe_modular_std_dev;
let ciphertext_modulus = params.ciphertext_modulus;
let message_modulus_log = params.message_modulus_log;
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
let mut rsc = TestResources::new();
const NB_TESTS: usize = 10;
let msg_modulus = Scalar::ONE.shl(message_modulus_log.0);
let mut msg = msg_modulus;
let delta: Scalar = encoding_with_padding / msg_modulus;
while msg != Scalar::ZERO {
msg = msg.wrapping_sub(Scalar::ONE);
for _ in 0..NB_TESTS {
let lwe_sk = allocate_and_generate_new_binary_lwe_secret_key(
lwe_dimension,
&mut rsc.secret_random_generator,
);
let pk = allocate_and_generate_new_lwe_compact_public_key(
&lwe_sk,
glwe_modular_std_dev,
ciphertext_modulus,
&mut rsc.encryption_random_generator,
);
let mut ct = LweCiphertext::new(
Scalar::ZERO,
lwe_dimension.to_lwe_size(),
ciphertext_modulus,
);
let plaintext = Plaintext(msg * delta);
encrypt_lwe_ciphertext_with_compact_public_key(
&pk,
&mut ct,
plaintext,
glwe_modular_std_dev,
glwe_modular_std_dev,
&mut rsc.secret_random_generator,
&mut rsc.encryption_random_generator,
);
assert!(check_content_respects_mod(&ct, ciphertext_modulus));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
assert_eq!(msg, decoded);
}
}
}
create_parametrized_test!(lwe_compact_public_encrypt_decrypt_custom_mod {
TEST_PARAMS_4_BITS_NATIVE_U64
});

View File

@@ -4,6 +4,7 @@ use paste::paste;
mod ggsw_encryption;
mod glwe_encryption;
mod lwe_bootstrap_key_generation;
mod lwe_compact_public_key_generation;
mod lwe_encryption;
mod lwe_keyswitch;
mod lwe_keyswitch_key_generation;

View File

@@ -9,7 +9,8 @@ use crate::core_crypto::commons::math::torus::UnsignedTorus;
use crate::core_crypto::commons::numeric::{CastInto, UnsignedInteger};
use crate::core_crypto::commons::parameters::{
CiphertextModulus, DecompositionLevelCount, FunctionalPackingKeyswitchKeyCount, GlweDimension,
GlweSize, LweBskGroupingFactor, LweCiphertextCount, LweDimension, LweSize, PolynomialSize,
GlweSize, LweBskGroupingFactor, LweCiphertextCount, LweDimension, LweMaskCount, LweSize,
PolynomialSize,
};
use concrete_csprng::generators::ForkError;
use rayon::prelude::*;
@@ -169,6 +170,16 @@ impl<G: ByteRandomGenerator> EncryptionRandomGenerator<G> {
self.try_fork(lwe_size.0, mask_bytes, noise_bytes)
}
pub(crate) fn fork_lwe_compact_ciphertext_list_to_bin<T: UnsignedInteger>(
&mut self,
lwe_mask_count: LweMaskCount,
lwe_dimension: LweDimension,
) -> Result<impl Iterator<Item = EncryptionRandomGenerator<G>>, ForkError> {
let mask_bytes = mask_bytes_per_lwe_compact_ciphertext_bin::<T>(lwe_dimension);
let noise_bytes = noise_bytes_per_lwe_compact_ciphertext_bin(lwe_dimension);
self.try_fork(lwe_mask_count.0, mask_bytes, noise_bytes)
}
// Forks both generators into an iterator
fn try_fork(
&mut self,
@@ -431,6 +442,16 @@ impl<G: ParallelByteRandomGenerator> EncryptionRandomGenerator<G> {
self.par_try_fork(lwe_size.0, mask_bytes, noise_bytes)
}
pub(crate) fn par_fork_lwe_compact_ciphertext_list_to_bin<T: UnsignedInteger>(
&mut self,
lwe_mask_count: LweMaskCount,
lwe_dimension: LweDimension,
) -> Result<impl IndexedParallelIterator<Item = EncryptionRandomGenerator<G>>, ForkError> {
let mask_bytes = mask_bytes_per_lwe_compact_ciphertext_bin::<T>(lwe_dimension);
let noise_bytes = noise_bytes_per_lwe_compact_ciphertext_bin(lwe_dimension);
self.par_try_fork(lwe_mask_count.0, mask_bytes, noise_bytes)
}
// Forks both generators into a parallel iterator.
fn par_try_fork(
&mut self,
@@ -504,6 +525,12 @@ fn mask_bytes_per_pfpksk<T: UnsignedInteger>(
lwe_size.0 * mask_bytes_per_pfpksk_chunk::<T>(level, glwe_size, poly_size)
}
fn mask_bytes_per_lwe_compact_ciphertext_bin<T: UnsignedInteger>(
lwe_dimension: LweDimension,
) -> usize {
lwe_dimension.0 * mask_bytes_per_coef::<T>()
}
fn noise_bytes_per_coef() -> usize {
// We use f64 to sample the noise for every precision, and we need 4/pi inputs to generate
// such an output (here we take 32 to keep a safety margin).
@@ -553,6 +580,10 @@ fn noise_bytes_per_pfpksk(
lwe_size.0 * noise_bytes_per_pfpksk_chunk(level, poly_size)
}
fn noise_bytes_per_lwe_compact_ciphertext_bin(lwe_dimension: LweDimension) -> usize {
lwe_dimension.0 * noise_bytes_per_coef()
}
#[cfg(test)]
mod test {
use crate::core_crypto::algorithms::*;

View File

@@ -79,6 +79,14 @@ impl LweDimension {
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub struct LwePublicKeyZeroEncryptionCount(pub usize);
/// The number of masks in a collection of LWE masks.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub struct LweMaskCount(pub usize);
/// The number of bodues in a collection of LWE bodies.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub struct LweBodyCount(pub usize);
/// The number of polynomials in a GLWE ciphertext, i.e. the number of polynomials in a GLWE mask
/// plus one.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Serialize, Deserialize)]

View File

@@ -12,7 +12,15 @@ type WrappingFunction<'data, Element, WrappingType> = fn(
),
) -> WrappingType;
type WrappingLendingIterator<'data, Element, WrappingType> = std::iter::Map<
type ChunksWrappingLendingIterator<'data, Element, WrappingType> = std::iter::Map<
std::iter::Zip<
std::slice::Chunks<'data, Element>,
itertools::RepeatN<<WrappingType as CreateFrom<&'data [Element]>>::Metadata>,
>,
WrappingFunction<'data, Element, WrappingType>,
>;
type ChunksExactWrappingLendingIterator<'data, Element, WrappingType> = std::iter::Map<
std::iter::Zip<
std::slice::ChunksExact<'data, Element>,
itertools::RepeatN<<WrappingType as CreateFrom<&'data [Element]>>::Metadata>,
@@ -20,7 +28,15 @@ type WrappingLendingIterator<'data, Element, WrappingType> = std::iter::Map<
WrappingFunction<'data, Element, WrappingType>,
>;
type ParallelWrappingLendingIterator<'data, Element, WrappingType> = rayon::iter::Map<
type ParallelChunksWrappingLendingIterator<'data, Element, WrappingType> = rayon::iter::Map<
rayon::iter::Zip<
rayon::slice::Chunks<'data, Element>,
rayon::iter::RepeatN<<WrappingType as CreateFrom<&'data [Element]>>::Metadata>,
>,
WrappingFunction<'data, Element, WrappingType>,
>;
type ParallelChunksExactWrappingLendingIterator<'data, Element, WrappingType> = rayon::iter::Map<
rayon::iter::Zip<
rayon::slice::ChunksExact<'data, Element>,
rayon::iter::RepeatN<<WrappingType as CreateFrom<&'data [Element]>>::Metadata>,
@@ -35,7 +51,15 @@ type WrappingFunctionMut<'data, Element, WrappingType> = fn(
),
) -> WrappingType;
type WrappingLendingIteratorMut<'data, Element, WrappingType> = std::iter::Map<
type ChunksWrappingLendingIteratorMut<'data, Element, WrappingType> = std::iter::Map<
std::iter::Zip<
std::slice::ChunksMut<'data, Element>,
itertools::RepeatN<<WrappingType as CreateFrom<&'data mut [Element]>>::Metadata>,
>,
WrappingFunctionMut<'data, Element, WrappingType>,
>;
type ChunksExactWrappingLendingIteratorMut<'data, Element, WrappingType> = std::iter::Map<
std::iter::Zip<
std::slice::ChunksExactMut<'data, Element>,
itertools::RepeatN<<WrappingType as CreateFrom<&'data mut [Element]>>::Metadata>,
@@ -43,7 +67,15 @@ type WrappingLendingIteratorMut<'data, Element, WrappingType> = std::iter::Map<
WrappingFunctionMut<'data, Element, WrappingType>,
>;
type ParallelWrappingLendingIteratorMut<'data, Element, WrappingType> = rayon::iter::Map<
type ParallelChunksWrappingLendingIteratorMut<'data, Element, WrappingType> = rayon::iter::Map<
rayon::iter::Zip<
rayon::slice::ChunksMut<'data, Element>,
rayon::iter::RepeatN<<WrappingType as CreateFrom<&'data mut [Element]>>::Metadata>,
>,
WrappingFunctionMut<'data, Element, WrappingType>,
>;
type ParallelChunksExactWrappingLendingIteratorMut<'data, Element, WrappingType> = rayon::iter::Map<
rayon::iter::Zip<
rayon::slice::ChunksExactMut<'data, Element>,
rayon::iter::RepeatN<<WrappingType as CreateFrom<&'data mut [Element]>>::Metadata>,
@@ -92,7 +124,7 @@ pub trait ContiguousEntityContainer: AsRef<[Self::Element]> {
/// Return an iterator borrowing immutably from the current contiguous container which returns
/// [`Self::EntityView`] entities.
fn iter(&self) -> WrappingLendingIterator<'_, Self::Element, Self::EntityView<'_>> {
fn iter(&self) -> ChunksExactWrappingLendingIterator<'_, Self::Element, Self::EntityView<'_>> {
let meta = self.get_entity_view_creation_metadata();
let entity_count = self.entity_count();
let entity_view_pod_size = self.get_entity_view_pod_size();
@@ -165,10 +197,26 @@ pub trait ContiguousEntityContainer: AsRef<[Self::Element]> {
}
}
fn chunks(
&self,
chunk_size: usize,
) -> ChunksWrappingLendingIterator<'_, Self::Element, Self::SelfView<'_>> {
let entity_count = self.entity_count();
let entity_view_pod_size = self.get_entity_view_pod_size();
let pod_chunk_size = entity_view_pod_size * chunk_size;
let meta = self.get_self_view_creation_metadata();
self.as_ref()
.chunks(pod_chunk_size)
.zip(itertools::repeat_n(meta, entity_count))
.map(|(elt, meta)| Self::SelfView::<'_>::create_from(elt, meta))
}
fn chunks_exact(
&self,
chunk_size: usize,
) -> WrappingLendingIterator<'_, Self::Element, Self::SelfView<'_>> {
) -> ChunksExactWrappingLendingIterator<'_, Self::Element, Self::SelfView<'_>> {
let entity_count = self.entity_count();
assert!(
entity_count % chunk_size == 0,
@@ -188,7 +236,7 @@ pub trait ContiguousEntityContainer: AsRef<[Self::Element]> {
fn par_iter<'this>(
&'this self,
) -> ParallelWrappingLendingIterator<'this, Self::Element, Self::EntityView<'this>>
) -> ParallelChunksExactWrappingLendingIterator<'this, Self::Element, Self::EntityView<'this>>
where
Self::Element: Sync,
Self::EntityView<'this>: Send,
@@ -202,6 +250,53 @@ pub trait ContiguousEntityContainer: AsRef<[Self::Element]> {
.zip(rayon::iter::repeatn(meta, entity_count))
.map(|(elt, meta)| Self::EntityView::<'this>::create_from(elt, meta))
}
fn par_chunks<'this>(
&'this self,
chunk_size: usize,
) -> ParallelChunksWrappingLendingIterator<'_, Self::Element, Self::SelfView<'_>>
where
Self::Element: Sync,
Self::SelfView<'this>: Send,
Self::SelfViewMetadata: Send,
{
let entity_count = self.entity_count();
let entity_view_pod_size = self.get_entity_view_pod_size();
let pod_chunk_size = entity_view_pod_size * chunk_size;
let meta = self.get_self_view_creation_metadata();
self.as_ref()
.par_chunks(pod_chunk_size)
.zip(rayon::iter::repeatn(meta, entity_count))
.map(|(elt, meta)| Self::SelfView::<'_>::create_from(elt, meta))
}
fn par_chunks_exact<'this>(
&'this self,
chunk_size: usize,
) -> ParallelChunksExactWrappingLendingIterator<'_, Self::Element, Self::SelfView<'_>>
where
Self::Element: Sync,
Self::SelfView<'this>: Send,
Self::SelfViewMetadata: Send,
{
let entity_count = self.entity_count();
assert!(
entity_count % chunk_size == 0,
"The current container has {entity_count} entities, which is not dividable by the \
requested chunk_size: {chunk_size}, preventing chunks_exact from returning an iterator."
);
let entity_view_pod_size = self.get_entity_view_pod_size();
let pod_chunk_size = entity_view_pod_size * chunk_size;
let meta = self.get_self_view_creation_metadata();
self.as_ref()
.par_chunks_exact(pod_chunk_size)
.zip(rayon::iter::repeatn(meta, entity_count))
.map(|(elt, meta)| Self::SelfView::<'_>::create_from(elt, meta))
}
}
pub trait ContiguousEntityContainerMut: ContiguousEntityContainer + AsMut<[Self::Element]> {
@@ -230,7 +325,7 @@ pub trait ContiguousEntityContainerMut: ContiguousEntityContainer + AsMut<[Self:
/// [`Self::EntityMutView`] entities.
fn iter_mut(
&mut self,
) -> WrappingLendingIteratorMut<'_, Self::Element, Self::EntityMutView<'_>> {
) -> ChunksExactWrappingLendingIteratorMut<'_, Self::Element, Self::EntityMutView<'_>> {
let meta = self.get_entity_view_creation_metadata();
let entity_count = self.entity_count();
let entity_view_pod_size = self.get_entity_view_pod_size();
@@ -300,10 +395,26 @@ pub trait ContiguousEntityContainerMut: ContiguousEntityContainer + AsMut<[Self:
}
}
fn chunks_mut(
&mut self,
chunk_size: usize,
) -> ChunksWrappingLendingIteratorMut<'_, Self::Element, Self::SelfMutView<'_>> {
let entity_count = self.entity_count();
let entity_view_pod_size = self.get_entity_view_pod_size();
let pod_chunk_size = entity_view_pod_size * chunk_size;
let meta = self.get_self_view_creation_metadata();
self.as_mut()
.chunks_mut(pod_chunk_size)
.zip(itertools::repeat_n(meta, entity_count))
.map(|(elt, meta)| Self::SelfMutView::<'_>::create_from(elt, meta))
}
fn chunks_exact_mut(
&mut self,
chunk_size: usize,
) -> WrappingLendingIteratorMut<'_, Self::Element, Self::SelfMutView<'_>> {
) -> ChunksExactWrappingLendingIteratorMut<'_, Self::Element, Self::SelfMutView<'_>> {
let entity_count = self.entity_count();
assert!(
entity_count % chunk_size == 0,
@@ -324,7 +435,11 @@ pub trait ContiguousEntityContainerMut: ContiguousEntityContainer + AsMut<[Self:
fn par_iter_mut<'this>(
&'this mut self,
) -> ParallelWrappingLendingIteratorMut<'this, Self::Element, Self::EntityMutView<'this>>
) -> ParallelChunksExactWrappingLendingIteratorMut<
'this,
Self::Element,
Self::EntityMutView<'this>,
>
where
Self::Element: Sync + Send,
Self::EntityMutView<'this>: Send,
@@ -338,4 +453,51 @@ pub trait ContiguousEntityContainerMut: ContiguousEntityContainer + AsMut<[Self:
.zip(rayon::iter::repeatn(meta, entity_count))
.map(|(elt, meta)| Self::EntityMutView::<'this>::create_from(elt, meta))
}
fn par_chunks_mut<'this>(
&'this mut self,
chunk_size: usize,
) -> ParallelChunksWrappingLendingIteratorMut<'_, Self::Element, Self::SelfMutView<'_>>
where
Self::Element: Sync + Send,
Self::SelfMutView<'this>: Send,
Self::SelfViewMetadata: Send,
{
let entity_count = self.entity_count();
let entity_view_pod_size = self.get_entity_view_pod_size();
let pod_chunk_size = entity_view_pod_size * chunk_size;
let meta = self.get_self_view_creation_metadata();
self.as_mut()
.par_chunks_mut(pod_chunk_size)
.zip(rayon::iter::repeatn(meta, entity_count))
.map(|(elt, meta)| Self::SelfMutView::<'_>::create_from(elt, meta))
}
fn par_chunks_exact_mut<'this>(
&'this mut self,
chunk_size: usize,
) -> ParallelChunksExactWrappingLendingIteratorMut<'_, Self::Element, Self::SelfMutView<'_>>
where
Self::Element: Sync + Send,
Self::SelfMutView<'this>: Send,
Self::SelfViewMetadata: Send,
{
let entity_count = self.entity_count();
assert!(
entity_count % chunk_size == 0,
"The current container has {entity_count} entities, which is not dividable by the \
requested chunk_size: {chunk_size}, preventing chunks_exact from returning an iterator."
);
let entity_view_pod_size = self.get_entity_view_pod_size();
let pod_chunk_size = entity_view_pod_size * chunk_size;
let meta = self.get_self_view_creation_metadata();
self.as_mut()
.par_chunks_exact_mut(pod_chunk_size)
.zip(rayon::iter::repeatn(meta, entity_count))
.map(|(elt, meta)| Self::SelfMutView::<'_>::create_from(elt, meta))
}
}

View File

@@ -278,7 +278,7 @@ pub fn glwe_ciphertext_mask_size(
///
/// **Remark:** Observe that the decryption is followed by a decoding phase that will contain a
/// rounding.
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct GlweCiphertext<C: Container>
where
C::Element: UnsignedInteger,

View File

@@ -22,6 +22,188 @@ pub struct LweBodyRefMut<'a, Scalar: UnsignedInteger> {
ciphertext_modulus: CiphertextModulus<Scalar>,
}
impl<Scalar: UnsignedInteger> LweBody<Scalar> {
pub fn new(data: Scalar, ciphertext_modulus: CiphertextModulus<Scalar>) -> Self {
Self {
data,
ciphertext_modulus,
}
}
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
self.ciphertext_modulus
}
}
impl<'outer, T: UnsignedInteger> LweBodyRef<'outer, T> {
pub fn new(data: &'outer T, ciphertext_modulus: CiphertextModulus<T>) -> Self {
Self {
data,
ciphertext_modulus,
}
}
pub fn ciphertext_modulus(&self) -> CiphertextModulus<T> {
self.ciphertext_modulus
}
}
impl<'outer, T: UnsignedInteger> LweBodyRefMut<'outer, T> {
pub fn new(data: &'outer mut T, ciphertext_modulus: CiphertextModulus<T>) -> Self {
Self {
data,
ciphertext_modulus,
}
}
pub fn ciphertext_modulus(&self) -> CiphertextModulus<T> {
self.ciphertext_modulus
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data [T]> for LweBodyRef<'data, T> {
type Metadata = LweBodyCreationMetadata<T>;
#[inline]
fn create_from(from: &[T], meta: Self::Metadata) -> LweBodyRef<T> {
let LweBodyCreationMetadata(ciphertext_modulus) = meta;
LweBodyRef {
data: &from[0],
ciphertext_modulus,
}
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data mut [T]> for LweBodyRefMut<'data, T> {
type Metadata = LweBodyCreationMetadata<T>;
#[inline]
fn create_from(from: &mut [T], meta: Self::Metadata) -> LweBodyRefMut<T> {
let LweBodyCreationMetadata(ciphertext_modulus) = meta;
LweBodyRefMut {
data: &mut from[0],
ciphertext_modulus,
}
}
}
#[derive(Clone, Debug)]
pub struct LweBodyList<C: Container>
where
C::Element: UnsignedInteger,
{
data: C,
ciphertext_modulus: CiphertextModulus<C::Element>,
}
pub type LweBodyListView<'data, Scalar> = LweBodyList<&'data [Scalar]>;
pub type LweBodyListMutView<'data, Scalar> = LweBodyList<&'data mut [Scalar]>;
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for LweBodyList<C> {
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for LweBodyList<C> {
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data [T]> for LweBodyListView<'data, T> {
type Metadata = LweBodyListCreationMetadata<T>;
#[inline]
fn create_from(from: &[T], meta: Self::Metadata) -> LweBodyListView<'_, T> {
let LweBodyListCreationMetadata(ciphertext_modulus) = meta;
LweBodyList {
data: from,
ciphertext_modulus,
}
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data mut [T]> for LweBodyListMutView<'data, T> {
type Metadata = LweBodyListCreationMetadata<T>;
#[inline]
fn create_from(from: &mut [T], meta: Self::Metadata) -> LweBodyListMutView<'_, T> {
let LweBodyListCreationMetadata(ciphertext_modulus) = meta;
LweBodyList {
data: from,
ciphertext_modulus,
}
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweBodyList<C> {
pub fn from_container(container: C, ciphertext_modulus: CiphertextModulus<Scalar>) -> Self {
Self {
data: container,
ciphertext_modulus,
}
}
pub fn lwe_body_count(&self) -> LweBodyCount {
LweBodyCount(self.data.container_len())
}
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
self.ciphertext_modulus
}
}
/// Metadata used in the [`CreateFrom`] implementation to create [`LweBody`] entities.
#[derive(Clone, Copy)]
pub struct LweBodyCreationMetadata<Scalar: UnsignedInteger>(pub CiphertextModulus<Scalar>);
/// Metadata used in the [`CreateFrom`] implementation to create [`LweBodyList`] entities.
#[derive(Clone, Copy)]
pub struct LweBodyListCreationMetadata<Scalar: UnsignedInteger>(pub CiphertextModulus<Scalar>);
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
for LweBodyList<C>
{
type Element = C::Element;
type EntityViewMetadata = LweBodyCreationMetadata<Self::Element>;
type EntityView<'this> = LweBodyRef<'this, Self::Element>
where
Self: 'this;
type SelfViewMetadata = LweBodyListCreationMetadata<Self::Element>;
type SelfView<'this> = LweBodyListView<'this,Self::Element>
where
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
LweBodyCreationMetadata(self.ciphertext_modulus())
}
fn get_entity_view_pod_size(&self) -> usize {
1
}
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
LweBodyListCreationMetadata(self.ciphertext_modulus())
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
for LweBodyList<C>
{
type EntityMutView<'this> = LweBodyRefMut<'this, Self::Element>
where
Self: 'this;
type SelfMutView<'this> = LweBodyListMutView<'this, Self::Element>
where
Self: 'this;
}
#[derive(Clone, Debug)]
pub struct LweMask<C: Container>
where
@@ -87,69 +269,178 @@ impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for LweMask<C>
}
}
impl<Scalar: UnsignedInteger> LweBody<Scalar> {
pub fn new(data: Scalar, ciphertext_modulus: CiphertextModulus<Scalar>) -> Self {
Self {
data,
impl<'data, T: UnsignedInteger> CreateFrom<&'data [T]> for LweMask<&'data [T]> {
type Metadata = LweMaskCreationMetadata<T>;
#[inline]
fn create_from(from: &[T], meta: Self::Metadata) -> LweMask<&[T]> {
let LweMaskCreationMetadata(ciphertext_modulus) = meta;
LweMask {
data: from,
ciphertext_modulus,
}
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data mut [T]> for LweMask<&'data mut [T]> {
type Metadata = LweMaskCreationMetadata<T>;
#[inline]
fn create_from(from: &mut [T], meta: Self::Metadata) -> LweMask<&mut [T]> {
let LweMaskCreationMetadata(ciphertext_modulus) = meta;
LweMask {
data: from,
ciphertext_modulus,
}
}
}
#[derive(Clone, Debug)]
pub struct LweMaskList<C: Container>
where
C::Element: UnsignedInteger,
{
data: C,
lwe_dimension: LweDimension,
ciphertext_modulus: CiphertextModulus<C::Element>,
}
pub type LweMaskListView<'data, Scalar> = LweMaskList<&'data [Scalar]>;
pub type LweMaskListMutView<'data, Scalar> = LweMaskList<&'data mut [Scalar]>;
pub fn lwe_mask_list_size(lwe_dimension: LweDimension, lwe_mask_count: LweMaskCount) -> usize {
lwe_dimension.0 * lwe_mask_count.0
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for LweMaskList<C> {
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for LweMaskList<C> {
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data [T]> for LweMaskListView<'data, T> {
type Metadata = LweMaskListCreationMetadata<T>;
#[inline]
fn create_from(from: &[T], meta: Self::Metadata) -> LweMaskListView<'_, T> {
let LweMaskListCreationMetadata(lwe_dimension, ciphertext_modulus) = meta;
LweMaskList {
data: from,
lwe_dimension,
ciphertext_modulus,
}
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data mut [T]> for LweMaskListMutView<'data, T> {
type Metadata = LweMaskListCreationMetadata<T>;
#[inline]
fn create_from(from: &mut [T], meta: Self::Metadata) -> LweMaskListMutView<'_, T> {
let LweMaskListCreationMetadata(lwe_dimension, ciphertext_modulus) = meta;
LweMaskList {
data: from,
lwe_dimension,
ciphertext_modulus,
}
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweMaskList<C> {
pub fn from_container(
container: C,
lwe_dimension: LweDimension,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> Self {
assert!(
container.container_len() % lwe_dimension.0 == 0,
"The provided container length is not valid. \
It needs to be dividable by lwe_dimension. \
Got container length: {} and lwe_dimension: {lwe_dimension:?}.",
container.container_len()
);
Self {
data: container,
lwe_dimension,
ciphertext_modulus,
}
}
pub fn lwe_dimension(&self) -> LweDimension {
self.lwe_dimension
}
pub fn lwe_mask_count(&self) -> LweMaskCount {
LweMaskCount(self.data.container_len() / self.lwe_dimension.0)
}
pub fn lwe_mask_list_size(&self) -> usize {
lwe_mask_list_size(self.lwe_dimension(), self.lwe_mask_count())
}
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
self.ciphertext_modulus
}
}
impl<'outer, T: UnsignedInteger> LweBodyRef<'outer, T> {
pub fn new(data: &'outer T, ciphertext_modulus: CiphertextModulus<T>) -> Self {
Self {
data,
ciphertext_modulus,
}
/// Metadata used in the [`CreateFrom`] implementation to create [`LweMask`] entities.
#[derive(Clone, Copy)]
pub struct LweMaskCreationMetadata<Scalar: UnsignedInteger>(pub CiphertextModulus<Scalar>);
/// Metadata used in the [`CreateFrom`] implementation to create [`LweMaskList`] entities.
#[derive(Clone, Copy)]
pub struct LweMaskListCreationMetadata<Scalar: UnsignedInteger>(
pub LweDimension,
pub CiphertextModulus<Scalar>,
);
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
for LweMaskList<C>
{
type Element = C::Element;
type EntityViewMetadata = LweMaskCreationMetadata<Self::Element>;
type EntityView<'this> = LweMask<&'this [ Self::Element]>
where
Self: 'this;
type SelfViewMetadata = LweMaskListCreationMetadata<Self::Element>;
type SelfView<'this> = LweMaskListView<'this, Self::Element>
where
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
LweMaskCreationMetadata(self.ciphertext_modulus())
}
pub fn ciphertext_modulus(&self) -> CiphertextModulus<T> {
self.ciphertext_modulus
fn get_entity_view_pod_size(&self) -> usize {
self.lwe_dimension().0
}
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
LweMaskListCreationMetadata(self.lwe_dimension(), self.ciphertext_modulus())
}
}
impl<'outer, T: UnsignedInteger> LweBodyRefMut<'outer, T> {
pub fn new(data: &'outer mut T, ciphertext_modulus: CiphertextModulus<T>) -> Self {
Self {
data,
ciphertext_modulus,
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
for LweMaskList<C>
{
type EntityMutView<'this> = LweMask<&'this mut [ Self::Element]>
where
Self: 'this;
pub fn ciphertext_modulus(&self) -> CiphertextModulus<T> {
self.ciphertext_modulus
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data [T]> for LweBodyRef<'data, T> {
type Metadata = LweCiphertextCreationMetadata<T>;
#[inline]
fn create_from(from: &[T], meta: Self::Metadata) -> LweBodyRef<T> {
let LweCiphertextCreationMetadata(ciphertext_modulus) = meta;
LweBodyRef {
data: &from[0],
ciphertext_modulus,
}
}
}
impl<'data, T: UnsignedInteger> CreateFrom<&'data mut [T]> for LweBodyRefMut<'data, T> {
type Metadata = LweCiphertextCreationMetadata<T>;
#[inline]
fn create_from(from: &mut [T], meta: Self::Metadata) -> LweBodyRefMut<T> {
let LweCiphertextCreationMetadata(ciphertext_modulus) = meta;
LweBodyRefMut {
data: &mut from[0],
ciphertext_modulus,
}
}
type SelfMutView<'this> = LweMaskListMutView<'this,Self::Element>
where
Self: 'this;
}
/// An [`LWE ciphertext`](`LweCiphertext`).

View File

@@ -0,0 +1,334 @@
//! Module containing the definition of the [`LweCompactCiphertextList`] a space efficient
//! encryption of a list of LWE ciphertexts.
use crate::core_crypto::algorithms::{
expand_lwe_compact_ciphertext_list, par_expand_lwe_compact_ciphertext_list,
};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`compact list of LWE ciphertexts`](`LweCompactCiphertextList`) obtained through encryption
/// with a [`compact LWE public key`](`super::LweCompactPublicKey`).
///
/// See section 4 of the public key construction described in <https://eprint.iacr.org/2023/603> by
/// M. Joye.
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct LweCompactCiphertextList<C: Container>
where
C::Element: UnsignedInteger,
{
data: C,
lwe_size: LweSize,
lwe_ciphertext_count: LweCiphertextCount,
ciphertext_modulus: CiphertextModulus<C::Element>,
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for LweCompactCiphertextList<C> {
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for LweCompactCiphertextList<C> {
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
pub fn lwe_compact_ciphertext_list_mask_count(
lwe_dimension: LweDimension,
lwe_ciphertext_count: LweCiphertextCount,
) -> LweMaskCount {
LweMaskCount(
lwe_ciphertext_count.0 / lwe_dimension.0
+ if lwe_ciphertext_count.0 % lwe_dimension.0 != 0 {
1
} else {
0
},
)
}
pub fn lwe_compact_ciphertext_list_size(
lwe_dimension: LweDimension,
lwe_ciphertext_count: LweCiphertextCount,
) -> usize {
// we expect one mask per "ciphertext bin" plus the bodies, so mask_count * lwe_dimension +
// ciphertext_count
let mask_count = lwe_compact_ciphertext_list_mask_count(lwe_dimension, lwe_ciphertext_count);
mask_count.0 * lwe_dimension.0 + lwe_ciphertext_count.0
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweCompactCiphertextList<C> {
/// Create an [`LweCompactCiphertextList`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to encrypt data
/// you need to use
/// [`crate::core_crypto::algorithms::encrypt_lwe_compact_ciphertext_list_with_compact_public_key`]
/// or its parallel variant
/// [`crate::core_crypto::algorithms::par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key`] using this list as
/// output.
///
/// This docstring exhibits [`LweCompactCiphertextList`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCompactCiphertextList creation
/// let lwe_size = LweSize(1025);
/// let lwe_ciphertext_count = LweCiphertextCount(5);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create a new LweCiphertextList
/// let lwe_compact_list =
/// LweCompactCiphertextList::new(0u64, lwe_size, lwe_ciphertext_count, ciphertext_modulus);
///
/// assert_eq!(lwe_compact_list.lwe_size(), lwe_size);
/// assert_eq!(
/// lwe_compact_list.lwe_ciphertext_count(),
/// lwe_ciphertext_count
/// );
/// assert_eq!(lwe_compact_list.ciphertext_modulus(), ciphertext_modulus);
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = lwe_compact_list.into_container();
///
/// // Recreate a list using from_container
/// let lwe_compact_list = LweCompactCiphertextList::from_container(
/// underlying_container,
/// lwe_size,
/// lwe_ciphertext_count,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(lwe_compact_list.lwe_size(), lwe_size);
/// assert_eq!(
/// lwe_compact_list.lwe_ciphertext_count(),
/// lwe_ciphertext_count
/// );
/// assert_eq!(lwe_compact_list.ciphertext_modulus(), ciphertext_modulus);
///
/// let lwe_list = lwe_compact_list.expand_into_lwe_ciphertext_list();
/// assert_eq!(lwe_list.lwe_size(), lwe_size);
/// assert_eq!(lwe_list.lwe_ciphertext_count(), lwe_ciphertext_count);
/// assert_eq!(lwe_list.ciphertext_modulus(), ciphertext_modulus);
/// ```
pub fn from_container(
container: C,
lwe_size: LweSize,
lwe_ciphertext_count: LweCiphertextCount,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> Self {
let expected_container_len =
lwe_compact_ciphertext_list_size(lwe_size.to_lwe_dimension(), lwe_ciphertext_count);
assert!(
container.container_len() == expected_container_len,
"Expected container for be of length {}, got length {}",
expected_container_len,
container.container_len()
);
Self {
data: container,
lwe_size,
lwe_ciphertext_count,
ciphertext_modulus,
}
}
/// Consume the entity and return its underlying container.
///
/// See [`LweCompactCiphertextList::from_container`] for usage.
pub fn into_container(self) -> C {
self.data
}
/// Return the [`LweSize`] of the [`LweCompactCiphertextList`].
///
/// See [`LweCompactCiphertextList::from_container`] for usage.
pub fn lwe_size(&self) -> LweSize {
self.lwe_size
}
/// Return the [`LweCiphertextCount`] of the [`LweCompactCiphertextList`].
///
/// See [`LweCompactCiphertextList::from_container`] for usage.
pub fn lwe_ciphertext_count(&self) -> LweCiphertextCount {
self.lwe_ciphertext_count
}
/// Return the [`CiphertextModulus`] of the [`LweCompactCiphertextList`].
///
/// See [`LweCompactCiphertextList::from_container`] for usage.
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
self.ciphertext_modulus
}
/// Return the [`LweMaskCount`] of the [`LweCompactCiphertextList`].
///
/// See [`LweCompactCiphertextList::from_container`] for usage.
pub fn lwe_mask_count(&self) -> LweMaskCount {
lwe_compact_ciphertext_list_mask_count(
self.lwe_size().to_lwe_dimension(),
self.lwe_ciphertext_count(),
)
}
/// Return an immutable view to the [`LweMaskList`] of a [`LweCompactCiphertextList`].
pub fn get_mask_list(&self) -> LweMaskListView<'_, Scalar> {
let lwe_mask_list_size =
lwe_mask_list_size(self.lwe_size().to_lwe_dimension(), self.lwe_mask_count());
LweMaskList::from_container(
&self.data.as_ref()[..lwe_mask_list_size],
self.lwe_size().to_lwe_dimension(),
self.ciphertext_modulus(),
)
}
/// Return an immutable view to the [`LweBodyList`] of a [`LweCompactCiphertextList`].
pub fn get_body_list(&self) -> LweBodyListView<'_, Scalar> {
let lwe_mask_list_size =
lwe_mask_list_size(self.lwe_size().to_lwe_dimension(), self.lwe_mask_count());
LweBodyList::from_container(
&self.data.as_ref()[lwe_mask_list_size..],
self.ciphertext_modulus(),
)
}
/// Return immutable views to the [`LweMaskList`] and [`LweBodyList`] of a
/// [`LweCompactCiphertextList`].
pub fn get_mask_and_body_list(
&self,
) -> (LweMaskListView<'_, Scalar>, LweBodyListView<'_, Scalar>) {
(self.get_mask_list(), self.get_body_list())
}
/// Consume the [`LweCompactCiphertextList`] and expand it into a standard
/// [`LweCiphertextList`].
///
/// See [`LweCompactCiphertextList::from_container`] for usage.
pub fn expand_into_lwe_ciphertext_list(self) -> LweCiphertextListOwned<Scalar> {
let mut lwe_ciphertext_list = LweCiphertextListOwned::new(
Scalar::ZERO,
self.lwe_size(),
self.lwe_ciphertext_count(),
self.ciphertext_modulus(),
);
expand_lwe_compact_ciphertext_list(&mut lwe_ciphertext_list, &self);
lwe_ciphertext_list
}
/// Parallel variant of [`Self::expand_into_lwe_ciphertext_list`]
pub fn par_expand_into_lwe_ciphertext_list(self) -> LweCiphertextListOwned<Scalar>
where
Scalar: UnsignedInteger + Send + Sync,
{
let mut lwe_ciphertext_list = LweCiphertextListOwned::new(
Scalar::ZERO,
self.lwe_size(),
self.lwe_ciphertext_count(),
self.ciphertext_modulus(),
);
par_expand_lwe_compact_ciphertext_list(&mut lwe_ciphertext_list, &self);
lwe_ciphertext_list
}
pub fn size_elements(&self) -> usize {
self.data.container_len()
}
pub fn size_bytes(&self) -> usize {
std::mem::size_of_val(self.data.as_ref())
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> LweCompactCiphertextList<C> {
/// Return a mutable view to the [`LweMaskList`] of a [`LweCompactCiphertextList`].
pub fn get_mut_mask_list(&mut self) -> LweMaskListMutView<'_, Scalar> {
let lwe_dimension = self.lwe_size().to_lwe_dimension();
let lwe_mask_count = self.lwe_mask_count();
let ciphertext_modulus = self.ciphertext_modulus();
let lwe_mask_list_size = lwe_mask_list_size(lwe_dimension, lwe_mask_count);
LweMaskList::from_container(
&mut self.data.as_mut()[..lwe_mask_list_size],
lwe_dimension,
ciphertext_modulus,
)
}
/// Return a mutable view to the [`LweBodyList`] of a [`LweCompactCiphertextList`].
pub fn get_mut_body_list(&mut self) -> LweBodyListMutView<'_, Scalar> {
let lwe_dimension = self.lwe_size().to_lwe_dimension();
let lwe_mask_count = self.lwe_mask_count();
let ciphertext_modulus = self.ciphertext_modulus();
let lwe_mask_list_size = lwe_mask_list_size(lwe_dimension, lwe_mask_count);
LweBodyList::from_container(
&mut self.data.as_mut()[lwe_mask_list_size..],
ciphertext_modulus,
)
}
/// Return mutable views to the [`LweMaskList`] and [`LweBodyList`] of a
/// [`LweCompactCiphertextList`].
pub fn get_mut_mask_and_body_list(
&mut self,
) -> (
LweMaskListMutView<'_, Scalar>,
LweBodyListMutView<'_, Scalar>,
) {
let lwe_dimension = self.lwe_size().to_lwe_dimension();
let lwe_mask_count = self.lwe_mask_count();
let ciphertext_modulus = self.ciphertext_modulus();
let lwe_mask_list_size = lwe_mask_list_size(lwe_dimension, lwe_mask_count);
let (mask_slice, body_slice) = self.as_mut().split_at_mut(lwe_mask_list_size);
(
LweMaskList::from_container(mask_slice, lwe_dimension, ciphertext_modulus),
LweBodyList::from_container(body_slice, ciphertext_modulus),
)
}
}
pub type LweCompactCiphertextListOwned<Scalar> = LweCompactCiphertextList<Vec<Scalar>>;
pub type LweCompactCiphertextListView<'data, Scalar> = LweCompactCiphertextList<&'data [Scalar]>;
pub type LweCompactCiphertextListMutView<'data, Scalar> =
LweCompactCiphertextList<&'data mut [Scalar]>;
impl<Scalar: UnsignedInteger> LweCompactCiphertextListOwned<Scalar> {
/// Allocate memory and create a new owned [`LweCompactCiphertextListOwned`].
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to encrypt data you need to use
/// [`crate::core_crypto::algorithms::encrypt_lwe_compact_ciphertext_list_with_compact_public_key`]
/// or its parallel variant
/// [`crate::core_crypto::algorithms::par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key`]
/// using this list as output.
///
/// See [`LweCompactCiphertextListOwned::from_container`] for usage.
pub fn new(
fill_with: Scalar,
lwe_size: LweSize,
lwe_ciphertext_count: LweCiphertextCount,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> Self {
Self::from_container(
vec![
fill_with;
lwe_compact_ciphertext_list_size(lwe_size.to_lwe_dimension(), lwe_ciphertext_count)
],
lwe_size,
lwe_ciphertext_count,
ciphertext_modulus,
)
}
}

View File

@@ -0,0 +1,187 @@
//! Module containing the definition of the [`LweCompactPublicKey`].
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`compact public LWE encryption key`](`LweCompactPublicKey`).
///
/// Implementation of the public key construction described in <https://eprint.iacr.org/2023/603> by
/// M. Joye.
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct LweCompactPublicKey<C: Container>
where
C::Element: UnsignedInteger,
{
glwe_ciphertext: GlweCiphertext<C>,
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for LweCompactPublicKey<C> {
fn as_ref(&self) -> &[T] {
self.glwe_ciphertext.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for LweCompactPublicKey<C> {
fn as_mut(&mut self) -> &mut [T] {
self.glwe_ciphertext.as_mut()
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweCompactPublicKey<C> {
/// Create an [`LweCompactPublicKey`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate an
/// [`LweCompactPublicKey`] you need to call
/// [`crate::core_crypto::algorithms::generate_lwe_compact_public_key`] using this key as
/// output.
///
/// This docstring exhibits [`LweCompactPublicKey`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for LweCompactPublicKey creation
/// let lwe_dimension = LweDimension(1024);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Create a new LweCompactPublicKey
/// let lwe_compact_public_key = LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
///
/// assert_eq!(lwe_compact_public_key.lwe_dimension(), lwe_dimension);
/// assert_eq!(
/// lwe_compact_public_key.ciphertext_modulus(),
/// ciphertext_modulus
/// );
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = lwe_compact_public_key.into_container();
///
/// // Recreate a public key using from_container
/// let lwe_compact_public_key =
/// LweCompactPublicKey::from_container(underlying_container, ciphertext_modulus);
///
/// assert_eq!(lwe_compact_public_key.lwe_dimension(), lwe_dimension);
/// assert_eq!(
/// lwe_compact_public_key.ciphertext_modulus(),
/// ciphertext_modulus
/// );
/// ```
pub fn from_container(
container: C,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> LweCompactPublicKey<C> {
assert!(
container.container_len().is_power_of_two(),
"LweCompactPublicKey container len must be a power of 2, got len = {}",
container.container_len()
);
let equivalent_polynomial_size = PolynomialSize(container.container_len() / 2);
LweCompactPublicKey {
glwe_ciphertext: GlweCiphertext::from_container(
container,
equivalent_polynomial_size,
ciphertext_modulus,
),
}
}
/// Consume the entity and return its underlying container.
///
/// See [`LweCompactPublicKey::from_container`] for usage.
pub fn into_container(self) -> C {
self.glwe_ciphertext.into_container()
}
/// Return the [`LweDimension`] of the [`LweCompactPublicKey`].
///
/// See [`LweCompactPublicKey::from_container`] for usage.
pub fn lwe_dimension(&self) -> LweDimension {
LweDimension(self.glwe_ciphertext.polynomial_size().0)
}
/// Return the [`CiphertextModulus`] of the [`LweCompactPublicKey`].
///
/// See [`LweCompactPublicKey::from_container`] for usage.
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
self.glwe_ciphertext.ciphertext_modulus()
}
/// Return an immutable view to the [`GlweMask`] of the underlying [`GlweCiphertext`] of the
/// [`LweCompactPublicKey`].
pub fn get_mask(&self) -> GlweMask<&[Scalar]> {
self.glwe_ciphertext.get_mask()
}
/// Return an immutable view to the [`GlweBody`] of the underlying [`GlweCiphertext`] of the
/// [`LweCompactPublicKey`].
pub fn get_body(&self) -> GlweBody<&[Scalar]> {
self.glwe_ciphertext.get_body()
}
/// Return immutable views to the [`GlweMask`] and [`GlweBody`] of the underlying
/// [`GlweCiphertext`] of the [`LweCompactPublicKey`].
pub fn get_mask_and_body(&self) -> (GlweMask<&[Scalar]>, GlweBody<&[Scalar]>) {
self.glwe_ciphertext.get_mask_and_body()
}
pub fn as_glwe_ciphertext(&self) -> GlweCiphertextView<'_, Scalar> {
self.glwe_ciphertext.as_view()
}
pub fn size_elements(&self) -> usize {
self.glwe_ciphertext.as_ref().len()
}
pub fn size_bytes(&self) -> usize {
std::mem::size_of_val(self.glwe_ciphertext.as_ref())
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> LweCompactPublicKey<C> {
/// Return a mutable view to the [`GlweMask`] of the underlying [`GlweCiphertext`] of the
/// [`LweCompactPublicKey`].
pub fn get_mut_mask(&mut self) -> GlweMask<&mut [Scalar]> {
self.glwe_ciphertext.get_mut_mask()
}
/// Return a mutable view to the [`GlweBody`] of the underlying [`GlweCiphertext`] of the
/// [`LweCompactPublicKey`].
pub fn get_mut_body(&mut self) -> GlweBody<&mut [Scalar]> {
self.glwe_ciphertext.get_mut_body()
}
/// Return mutable views to the [`GlweMask`] and [`GlweBody`] of the underlying
/// [`GlweCiphertext`] of the [`LweCompactPublicKey`].
pub fn get_mut_mask_and_body(&mut self) -> (GlweMask<&mut [Scalar]>, GlweBody<&mut [Scalar]>) {
self.glwe_ciphertext.get_mut_mask_and_body()
}
pub fn as_mut_glwe_ciphertext(&mut self) -> GlweCiphertextMutView<'_, Scalar> {
self.glwe_ciphertext.as_mut_view()
}
}
pub type LweCompactPublicKeyOwned<Scalar> = LweCompactPublicKey<Vec<Scalar>>;
impl<Scalar: UnsignedInteger> LweCompactPublicKeyOwned<Scalar> {
pub fn new(
fill_with: Scalar,
lwe_dimension: LweDimension,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> LweCompactPublicKeyOwned<Scalar> {
assert!(
lwe_dimension.0.is_power_of_two(),
"LweCompactPublicKey only supports power of 2 LweDimension. Got lwe_dimension = {}.",
lwe_dimension.0
);
LweCompactPublicKeyOwned::from_container(
vec![fill_with; 2 * lwe_dimension.0],
ciphertext_modulus,
)
}
}

View File

@@ -7,7 +7,7 @@ use crate::core_crypto::entities::*;
// An LwePublicKey is literally an LweCiphertextList, so we wrap an LweCiphertextList and use
// Deref to have access to all the primitives of the LweCiphertextList easily
/// A [`public LWE bootstrap key`](`LwePublicKey`).
/// A [`public LWE encryption key`](`LwePublicKey`).
///
/// This is a wrapper type of [`LweCiphertextList`], [`std::ops::Deref`] and [`std::ops::DerefMut`]
/// are implemented to dereference to the underlying [`LweCiphertextList`] for ease of use. See

View File

@@ -13,6 +13,8 @@ pub mod gsw_ciphertext;
pub mod lwe_bootstrap_key;
pub mod lwe_ciphertext;
pub mod lwe_ciphertext_list;
pub mod lwe_compact_ciphertext_list;
pub mod lwe_compact_public_key;
pub mod lwe_keyswitch_key;
pub mod lwe_multi_bit_bootstrap_key;
pub mod lwe_private_functional_packing_keyswitch_key;
@@ -30,6 +32,7 @@ pub mod seeded_glwe_ciphertext_list;
pub mod seeded_lwe_bootstrap_key;
pub mod seeded_lwe_ciphertext;
pub mod seeded_lwe_ciphertext_list;
pub mod seeded_lwe_compact_public_key;
pub mod seeded_lwe_keyswitch_key;
pub mod seeded_lwe_multi_bit_bootstrap_key;
pub mod seeded_lwe_public_key;
@@ -57,6 +60,8 @@ pub use gsw_ciphertext::*;
pub use lwe_bootstrap_key::*;
pub use lwe_ciphertext::*;
pub use lwe_ciphertext_list::*;
pub use lwe_compact_ciphertext_list::*;
pub use lwe_compact_public_key::*;
pub use lwe_keyswitch_key::*;
pub use lwe_multi_bit_bootstrap_key::*;
pub use lwe_private_functional_packing_keyswitch_key::*;
@@ -74,6 +79,7 @@ pub use seeded_glwe_ciphertext_list::*;
pub use seeded_lwe_bootstrap_key::*;
pub use seeded_lwe_ciphertext::*;
pub use seeded_lwe_ciphertext_list::*;
pub use seeded_lwe_compact_public_key::*;
pub use seeded_lwe_keyswitch_key::*;
pub use seeded_lwe_multi_bit_bootstrap_key::*;
pub use seeded_lwe_public_key::*;

View File

@@ -7,7 +7,7 @@ use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`seeded GLWE ciphertext`](`SeededGlweCiphertext`).
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct SeededGlweCiphertext<C: Container>
where
C::Element: UnsignedInteger,

View File

@@ -250,7 +250,7 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityCo
{
type Element = C::Element;
type EntityViewMetadata = LweCiphertextCreationMetadata<Self::Element>;
type EntityViewMetadata = LweBodyCreationMetadata<Self::Element>;
type EntityView<'this> = LweBodyRef<'this, Self::Element>
where
@@ -263,7 +263,7 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityCo
Self: 'this;
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
LweCiphertextCreationMetadata(self.ciphertext_modulus())
LweBodyCreationMetadata(self.ciphertext_modulus())
}
fn get_entity_view_pod_size(&self) -> usize {

View File

@@ -0,0 +1,214 @@
//! Module containing the definition of the [`SeededLweCompactPublicKey`].
use crate::core_crypto::algorithms::decompress_seeded_lwe_compact_public_key;
use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, CompressionSeed};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`seeded compact public LWE encryption key`](`SeededLweCompactPublicKey`).
///
/// Implementation of the public key construction described in <https://eprint.iacr.org/2023/603> by
/// M. Joye.
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct SeededLweCompactPublicKey<C: Container>
where
C::Element: UnsignedInteger,
{
seeded_glwe_ciphertext: SeededGlweCiphertext<C>,
}
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for SeededLweCompactPublicKey<C> {
fn as_ref(&self) -> &[T] {
self.seeded_glwe_ciphertext.as_ref()
}
}
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for SeededLweCompactPublicKey<C> {
fn as_mut(&mut self) -> &mut [T] {
self.seeded_glwe_ciphertext.as_mut()
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> SeededLweCompactPublicKey<C> {
/// Create an [`SeededLweCompactPublicKey`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate an
/// [`SeededLweCompactPublicKey`] you need to call
/// [`crate::core_crypto::algorithms::generate_seeded_lwe_compact_public_key`] using this key as
/// output.
///
/// This docstring exhibits [`SeededLweCompactPublicKey`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for SeededLweCompactPublicKey creation
/// let lwe_dimension = LweDimension(1024);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Get a seeder
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
///
/// // Create a new SeededLweCompactPublicKey
/// let seeded_lwe_compact_public_key = SeededLweCompactPublicKey::new(
/// 0u64,
/// lwe_dimension,
/// seeder.seed().into(),
/// ciphertext_modulus,
/// );
///
/// assert_eq!(seeded_lwe_compact_public_key.lwe_dimension(), lwe_dimension);
/// assert_eq!(
/// seeded_lwe_compact_public_key.ciphertext_modulus(),
/// ciphertext_modulus
/// );
///
/// let compression_seed = seeded_lwe_compact_public_key.compression_seed();
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = seeded_lwe_compact_public_key.into_container();
///
/// // Recreate a public key using from_container
/// let seeded_lwe_compact_public_key = SeededLweCompactPublicKey::from_container(
/// underlying_container,
/// compression_seed,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(seeded_lwe_compact_public_key.lwe_dimension(), lwe_dimension);
/// assert_eq!(
/// seeded_lwe_compact_public_key.ciphertext_modulus(),
/// ciphertext_modulus
/// );
///
/// let lwe_compact_public_key =
/// seeded_lwe_compact_public_key.decompress_into_lwe_compact_public_key();
///
/// assert_eq!(lwe_compact_public_key.lwe_dimension(), lwe_dimension);
/// assert_eq!(
/// lwe_compact_public_key.ciphertext_modulus(),
/// ciphertext_modulus
/// );
/// ```
pub fn from_container(
container: C,
compression_seed: CompressionSeed,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> SeededLweCompactPublicKey<C> {
assert!(
container.container_len().is_power_of_two(),
"SeededLweCompactPublicKey container len must be a power of 2, got len = {}",
container.container_len()
);
SeededLweCompactPublicKey {
seeded_glwe_ciphertext: SeededGlweCiphertext::from_container(
container,
GlweSize(1),
compression_seed,
ciphertext_modulus,
),
}
}
/// Consume the entity and return its underlying container.
///
/// See [`SeededLweCompactPublicKey::from_container`] for usage.
pub fn into_container(self) -> C {
self.seeded_glwe_ciphertext.into_container()
}
/// Return the [`LweDimension`] of the [`SeededLweCompactPublicKey`].
///
/// See [`SeededLweCompactPublicKey::from_container`] for usage.
pub fn lwe_dimension(&self) -> LweDimension {
LweDimension(self.seeded_glwe_ciphertext.polynomial_size().0)
}
/// Return the [`CompressionSeed`] of the [`SeededLweCompactPublicKey`].
///
/// See [`SeededLweCompactPublicKey::from_container`] for usage.
pub fn compression_seed(&self) -> CompressionSeed {
self.seeded_glwe_ciphertext.compression_seed()
}
/// Return the [`CiphertextModulus`] of the [`SeededLweCompactPublicKey`].
///
/// See [`SeededLweCompactPublicKey::from_container`] for usage.
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
self.seeded_glwe_ciphertext.ciphertext_modulus()
}
/// Return an immutable view to the [`GlweBody`] of the underlying [`GlweCiphertext`] of the
/// [`SeededLweCompactPublicKey`].
pub fn get_body(&self) -> GlweBody<&[Scalar]> {
self.seeded_glwe_ciphertext.get_body()
}
/// Return an immutable view to the the underlying [`SeededGlweCiphertext`] of the
/// [`SeededLweCompactPublicKey`].
pub fn as_seeded_glwe_ciphertext(&self) -> SeededGlweCiphertextView<'_, Scalar> {
self.seeded_glwe_ciphertext.as_view()
}
/// Consume the [`SeededLweCompactPublicKey`] and decompress it into a standard
/// [`LweCompactPublicKey`].
///
/// See [`SeededLweCompactPublicKey::from_container`] for usage.
pub fn decompress_into_lwe_compact_public_key(self) -> LweCompactPublicKeyOwned<Scalar>
where
Scalar: UnsignedTorus,
{
let mut decompressed_cpk = LweCompactPublicKey::new(
Scalar::ZERO,
self.lwe_dimension(),
self.ciphertext_modulus(),
);
decompress_seeded_lwe_compact_public_key::<_, _, _, ActivatedRandomGenerator>(
&mut decompressed_cpk,
&self,
);
decompressed_cpk
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> SeededLweCompactPublicKey<C> {
/// Return a mutable view to the [`GlweBody`] of the underlying [`SeededGlweCiphertext`] of the
/// [`SeededLweCompactPublicKey`].
pub fn get_mut_body(&mut self) -> GlweBody<&mut [Scalar]> {
self.seeded_glwe_ciphertext.get_mut_body()
}
/// Return a mutable view to the the underlying [`SeededGlweCiphertext`] of the
/// [`SeededLweCompactPublicKey`].
pub fn as_mut_seeded_glwe_ciphertext(&mut self) -> SeededGlweCiphertextMutView<'_, Scalar> {
self.seeded_glwe_ciphertext.as_mut_view()
}
}
pub type SeededLweCompactPublicKeyOwned<Scalar> = SeededLweCompactPublicKey<Vec<Scalar>>;
impl<Scalar: UnsignedInteger> SeededLweCompactPublicKeyOwned<Scalar> {
pub fn new(
fill_with: Scalar,
lwe_dimension: LweDimension,
compression_seed: CompressionSeed,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> SeededLweCompactPublicKeyOwned<Scalar> {
assert!(
lwe_dimension.0.is_power_of_two(),
"SeededLweCompactPublicKey only supports power of 2 LweDimension. Got lwe_dimension = {}.",
lwe_dimension.0
);
SeededLweCompactPublicKeyOwned::from_container(
vec![fill_with; lwe_dimension.0],
compression_seed,
ciphertext_modulus,
)
}
}

View File

@@ -1,5 +1,10 @@
use crate::boolean::client_key::ClientKey;
use std::marker::PhantomData;
use crate::boolean::client_key::ClientKey;
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
use concrete_csprng::seeders::Seed;
use serde::{Deserialize, Serialize};
use super::parameters::BooleanParameterSet;
@@ -16,6 +21,19 @@ where
_marker: std::marker::PhantomData<P>,
}
impl GenericBoolClientKey<StaticBoolParameters> {
pub(crate) fn with_seed(parameters: FheBoolParameters, seed: Seed) -> Self {
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
let key = crate::boolean::engine::BooleanEngine::new_from_seeder(&mut seeder)
.create_client_key(parameters.into());
Self {
key,
_marker: PhantomData,
}
}
}
impl From<FheBoolParameters> for GenericBoolClientKey<StaticBoolParameters> {
fn from(parameters: FheBoolParameters) -> Self {
Self {

View File

@@ -127,7 +127,7 @@ impl ConfigBuilder {
}
#[cfg(feature = "integer")]
pub fn enable_default_custom_integers(
pub fn enable_custom_integers(
mut self,
block_parameters: crate::shortint::ClassicPBSParameters,
wopbs_block_parameters: Option<crate::shortint::WopbsParameters>,

View File

@@ -60,6 +60,24 @@ macro_rules! define_key_structs {
)*
}
impl [<$base_struct_name ClientKey>] {
pub(crate) fn with_seed(
config: [<$base_struct_name Config>],
seed: ::concrete_csprng::seeders::Seed
) -> Self {
Self {
$(
[<$name _params>]: None,
[<$name _key>]: config
.[<$name _params>]
.map(|params| {
<[<$base_ty_name ClientKey>]>::with_seed(params, seed)
}),
)*
}
}
}
impl From<[<$base_struct_name Config>]> for [<$base_struct_name ClientKey>] {
fn from(config: [<$base_struct_name Config>]) -> Self {
Self {

View File

@@ -1,5 +1,14 @@
use concrete_csprng::seeders::Seed;
use serde::{Deserialize, Serialize};
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
use crate::integer::U256;
use crate::shortint::EncryptionKeyChoice;
use super::server_key::RadixCiphertextDyn;
use super::types::compact::CompactCiphertextListDyn;
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct IntegerConfig {
pub(crate) block_parameters: Option<crate::shortint::ClassicPBSParameters>,
@@ -61,6 +70,26 @@ pub(crate) struct IntegerClientKey {
}
impl IntegerClientKey {
pub(crate) fn with_seed(config: IntegerConfig, seed: Seed) -> Self {
let (key, encryption_type) = match config.block_parameters {
Some(params) => {
let encryption_type = params.encryption_key_choice;
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
let cks = crate::shortint::engine::ShortintEngine::new_from_seeder(&mut seeder)
.new_client_key(params.into())
.unwrap();
let cks = crate::integer::ClientKey::from(cks);
(Some(cks), encryption_type)
}
None => (None, EncryptionKeyChoice::Big),
};
Self {
key,
wopbs_block_parameters: config.wopbs_block_parameters,
encryption_type,
}
}
pub(crate) fn encryption_type(&self) -> EncryptionKeyChoice {
self.encryption_type
}
@@ -171,3 +200,133 @@ impl IntegerCompressedServerKey {
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(in crate::high_level_api) enum CompactPublicKeyDyn {
Big(crate::integer::CompactPublicKeyBig),
Small(crate::integer::CompactPublicKeySmall),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(in crate::high_level_api) struct IntegerCompactPublicKey {
pub(in crate::high_level_api) key: Option<CompactPublicKeyDyn>,
}
impl IntegerCompactPublicKey {
pub(in crate::high_level_api) fn new(client_key: &IntegerClientKey) -> Self {
Self::try_new(client_key).expect("Incompatible parameters")
}
pub(in crate::high_level_api) fn try_new(client_key: &IntegerClientKey) -> Option<Self> {
let Some(cks) = client_key.key.as_ref() else {
return Some(Self {
key: None,
});
};
let key = match client_key.encryption_type {
crate::shortint::EncryptionKeyChoice::Big => {
CompactPublicKeyDyn::Big(crate::integer::CompactPublicKeyBig::try_new(cks)?)
}
crate::shortint::EncryptionKeyChoice::Small => {
CompactPublicKeyDyn::Small(crate::integer::CompactPublicKeySmall::try_new(cks)?)
}
};
Some(Self { key: Some(key) })
}
pub(in crate::high_level_api) fn try_encrypt<T>(
&self,
value: T,
num_blocks: usize,
) -> Option<RadixCiphertextDyn>
where
T: Into<U256>,
{
let Some(key) = self.key.as_ref() else {
return None;
};
let value = value.into();
let ct = match key {
CompactPublicKeyDyn::Big(pk) => {
RadixCiphertextDyn::Big(pk.encrypt_radix(value, num_blocks))
}
CompactPublicKeyDyn::Small(pk) => {
RadixCiphertextDyn::Small(pk.encrypt_radix(value, num_blocks))
}
};
Some(ct)
}
pub(in crate::high_level_api::integers) fn try_encrypt_compact<T>(
&self,
values: &[T],
num_blocks: usize,
) -> Option<CompactCiphertextListDyn>
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,
{
let Some(key) = self.key.as_ref() else {
return None;
};
let ct = match key {
CompactPublicKeyDyn::Big(pk) => {
CompactCiphertextListDyn::Big(pk.encrypt_slice_radix_compact(values, num_blocks))
}
CompactPublicKeyDyn::Small(pk) => {
CompactCiphertextListDyn::Small(pk.encrypt_slice_radix_compact(values, num_blocks))
}
};
Some(ct)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(in crate::high_level_api) enum CompressedCompactPublicKeyDyn {
Big(crate::integer::CompressedCompactPublicKeyBig),
Small(crate::integer::CompressedCompactPublicKeySmall),
}
impl CompressedCompactPublicKeyDyn {
fn decompress(self) -> CompactPublicKeyDyn {
match self {
CompressedCompactPublicKeyDyn::Big(big) => CompactPublicKeyDyn::Big(big.decompress()),
CompressedCompactPublicKeyDyn::Small(small) => {
CompactPublicKeyDyn::Small(small.decompress())
}
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(in crate::high_level_api) struct IntegerCompressedCompactPublicKey {
pub(in crate::high_level_api) key: Option<CompressedCompactPublicKeyDyn>,
}
impl IntegerCompressedCompactPublicKey {
pub(in crate::high_level_api) fn new(client_key: &IntegerClientKey) -> Self {
let Some(cks) = client_key.key.as_ref() else {
return Self {
key: None,
};
};
let key = match client_key.encryption_type {
crate::shortint::EncryptionKeyChoice::Big => CompressedCompactPublicKeyDyn::Big(
crate::integer::CompressedCompactPublicKeyBig::new(cks),
),
crate::shortint::EncryptionKeyChoice::Small => CompressedCompactPublicKeyDyn::Small(
crate::integer::CompressedCompactPublicKeySmall::new(cks),
),
};
Self { key: Some(key) }
}
pub(in crate::high_level_api) fn decompress(self) -> IntegerCompactPublicKey {
IntegerCompactPublicKey {
key: self.key.map(CompressedCompactPublicKeyDyn::decompress),
}
}
}

View File

@@ -1,12 +1,13 @@
pub use types::{
CompressedFheUint10, CompressedFheUint12, CompressedFheUint128, CompressedFheUint14,
CompressedFheUint16, CompressedFheUint256, CompressedFheUint32, CompressedFheUint64,
CompressedFheUint8, FheUint10, FheUint12, FheUint128, FheUint14, FheUint16, FheUint256,
FheUint32, FheUint64, FheUint8, GenericInteger,
};
expand_pub_use_fhe_type!(
pub use types{
FheUint8, FheUint10, FheUint12, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128,
FheUint256
};
);
pub(in crate::high_level_api) use keys::{
IntegerClientKey, IntegerCompressedServerKey, IntegerConfig, IntegerServerKey,
IntegerClientKey, IntegerCompactPublicKey, IntegerCompressedCompactPublicKey,
IntegerCompressedServerKey, IntegerConfig, IntegerServerKey,
};
pub(in crate::high_level_api) use public_key::compressed::CompressedPublicKeyDyn;
pub(in crate::high_level_api) use public_key::PublicKeyDyn;

View File

@@ -1,9 +1,12 @@
use rand::Rng;
use crate::high_level_api::prelude::*;
use crate::high_level_api::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
use crate::integer::U256;
use crate::{
CompressedFheUint16, CompressedFheUint256, CompressedPublicKey, FheUint128, FheUint16,
FheUint256, FheUint32, FheUint64,
CompactFheUint32, CompactFheUint32List, CompactPublicKey, CompressedFheUint16,
CompressedFheUint256, CompressedPublicKey, Config, FheUint128, FheUint16, FheUint256,
FheUint32, FheUint64,
};
#[test]
@@ -223,6 +226,98 @@ fn test_decompressed_public_key_encrypt() {
assert_eq!(clear, 255u8);
}
#[test]
fn test_compact_public_key_big() {
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK,
None,
)
.build();
let (client_key, _) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
let a = FheUint8::try_encrypt(255u8, &public_key).unwrap();
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 255u8);
}
#[test]
fn test_compact_public_key_list_big() {
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK,
None,
)
.build();
test_compact_public_key_list(config);
}
#[test]
fn test_compact_public_key_list_small() {
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
crate::shortint::parameters::PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK,
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::all_disabled()
.enable_custom_integers(
crate::shortint::parameters::PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK,
None,
)
.build();
let (client_key, _) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
let a = FheUint8::try_encrypt(255u8, &public_key).unwrap();
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 255u8);
}
#[test]
fn test_trivial_fhe_uint8() {
let config = ConfigBuilder::all_disabled()

View File

@@ -26,6 +26,8 @@ use crate::high_level_api::traits::{
FheBootstrap, FheDecrypt, FheEq, FheOrd, FheTrivialEncrypt, FheTryEncrypt, FheTryTrivialEncrypt,
};
use crate::high_level_api::{ClientKey, PublicKey};
use crate::integer::U256;
use crate::CompactPublicKey;
/// A Generic FHE unsigned integer
///
@@ -212,6 +214,25 @@ where
}
}
impl<P, T> FheTryEncrypt<T, CompactPublicKey> for GenericInteger<P>
where
T: Into<U256>,
P: IntegerParameter,
P::Id: Default + TypeIdentifier,
{
type Error = crate::high_level_api::errors::Error;
fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let id = P::Id::default();
let ciphertext = key
.integer_key
.try_encrypt(value, P::num_blocks())
.ok_or(UninitializedPublicKey(id.type_variant()))
.unwrap_display();
Ok(Self::new(ciphertext, id))
}
}
impl<P, T> FheTryTrivialEncrypt<T> for GenericInteger<P>
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,

View File

@@ -0,0 +1,119 @@
use crate::errors::{UninitializedPublicKey, UnwrapResultExt};
use crate::high_level_api::integers::parameters::IntegerParameter;
use crate::high_level_api::integers::server_key::RadixCiphertextDyn;
use crate::high_level_api::integers::types::base::GenericInteger;
use crate::high_level_api::internal_traits::TypeIdentifier;
use crate::high_level_api::traits::FheTryEncrypt;
use crate::integer::ciphertext::{CompactCiphertextListBig, CompactCiphertextListSmall};
use crate::CompactPublicKey;
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub(in crate::high_level_api::integers) enum CompactCiphertextListDyn {
Big(CompactCiphertextListBig),
Small(CompactCiphertextListSmall),
}
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct GenericCompactInteger<P: IntegerParameter> {
pub(in crate::high_level_api::integers) list: CompactCiphertextListDyn,
pub(in crate::high_level_api::integers) id: P::Id,
}
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct GenericCompactIntegerList<P: IntegerParameter> {
pub(in crate::high_level_api::integers) list: CompactCiphertextListDyn,
pub(in crate::high_level_api::integers) id: P::Id,
}
impl<P> GenericCompactInteger<P>
where
P: IntegerParameter,
{
pub fn expand(&self) -> GenericInteger<P> {
match &self.list {
CompactCiphertextListDyn::Big(list) => {
let expanded = RadixCiphertextDyn::Big(list.expand_one());
GenericInteger::new(expanded, self.id)
}
CompactCiphertextListDyn::Small(list) => {
let expanded = RadixCiphertextDyn::Small(list.expand_one());
GenericInteger::new(expanded, self.id)
}
}
}
}
impl<P> GenericCompactIntegerList<P>
where
P: IntegerParameter,
{
pub fn len(&self) -> usize {
match &self.list {
CompactCiphertextListDyn::Big(list) => list.ciphertext_count(),
CompactCiphertextListDyn::Small(list) => list.ciphertext_count(),
}
}
pub fn expand(&self) -> Vec<GenericInteger<P>> {
match &self.list {
CompactCiphertextListDyn::Big(list) => list
.expand()
.into_iter()
.map(RadixCiphertextDyn::Big)
.map(|ct| GenericInteger::new(ct, self.id))
.collect::<Vec<_>>(),
CompactCiphertextListDyn::Small(list) => list
.expand()
.into_iter()
.map(RadixCiphertextDyn::Small)
.map(|ct| GenericInteger::new(ct, self.id))
.collect::<Vec<_>>(),
}
}
}
impl<P, T> FheTryEncrypt<T, CompactPublicKey> for GenericCompactInteger<P>
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,
P: IntegerParameter,
P::Id: Default + TypeIdentifier,
{
type Error = crate::high_level_api::errors::Error;
fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let id = P::Id::default();
let ciphertext = key
.integer_key
.try_encrypt_compact(&[value], P::num_blocks())
.ok_or(UninitializedPublicKey(id.type_variant()))
.unwrap_display();
Ok(Self {
list: ciphertext,
id,
})
}
}
impl<'a, P, T> FheTryEncrypt<&'a [T], CompactPublicKey> for GenericCompactIntegerList<P>
where
T: crate::integer::block_decomposition::DecomposableInto<u64>,
P: IntegerParameter,
P::Id: Default + TypeIdentifier,
{
type Error = crate::high_level_api::errors::Error;
fn try_encrypt(values: &'a [T], key: &CompactPublicKey) -> Result<Self, Self::Error> {
let id = P::Id::default();
let ciphertext = key
.integer_key
.try_encrypt_compact(values, P::num_blocks())
.ok_or(UninitializedPublicKey(id.type_variant()))
.unwrap_display();
Ok(Self {
list: ciphertext,
id,
})
}
}

View File

@@ -1,11 +1,13 @@
pub use base::GenericInteger;
pub use static_::{
CompressedFheUint10, CompressedFheUint12, CompressedFheUint128, CompressedFheUint14,
CompressedFheUint16, CompressedFheUint256, CompressedFheUint32, CompressedFheUint64,
CompressedFheUint8, FheUint10, FheUint12, FheUint128, FheUint14, FheUint16, FheUint256,
FheUint32, FheUint64, FheUint8,
};
expand_pub_use_fhe_type!(
pub use static_{
FheUint8, FheUint10, FheUint12, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128,
FheUint256
};
);
pub(super) mod base;
pub(super) mod compact;
pub(super) mod compressed;
pub(super) mod static_;

View File

@@ -2,6 +2,9 @@ use serde::{Deserialize, Serialize};
use super::base::GenericInteger;
use crate::high_level_api::integers::parameters::{EvaluationIntegerKey, IntegerParameter};
use crate::high_level_api::integers::types::compact::{
GenericCompactInteger, GenericCompactIntegerList,
};
use crate::high_level_api::integers::types::compressed::CompressedGenericInteger;
use crate::high_level_api::internal_traits::{ParameterType, TypeIdentifier};
#[cfg(feature = "internal-keycache")]
@@ -67,6 +70,11 @@ macro_rules! static_int_type {
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compressed $name>] = CompressedGenericInteger<[<$name Parameters>]>;
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact $name>] = GenericCompactInteger<[<$name Parameters>]>;
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact $name List>] = GenericCompactIntegerList<[<$name Parameters>]>;
impl $crate::high_level_api::keys::RefKeyFromKeyChain for [<FheUint $num_bits Id>] {
type Key = crate::integer::ClientKey;

View File

@@ -2,6 +2,8 @@
//!
//! - [ClientKey] aggregates the keys used to encrypt/decrypt between normal and homomorphic types.
use concrete_csprng::seeders::Seed;
#[cfg(feature = "boolean")]
use crate::high_level_api::booleans::BooleanClientKey;
use crate::high_level_api::config::Config;
@@ -44,6 +46,19 @@ impl ClientKey {
}
}
pub fn generate_with_seed<C: Into<Config>>(config: C, seed: Seed) -> ClientKey {
#[allow(unused_variables)]
let config: Config = config.into();
ClientKey {
#[cfg(feature = "boolean")]
boolean_key: BooleanClientKey::with_seed(config.boolean_config, seed),
#[cfg(feature = "shortint")]
shortint_key: ShortIntClientKey::with_seed(config.shortint_config, seed),
#[cfg(feature = "integer")]
integer_key: IntegerClientKey::with_seed(config.integer_config, seed),
}
}
/// Generates a new ServerKey
///
/// The `ServerKey` generated is meant to be used to initialize the global state

View File

@@ -7,7 +7,8 @@ mod server;
use crate::high_level_api::config::Config;
pub use client::{ClientKey, RefKeyFromKeyChain};
pub use public::{
CompressedPublicKey, PublicKey, RefKeyFromCompressedPublicKeyChain, RefKeyFromPublicKeyChain,
CompactPublicKey, CompressedCompactPublicKey, CompressedPublicKey, PublicKey,
RefKeyFromCompressedPublicKeyChain, RefKeyFromPublicKeyChain,
};
pub use server::{CompressedServerKey, ServerKey};

View File

@@ -5,6 +5,8 @@
#[cfg(feature = "boolean")]
use crate::high_level_api::booleans::{BooleanCompressedPublicKey, BooleanPublicKey};
use crate::high_level_api::errors::{UninitializedPublicKey, UnwrapResultExt};
#[cfg(feature = "integer")]
use crate::high_level_api::integers::{IntegerCompactPublicKey, IntegerCompressedCompactPublicKey};
#[cfg(feature = "shortint")]
use crate::high_level_api::shortints::{ShortIntCompressedPublicKey, ShortIntPublicKey};
@@ -189,3 +191,76 @@ macro_rules! impl_ref_key_from_compressed_public_keychain {
}
}
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct CompactPublicKey {
#[cfg(feature = "integer")]
pub(in crate::high_level_api) integer_key: IntegerCompactPublicKey,
}
impl CompactPublicKey {
/// Creates a CompactPublicKey
///
/// Compared to the [PublicKey], this one is much smaller
/// however it supports less parameters.
///
/// # Panic
///
/// This will panic if parameters are not compatible
pub fn new(client_key: &ClientKey) -> Self {
#[cfg(feature = "integer")]
{
Self {
integer_key: IntegerCompactPublicKey::new(&client_key.integer_key),
}
}
#[cfg(not(feature = "integer"))]
{
let _ = client_key;
Self {}
}
}
pub fn try_new(client_key: &ClientKey) -> Option<Self> {
#[cfg(feature = "integer")]
{
Some(Self {
integer_key: IntegerCompactPublicKey::try_new(&client_key.integer_key)?,
})
}
#[cfg(not(feature = "integer"))]
{
let _ = client_key;
Some(Self {})
}
}
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct CompressedCompactPublicKey {
#[cfg(feature = "integer")]
pub(in crate::high_level_api) integer_key: IntegerCompressedCompactPublicKey,
}
impl CompressedCompactPublicKey {
pub fn new(client_key: &ClientKey) -> Self {
#[cfg(feature = "integer")]
{
Self {
integer_key: IntegerCompressedCompactPublicKey::new(&client_key.integer_key),
}
}
#[cfg(not(feature = "integer"))]
{
let _ = client_key;
Self {}
}
}
pub fn decompress(self) -> CompactPublicKey {
CompactPublicKey {
#[cfg(feature = "integer")]
integer_key: self.integer_key.decompress(),
}
}
}

View File

@@ -1,9 +1,32 @@
#![allow(unused_doc_comments)]
#[allow(unused)]
macro_rules! expand_pub_use_fhe_type(
(
pub use $module_path:path { $($fhe_type_name:ident),* $(,)? };
)=> {
::paste::paste! {
pub use $module_path::{
$(
$fhe_type_name,
[<Compressed $fhe_type_name>],
[<Compact $fhe_type_name>],
[<Compact $fhe_type_name List>],
)*
};
}
}
);
pub use crate::core_crypto::commons::math::random::Seed;
pub use config::{Config, ConfigBuilder};
pub use errors::{Error, OutOfRangeError};
pub use global_state::{set_server_key, unset_server_key, with_server_key_as_context};
pub use keys::{
generate_keys, ClientKey, CompressedPublicKey, CompressedServerKey, PublicKey, ServerKey,
generate_keys, ClientKey, CompactPublicKey, CompressedCompactPublicKey, CompressedPublicKey,
CompressedServerKey, PublicKey, ServerKey,
};
#[cfg(test)]
@@ -12,12 +35,12 @@ mod tests;
#[cfg(feature = "boolean")]
pub use crate::high_level_api::booleans::{CompressedFheBool, FheBool, FheBoolParameters};
#[cfg(feature = "integer")]
pub use crate::high_level_api::integers::{
CompressedFheUint10, CompressedFheUint12, CompressedFheUint128, CompressedFheUint14,
CompressedFheUint16, CompressedFheUint256, CompressedFheUint32, CompressedFheUint64,
CompressedFheUint8, FheUint10, FheUint12, FheUint128, FheUint14, FheUint16, FheUint256,
FheUint32, FheUint64, FheUint8, GenericInteger,
};
expand_pub_use_fhe_type!(
pub use crate::high_level_api::integers{
FheUint8, FheUint10, FheUint12, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128,
FheUint256
};
);
#[cfg(feature = "shortint")]
pub use crate::high_level_api::shortints::{
CompressedFheUint2, CompressedFheUint3, CompressedFheUint4, FheUint2, FheUint2Parameters,

View File

@@ -7,6 +7,10 @@ use crate::shortint::keycache::KEY_CACHE;
use crate::shortint::ClientKey;
use super::parameters::ShortIntegerParameter;
use concrete_csprng::seeders::Seed;
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
/// The key associated to a short integer type
///
@@ -17,6 +21,23 @@ pub struct GenericShortIntClientKey<P: ShortIntegerParameter> {
_marker: PhantomData<P>,
}
impl<P> GenericShortIntClientKey<P>
where
P: ShortIntegerParameter,
{
pub(crate) fn with_seed(parameters: P, seed: Seed) -> Self {
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
let key = crate::shortint::engine::ShortintEngine::new_from_seeder(&mut seeder)
.new_client_key(parameters.into().into())
.unwrap();
Self {
key,
_marker: PhantomData,
}
}
}
impl<P> From<P> for GenericShortIntClientKey<P>
where
P: ShortIntegerParameter,

View File

@@ -9,7 +9,7 @@ use crate::high_level_api::{generate_keys, ClientKey, ConfigBuilder, PublicKey};
use crate::high_level_api::{FheUint256, FheUint8};
#[cfg(feature = "integer")]
use crate::integer::U256;
use crate::{CompressedPublicKey, CompressedServerKey};
use crate::{CompactPublicKey, CompressedPublicKey, CompressedServerKey};
#[cfg(any(feature = "boolean", feature = "shortint", feature = "integer"))]
use std::fmt::Debug;
@@ -129,6 +129,41 @@ fn test_server_key_decompression() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
#[test]
fn test_with_seed() -> Result<(), Box<dyn std::error::Error>> {
use crate::Seed;
let mut builder = ConfigBuilder::all_disabled();
#[cfg(feature = "integer")]
{
builder = builder.enable_default_integers();
}
#[cfg(feature = "shortint")]
{
builder = builder.enable_default_uint2();
}
#[cfg(feature = "boolean")]
{
builder = builder.enable_default_bool();
}
let config = builder.build();
let cks1 = ClientKey::generate_with_seed(config.clone(), Seed(125));
let cks2 = ClientKey::generate(config.clone());
let cks3 = ClientKey::generate_with_seed(config.clone(), Seed(125));
let cks4 = ClientKey::generate_with_seed(config, Seed(127));
let cks1_serialized = bincode::serialize(&cks1).unwrap();
let cks2_serialized = bincode::serialize(&cks2).unwrap();
let cks3_serialized = bincode::serialize(&cks3).unwrap();
let cks4_serialized = bincode::serialize(&cks4).unwrap();
assert_eq!(&cks1_serialized, &cks3_serialized);
assert_ne!(&cks1_serialized, &cks2_serialized);
assert_ne!(&cks1_serialized, &cks4_serialized);
Ok(())
}
#[cfg(feature = "integer")]
#[test]
#[should_panic]
@@ -172,10 +207,12 @@ fn test_serialize_deserialize_are_implemented() {
let pks = PublicKey::new(&cks);
let cpks = CompressedPublicKey::new(&cks);
let csks = CompressedServerKey::new(&cks);
let pksz = CompactPublicKey::new(&cks);
can_be_deserialized(&cks);
can_be_deserialized(&sks);
can_be_deserialized(&pks);
can_be_deserialized(&cpks);
can_be_deserialized(&csks);
can_be_deserialized(&pksz);
}

View File

@@ -30,6 +30,58 @@ pub type RadixCiphertextSmall = BaseRadixCiphertext<CiphertextSmall>;
pub type CompressedRadixCiphertextBig = BaseRadixCiphertext<CompressedCiphertextBig>;
pub type CompressedRadixCiphertextSmall = BaseRadixCiphertext<CompressedCiphertextSmall>;
pub type CompactCiphertextListBig = CompactCiphertextList<KeyswitchBootstrap>;
pub type CompactCiphertextListSmall = CompactCiphertextList<BootstrapKeyswitch>;
#[derive(Clone, Serialize, Deserialize)]
pub struct CompactCiphertextList<PBSOrder: PBSOrderMarker> {
pub(crate) ct_list: crate::shortint::ciphertext::CompactCiphertextList<PBSOrder>,
// 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: usize,
}
impl<PBSOrder: PBSOrderMarker> CompactCiphertextList<PBSOrder> {
pub fn expand_one(&self) -> RadixCiphertext<PBSOrder> {
let mut blocks = self.ct_list.expand();
blocks.truncate(self.num_blocks);
RadixCiphertext::from(blocks)
}
pub fn ciphertext_count(&self) -> usize {
self.ct_list.ct_list.lwe_ciphertext_count().0 / self.num_blocks
}
pub fn expand(&self) -> Vec<RadixCiphertext<PBSOrder>> {
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);
for _ in 0..num_ct {
let ct_blocks = all_block_iter
.by_ref()
.take(self.num_blocks)
.collect::<Vec<_>>();
if ct_blocks.len() < self.num_blocks {
break;
}
let ct = RadixCiphertext::from(ct_blocks);
ciphertexts.push(ct);
}
ciphertexts
}
pub fn size_elements(&self) -> usize {
self.ct_list.size_elements()
}
pub fn size_bytes(&self) -> usize {
self.ct_list.size_bytes()
}
}
impl<PBSOrder: PBSOrderMarker> RadixCiphertext<PBSOrder> {
pub fn block_carries_are_empty(&self) -> bool {
self.blocks.iter().all(|block| block.carry_is_empty())

View File

@@ -27,6 +27,14 @@ impl<OpOrder: crate::shortint::PBSOrderMarker> KnowsMessageModulus
}
}
impl<OpOrder: crate::shortint::PBSOrderMarker> KnowsMessageModulus
for crate::shortint::CompactPublicKeyBase<OpOrder>
{
fn message_modulus(&self) -> MessageModulus {
self.parameters.message_modulus()
}
}
impl KnowsMessageModulus for crate::shortint::ServerKey {
fn message_modulus(&self) -> MessageModulus {
self.message_modulus
@@ -53,26 +61,34 @@ where
F: Fn(&BlockKey, u64) -> Block,
RadixCiphertextType: From<Vec<Block>>,
{
let bits_in_block = encrypting_key.message_modulus().0.ilog2();
let decomposer = BlockDecomposer::new(message, bits_in_block);
let mut blocks = Vec::with_capacity(num_blocks);
for clear_block in decomposer.iter_as::<u64>().take(num_blocks) {
let ct = encrypt_block(encrypting_key, clear_block);
blocks.push(ct);
}
let message_modulus = encrypting_key.message_modulus();
let clear_block_iterator =
create_clear_radix_block_iterator(message, message_modulus, num_blocks);
// This will happen if T has less bits than
// bits_in_block * num_blocks
if blocks.len() < num_blocks {
for _ in 0..num_blocks - blocks.len() {
let ct = encrypt_block(encrypting_key, 0);
blocks.push(ct);
}
}
let blocks = clear_block_iterator
.map(|clear_block| encrypt_block(encrypting_key, clear_block))
.collect::<Vec<_>>();
RadixCiphertextType::from(blocks)
}
pub(crate) fn create_clear_radix_block_iterator<T>(
message: T,
message_modulus: MessageModulus,
num_blocks: usize,
) -> impl Iterator<Item = u64>
where
T: DecomposableInto<u64>,
{
let bits_in_block = message_modulus.0.ilog2();
let decomposer = BlockDecomposer::new(message, bits_in_block);
decomposer
.iter_as::<u64>()
.chain(std::iter::repeat(0u64))
.take(num_blocks)
}
pub(crate) fn encrypt_crt<BlockKey, Block, CrtCiphertextType, F>(
encrypting_key: &BlockKey,
message: u64,

View File

@@ -69,7 +69,9 @@ pub use ciphertext::{
};
pub use client_key::{ClientKey, CrtClientKey, RadixClientKey};
pub use public_key::{
CompressedPublicKeyBig, CompressedPublicKeySmall, PublicKeyBig, PublicKeySmall,
CompactPublicKeyBig, CompactPublicKeySmall, CompressedCompactPublicKeyBase,
CompressedCompactPublicKeyBig, CompressedCompactPublicKeySmall, CompressedPublicKeyBig,
CompressedPublicKeySmall, PublicKeyBig, PublicKeySmall,
};
pub use server_key::{CheckError, CompressedServerKey, ServerKey};
pub use u256::U256;

View File

@@ -0,0 +1,154 @@
use serde::{Deserialize, Serialize};
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::ClientKey;
use crate::shortint::ciphertext::{BootstrapKeyswitch, KeyswitchBootstrap};
use crate::shortint::{
CompactPublicKeyBase as ShortintCompactPublicKeyBase,
CompressedCompactPublicKeyBase as ShortintCompressedCompactPublicKeyBase, PBSOrderMarker,
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompactPublicKeyBase<OpOrder: PBSOrderMarker> {
pub(crate) key: ShortintCompactPublicKeyBase<OpOrder>,
}
pub type CompactPublicKeyBig = CompactPublicKeyBase<KeyswitchBootstrap>;
pub type CompactPublicKeySmall = CompactPublicKeyBase<BootstrapKeyswitch>;
impl<OpOrder: PBSOrderMarker> CompactPublicKeyBase<OpOrder> {
pub fn new(client_key: &ClientKey) -> Self {
let key = ShortintCompactPublicKeyBase::<OpOrder>::new(&client_key.key);
Self { key }
}
pub fn try_new(client_key: &ClientKey) -> Option<Self> {
let key = ShortintCompactPublicKeyBase::<OpOrder>::try_new(&client_key.key)?;
Some(Self { key })
}
pub fn encrypt_radix<T: DecomposableInto<u64>>(
&self,
message: T,
num_blocks: usize,
) -> RadixCiphertext<OpOrder> {
encrypt_words_radix_impl(
&self.key,
message,
num_blocks,
ShortintCompactPublicKeyBase::encrypt,
)
}
pub fn encrypt_radix_compact<T: DecomposableInto<u64>>(
&self,
message: T,
num_blocks: usize,
) -> CompactCiphertextList<OpOrder> {
let clear_block_iter = create_clear_radix_block_iterator(
message,
self.key.parameters.message_modulus(),
num_blocks,
);
let ct_list = self.key.encrypt_iter(clear_block_iter);
CompactCiphertextList {
ct_list,
num_blocks,
}
}
pub fn encrypt_slice_radix_compact<T: DecomposableInto<u64>>(
&self,
messages: &[T],
num_blocks: usize,
) -> CompactCiphertextList<OpOrder> {
self.encrypt_iter_radix_compact(messages.iter().copied(), num_blocks)
}
pub fn encrypt_iter_radix_compact<T: DecomposableInto<u64>>(
&self,
mut message_iter: impl Iterator<Item = T>,
num_blocks: usize,
) -> CompactCiphertextList<OpOrder> {
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);
}
(Some(first_message), Some(second_message)) => {
let first_iter = create_clear_radix_block_iterator(
first_message,
self.key.parameters.message_modulus(),
num_blocks,
);
let second_iter = create_clear_radix_block_iterator(
second_message,
self.key.parameters.message_modulus(),
num_blocks,
);
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,
);
iterator_chain = Box::new(iterator_chain.chain(other_iter));
}
let ct_list = self.key.encrypt_iter(iterator_chain);
CompactCiphertextList {
ct_list,
num_blocks,
}
}
pub fn size_elements(&self) -> usize {
self.key.size_elements()
}
pub fn size_bytes(&self) -> usize {
self.key.size_bytes()
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompressedCompactPublicKeyBase<OpOrder: PBSOrderMarker> {
pub(crate) key: ShortintCompressedCompactPublicKeyBase<OpOrder>,
}
pub type CompressedCompactPublicKeyBig = CompressedCompactPublicKeyBase<KeyswitchBootstrap>;
pub type CompressedCompactPublicKeySmall = CompressedCompactPublicKeyBase<BootstrapKeyswitch>;
impl<OpOrder: PBSOrderMarker> CompressedCompactPublicKeyBase<OpOrder> {
pub fn new(client_key: &ClientKey) -> Self {
let key = ShortintCompressedCompactPublicKeyBase::<OpOrder>::new(&client_key.key);
Self { key }
}
pub fn decompress(self) -> CompactPublicKeyBase<OpOrder> {
CompactPublicKeyBase {
key: self.key.decompress(),
}
}
}
impl<OpOrder: PBSOrderMarker> From<CompressedCompactPublicKeyBase<OpOrder>>
for CompactPublicKeyBase<OpOrder>
{
fn from(value: CompressedCompactPublicKeyBase<OpOrder>) -> Self {
value.decompress()
}
}

View File

@@ -1,8 +1,13 @@
//! Module with the definition of the encryption PublicKey.
pub mod compact;
pub mod compressed;
pub mod standard;
pub use compact::{
CompactPublicKeyBase, CompactPublicKeyBig, CompactPublicKeySmall,
CompressedCompactPublicKeyBase, CompressedCompactPublicKeyBig, CompressedCompactPublicKeySmall,
};
pub use compressed::{CompressedPublicKeyBase, CompressedPublicKeyBig, CompressedPublicKeySmall};
pub use standard::{PublicKey, PublicKeyBig, PublicKeySmall};

View File

@@ -1,6 +1,6 @@
use rand::Rng;
use crate::integer::{CompressedPublicKeyBig, PublicKeyBig};
use crate::integer::{gen_keys, CompressedPublicKeyBig, PublicKeyBig};
use crate::shortint::parameters::*;
use crate::shortint::ClassicPBSParameters;
@@ -17,6 +17,14 @@ create_parametrized_test!(radix_encrypt_decrypt_compressed_128_bits {
* PARAM_MESSAGE_4_CARRY_4, Skipped as its slow */
});
create_parametrized_test!(big_radix_encrypt_decrypt_compact_128_bits_list {
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK
});
create_parametrized_test!(small_radix_encrypt_decrypt_compact_128_bits_list {
PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK
});
/// Test that the public key can encrypt a 128 bit number
/// in radix decomposition, and that the client key can decrypt it
fn big_radix_encrypt_decrypt_128_bits(param: ClassicPBSParameters) {
@@ -58,3 +66,46 @@ fn radix_encrypt_decrypt_compressed_128_bits(param: ClassicPBSParameters) {
// assert
assert_eq!(clear, dec);
}
fn big_radix_encrypt_decrypt_compact_128_bits_list(params: ClassicPBSParameters) {
radix_encrypt_decrypt_compact_128_bits_list::<crate::shortint::ciphertext::KeyswitchBootstrap>(
params,
);
}
fn small_radix_encrypt_decrypt_compact_128_bits_list(params: ClassicPBSParameters) {
radix_encrypt_decrypt_compact_128_bits_list::<crate::shortint::ciphertext::BootstrapKeyswitch>(
params,
);
}
fn radix_encrypt_decrypt_compact_128_bits_list<OpOrder: crate::shortint::PBSOrderMarker>(
params: ClassicPBSParameters,
) {
let (cks, _) = gen_keys(params);
let pk = crate::integer::public_key::CompactPublicKeyBase::<OpOrder>::new(&cks);
let mut rng = rand::thread_rng();
let num_block = (128f64 / (params.message_modulus.0 as f64).log(2.0)).ceil() as usize;
const MAX_CT: usize = 20;
let mut clear_vec = Vec::with_capacity(MAX_CT);
for _ in 0..25 {
let num_ct_for_this_iter = rng.gen_range(1..=MAX_CT);
clear_vec.truncate(0);
for _ in 0..num_ct_for_this_iter {
let clear = rng.gen::<u128>();
clear_vec.push(clear);
}
let compact_encrypted_list = pk.encrypt_slice_radix_compact(&clear_vec, num_block);
let ciphertext_vec = compact_encrypted_list.expand();
for (ciphertext, clear) in ciphertext_vec.iter().zip(clear_vec.iter().copied()) {
let decrypted: u128 = cks.decrypt_radix(ciphertext);
assert_eq!(decrypted, clear);
}
}
}

View File

@@ -1,7 +1,9 @@
pub use crate::core_crypto::commons::math::random::Seed;
use bincode;
use wasm_bindgen::prelude::*;
use super::js_wasm_seeder;
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
use std::panic::set_hook;
@@ -101,14 +103,10 @@ impl Boolean {
let seed_low_bytes: u128 = seed_low_bytes.into();
let seed: u128 = (seed_high_bytes << 64) | seed_low_bytes;
let mut constant_seeder = Box::new(js_wasm_seeder::ConstantSeeder::new(
crate::core_crypto::commons::math::random::Seed(seed),
));
let mut tmp_boolean_engine =
crate::boolean::engine::BooleanEngine::new_from_seeder(constant_seeder.as_mut());
BooleanClientKey(tmp_boolean_engine.create_client_key(parameters.0.to_owned()))
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(Seed(seed));
let key = crate::boolean::engine::BooleanEngine::new_from_seeder(&mut seeder)
.create_client_key(parameters.0.to_owned());
BooleanClientKey(key)
}
#[wasm_bindgen]

View File

@@ -25,6 +25,14 @@ impl TfheConfigBuilder {
Self(self.0.enable_default_integers_small())
}
#[wasm_bindgen]
pub fn enable_custom_integers(
self,
block_parameters: crate::js_on_wasm_api::shortint::ShortintParameters,
) -> Self {
Self(self.0.enable_custom_integers(block_parameters.0, None))
}
#[wasm_bindgen]
pub fn build(self) -> TfheConfig {
TfheConfig(self.0.build())

View File

@@ -44,8 +44,9 @@ macro_rules! create_wrapper_type_non_native_type(
{
type_name: $type_name:ident,
compressed_type_name: $compressed_type_name:ident,
rust_type: $rust_type:ty
$(,)?
compact_type_name: $compact_type_name:ident,
compact_list_type_name: $compact_list_type_name:ident,
rust_type: $rust_type:ty $(,)?
}
) => {
#[wasm_bindgen]
@@ -95,6 +96,20 @@ macro_rules! create_wrapper_type_non_native_type(
})
}
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
value: JsValue,
compact_public_key: &crate::js_on_wasm_api::high_level_api::keys::TfheCompactPublicKey,
) -> Result<$type_name, JsError> {
catch_panic_result(|| {
let value = <$rust_type>::try_from(value)
.map_err(|_| JsError::new(&format!("Failed to convert the value to a {}", stringify!($rust_type))))?;
crate::high_level_api::$type_name::try_encrypt(value, &compact_public_key.0)
.map($type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn decrypt(
&self,
@@ -122,6 +137,7 @@ macro_rules! create_wrapper_type_non_native_type(
})
}
}
#[wasm_bindgen]
pub struct $compressed_type_name(pub(crate) crate::high_level_api::$compressed_type_name);
@@ -164,14 +180,113 @@ macro_rules! create_wrapper_type_non_native_type(
})
}
}
#[wasm_bindgen]
pub struct $compact_type_name(pub(crate) crate::high_level_api::$compact_type_name);
#[wasm_bindgen]
impl $compact_type_name {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
value: JsValue,
client_key: &crate::js_on_wasm_api::high_level_api::keys::TfheCompactPublicKey,
) -> Result<$compact_type_name, JsError> {
catch_panic_result(|| {
let value = <$rust_type>::try_from(value)
.map_err(|_| JsError::new(&format!("Failed to convert the value to a {}", stringify!($rust_type))))?;
crate::high_level_api::$compact_type_name::try_encrypt(value, &client_key.0)
.map($compact_type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn expand(
&self,
) -> Result<$type_name, JsError> {
catch_panic(||{
$type_name(self.0.expand())
})
}
#[wasm_bindgen]
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
catch_panic_result(|| bincode::serialize(&self.0).map_err(into_js_error))
}
#[wasm_bindgen]
pub fn deserialize(buffer: &[u8]) -> Result<$compact_type_name, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map($compact_type_name)
.map_err(into_js_error)
})
}
}
#[wasm_bindgen]
pub struct $compact_list_type_name(pub(crate) crate::high_level_api::$compact_list_type_name);
#[wasm_bindgen]
impl $compact_list_type_name {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
values: Vec<JsValue>,
public_key: &crate::js_on_wasm_api::high_level_api::keys::TfheCompactPublicKey,
) -> Result<$compact_list_type_name, JsError> {
catch_panic_result(|| {
let values = values
.into_iter()
.map(|value| {
<$rust_type>::try_from(value)
.map_err(|_| {
JsError::new(&format!("Failed to convert the value to a {}", stringify!($rust_type)))
})
})
.collect::<Result<Vec<_>, _>>()?;
crate::high_level_api::$compact_list_type_name::try_encrypt(&values, &public_key.0)
.map($compact_list_type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn expand(
&self,
) -> Result<Vec<JsValue>, JsError> {
catch_panic(||{
self.0.expand()
.into_iter()
.map($type_name)
.map(JsValue::from)
.collect::<Vec<_>>()
})
}
#[wasm_bindgen]
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
catch_panic_result(|| bincode::serialize(&self.0).map_err(into_js_error))
}
#[wasm_bindgen]
pub fn deserialize(buffer: &[u8]) -> Result<$compact_list_type_name, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map($compact_list_type_name)
.map_err(into_js_error)
})
}
}
};
(
$(
{
type_name: $type_name:ident,
compressed_type_name: $compressed_type_name:ident,
rust_type: $rust_type:ty
$(,)?
compact_type_name: $compact_type_name:ident,
compact_list_type_name: $compact_list_type_name:ident,
rust_type: $rust_type:ty $(,)?
}
),*
$(,)?
@@ -181,6 +296,8 @@ macro_rules! create_wrapper_type_non_native_type(
{
type_name: $type_name,
compressed_type_name: $compressed_type_name,
compact_type_name: $compact_type_name,
compact_list_type_name: $compact_list_type_name,
rust_type: $rust_type
}
);
@@ -192,13 +309,17 @@ create_wrapper_type_non_native_type!(
{
type_name: FheUint128,
compressed_type_name: CompressedFheUint128,
compact_type_name: CompactFheUint128,
compact_list_type_name: CompactFheUint128List,
rust_type: u128,
},
{
type_name: FheUint256,
compressed_type_name: CompressedFheUint256,
compact_type_name: CompactFheUint256,
compact_list_type_name: CompactFheUint256List,
rust_type: U256,
},
}
);
// We use this macro to define wasm wrapper for
@@ -209,8 +330,9 @@ macro_rules! create_wrapper_type_that_has_native_type(
{
type_name: $type_name:ident,
compressed_type_name: $compressed_type_name:ident,
native_type: $native_type:ty
$(,)?
compact_type_name: $compact_type_name:ident,
compact_list_type_name: $compact_list_type_name:ident,
native_type: $native_type:ty $(,)?
}
) => {
#[wasm_bindgen]
@@ -254,6 +376,19 @@ macro_rules! create_wrapper_type_that_has_native_type(
})
}
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
value: $native_type,
compact_public_key: &crate::js_on_wasm_api::high_level_api::keys::TfheCompactPublicKey,
) -> Result<$type_name, JsError> {
catch_panic_result(|| {
crate::high_level_api::$type_name::try_encrypt(value, &compact_public_key.0)
.map($type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn decrypt(
&self,
@@ -316,14 +451,102 @@ macro_rules! create_wrapper_type_that_has_native_type(
})
}
}
#[wasm_bindgen]
pub struct $compact_type_name(pub(crate) crate::high_level_api::$compact_type_name);
#[wasm_bindgen]
impl $compact_type_name {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
value: $native_type,
client_key: &crate::js_on_wasm_api::high_level_api::keys::TfheCompactPublicKey,
) -> Result<$compact_type_name, JsError> {
catch_panic_result(|| {
crate::high_level_api::$compact_type_name::try_encrypt(value, &client_key.0)
.map($compact_type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn expand(
&self,
) -> Result<$type_name, JsError> {
catch_panic(||{
$type_name(self.0.expand())
})
}
#[wasm_bindgen]
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
catch_panic_result(|| bincode::serialize(&self.0).map_err(into_js_error))
}
#[wasm_bindgen]
pub fn deserialize(buffer: &[u8]) -> Result<$compact_type_name, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map($compact_type_name)
.map_err(into_js_error)
})
}
}
#[wasm_bindgen]
pub struct $compact_list_type_name(pub(crate) crate::high_level_api::$compact_list_type_name);
#[wasm_bindgen]
impl $compact_list_type_name {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
values: Vec<$native_type>,
public_key: &crate::js_on_wasm_api::high_level_api::keys::TfheCompactPublicKey,
) -> Result<$compact_list_type_name, JsError> {
catch_panic_result(|| {
crate::high_level_api::$compact_list_type_name::try_encrypt(&values, &public_key.0)
.map($compact_list_type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn expand(
&self,
) -> Result<Vec<JsValue>, JsError> {
catch_panic(||{
self.0.expand()
.into_iter()
.map($type_name)
.map(JsValue::from)
.collect::<Vec<_>>()
})
}
#[wasm_bindgen]
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
catch_panic_result(|| bincode::serialize(&self.0).map_err(into_js_error))
}
#[wasm_bindgen]
pub fn deserialize(buffer: &[u8]) -> Result<$compact_list_type_name, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map($compact_list_type_name)
.map_err(into_js_error)
})
}
}
};
(
$(
{
type_name: $type_name:ident,
compressed_type_name: $compressed_type_name:ident,
native_type: $native_type:ty
$(,)?
compact_type_name: $compact_type_name:ident,
compact_list_type_name: $compact_list_type_name:ident,
native_type: $native_type:ty $(,)?
}
),*
$(,)?
@@ -333,6 +556,8 @@ macro_rules! create_wrapper_type_that_has_native_type(
{
type_name: $type_name,
compressed_type_name: $compressed_type_name,
compact_type_name: $compact_type_name,
compact_list_type_name: $compact_list_type_name,
native_type: $native_type
}
);
@@ -344,21 +569,29 @@ create_wrapper_type_that_has_native_type!(
{
type_name: FheUint8,
compressed_type_name: CompressedFheUint8,
compact_type_name: CompactFheUint8,
compact_list_type_name: CompactFheUint8List,
native_type: u8,
},
{
type_name: FheUint16,
compressed_type_name: CompressedFheUint16,
compact_type_name: CompactFheUint16,
compact_list_type_name: CompactFheUint16List,
native_type: u16,
},
{
type_name: FheUint32,
compressed_type_name: CompressedFheUint32,
compact_type_name: CompactFheUint32,
compact_list_type_name: CompactFheUint32List,
native_type: u32,
},
{
type_name: FheUint64,
compressed_type_name: CompressedFheUint64,
compact_type_name: CompactFheUint64,
compact_list_type_name: CompactFheUint64List,
native_type: u64,
},
);

View File

@@ -20,6 +20,19 @@ impl TfheClientKey {
catch_panic(|| Self(hlapi::ClientKey::generate(config.0.clone())))
}
#[wasm_bindgen]
pub fn generate_with_seed(
config: &TfheConfig,
seed: JsValue,
) -> Result<TfheClientKey, JsError> {
catch_panic_result(|| {
let seed =
u128::try_from(seed).map_err(|_| JsError::new("Value does not fit in a u128"))?;
let key = hlapi::ClientKey::generate_with_seed(config.0.clone(), crate::Seed(seed));
Ok(Self(key))
})
}
#[wasm_bindgen]
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
catch_panic_result(|| bincode::serialize(&self.0).map_err(into_js_error))
@@ -126,3 +139,58 @@ impl TfheCompressedPublicKey {
})
}
}
#[wasm_bindgen]
pub struct TfheCompactPublicKey(pub(crate) hlapi::CompactPublicKey);
#[wasm_bindgen]
impl TfheCompactPublicKey {
#[wasm_bindgen]
pub fn new(client_key: &TfheClientKey) -> Result<TfheCompactPublicKey, JsError> {
catch_panic(|| Self(hlapi::CompactPublicKey::new(&client_key.0)))
}
#[wasm_bindgen]
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
catch_panic_result(|| bincode::serialize(&self.0).map_err(into_js_error))
}
#[wasm_bindgen]
pub fn deserialize(buffer: &[u8]) -> Result<TfheCompactPublicKey, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map(Self)
.map_err(into_js_error)
})
}
}
#[wasm_bindgen]
pub struct TfheCompressedCompactPublicKey(pub(crate) hlapi::CompressedCompactPublicKey);
#[wasm_bindgen]
impl TfheCompressedCompactPublicKey {
#[wasm_bindgen]
pub fn new(client_key: &TfheClientKey) -> Result<TfheCompressedCompactPublicKey, JsError> {
catch_panic(|| Self(hlapi::CompressedCompactPublicKey::new(&client_key.0)))
}
#[wasm_bindgen]
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
catch_panic_result(|| bincode::serialize(&self.0).map_err(into_js_error))
}
#[wasm_bindgen]
pub fn deserialize(buffer: &[u8]) -> Result<TfheCompressedCompactPublicKey, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map(Self)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn decompress(&self) -> Result<TfheCompactPublicKey, JsError> {
catch_panic(|| TfheCompactPublicKey(self.0.clone().decompress()))
}
}

View File

@@ -13,32 +13,3 @@ pub use wasm_bindgen_rayon::init_thread_pool;
pub mod high_level_api;
pub use high_level_api::*;
pub(self) mod js_wasm_seeder {
use crate::core_crypto::commons::math::random::{Seed, Seeder};
const SEED_BYTES_COUNT: usize = 16;
pub struct ConstantSeeder {
seed: Seed,
}
impl ConstantSeeder {
pub fn new(seed: Seed) -> Self {
Self { seed }
}
}
impl Seeder for ConstantSeeder {
fn seed(&mut self) -> Seed {
self.seed
}
fn is_available() -> bool
where
Self: Sized,
{
true
}
}
}

View File

@@ -1,8 +1,9 @@
use crate::core_crypto::commons::generators::DeterministicSeeder;
pub use crate::core_crypto::commons::math::random::Seed;
use crate::core_crypto::prelude::ActivatedRandomGenerator;
use bincode;
use wasm_bindgen::prelude::*;
use super::js_wasm_seeder;
use std::panic::set_hook;
#[derive(serde::Serialize, serde::Deserialize)]
@@ -72,6 +73,84 @@ impl From<ShortintEncryptionKeyChoice> for crate::shortint::parameters::Encrypti
}
}
macro_rules! expose_predefined_parameters {
(
$(
$param_name:ident
),*
$(,)?
) => {
#[wasm_bindgen]
#[allow(non_camel_case_types)]
pub enum ShortintParametersName {
$(
$param_name,
)*
}
#[wasm_bindgen]
impl ShortintParameters {
#[wasm_bindgen(constructor)]
pub fn new(name: ShortintParametersName) -> Self {
match name {
$(
ShortintParametersName::$param_name => {
Self(crate::shortint::parameters::$param_name)
}
)*
}
}
}
}
}
expose_predefined_parameters! {
PARAM_MESSAGE_1_CARRY_0,
PARAM_MESSAGE_1_CARRY_1,
PARAM_MESSAGE_2_CARRY_0,
PARAM_MESSAGE_1_CARRY_2,
PARAM_MESSAGE_2_CARRY_1,
PARAM_MESSAGE_3_CARRY_0,
PARAM_MESSAGE_1_CARRY_3,
PARAM_MESSAGE_2_CARRY_2,
PARAM_MESSAGE_3_CARRY_1,
PARAM_MESSAGE_4_CARRY_0,
PARAM_MESSAGE_1_CARRY_4,
PARAM_MESSAGE_2_CARRY_3,
PARAM_MESSAGE_3_CARRY_2,
PARAM_MESSAGE_4_CARRY_1,
PARAM_MESSAGE_5_CARRY_0,
PARAM_MESSAGE_1_CARRY_5,
PARAM_MESSAGE_2_CARRY_4,
PARAM_MESSAGE_3_CARRY_3,
PARAM_MESSAGE_4_CARRY_2,
PARAM_MESSAGE_5_CARRY_1,
PARAM_MESSAGE_6_CARRY_0,
PARAM_MESSAGE_1_CARRY_6,
PARAM_MESSAGE_2_CARRY_5,
PARAM_MESSAGE_3_CARRY_4,
PARAM_MESSAGE_4_CARRY_3,
PARAM_MESSAGE_5_CARRY_2,
PARAM_MESSAGE_6_CARRY_1,
PARAM_MESSAGE_7_CARRY_0,
PARAM_MESSAGE_1_CARRY_7,
PARAM_MESSAGE_2_CARRY_6,
PARAM_MESSAGE_3_CARRY_5,
PARAM_MESSAGE_4_CARRY_4,
PARAM_MESSAGE_5_CARRY_3,
PARAM_MESSAGE_6_CARRY_2,
PARAM_MESSAGE_7_CARRY_1,
PARAM_MESSAGE_8_CARRY_0,
PARAM_SMALL_MESSAGE_1_CARRY_1,
PARAM_SMALL_MESSAGE_2_CARRY_2,
PARAM_SMALL_MESSAGE_3_CARRY_3,
PARAM_SMALL_MESSAGE_4_CARRY_4,
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK,
PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK,
}
#[wasm_bindgen]
impl Shortint {
#[wasm_bindgen]
@@ -198,14 +277,8 @@ impl Shortint {
let seed_low_bytes: u128 = seed_low_bytes.into();
let seed: u128 = (seed_high_bytes << 64) | seed_low_bytes;
let mut constant_seeder = Box::new(js_wasm_seeder::ConstantSeeder::new(
crate::core_crypto::commons::math::random::Seed(seed),
));
let mut tmp_shortint_engine =
crate::shortint::engine::ShortintEngine::new_from_seeder(constant_seeder.as_mut());
tmp_shortint_engine
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(Seed(seed));
crate::shortint::engine::ShortintEngine::new_from_seeder(&mut seeder)
.new_client_key(parameters.0.try_into().unwrap())
.map_err(|e| wasm_bindgen::JsError::new(format!("{e:?}").as_str()))
.map(ShortintClientKey)

View File

@@ -359,6 +359,66 @@ impl<OpOrder: PBSOrderMarker> From<CompressedCiphertextBase<OpOrder>> for Cipher
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompactCiphertextList<OpOrder: PBSOrderMarker> {
pub ct_list: LweCompactCiphertextListOwned<u64>,
pub degree: Degree,
pub message_modulus: MessageModulus,
pub carry_modulus: CarryModulus,
pub _order_marker: PhantomData<OpOrder>,
}
impl<OpOrder: PBSOrderMarker> CompactCiphertextList<OpOrder> {
pub fn expand(&self) -> Vec<CiphertextBase<OpOrder>> {
let mut output_lwe_ciphertext_list = LweCiphertextList::new(
0u64,
self.ct_list.lwe_size(),
self.ct_list.lwe_ciphertext_count(),
self.ct_list.ciphertext_modulus(),
);
// No parallelism allowed
#[cfg(all(feature = "__wasm_api", not(feature = "parallel-wasm-api")))]
{
use crate::core_crypto::prelude::expand_lwe_compact_ciphertext_list;
expand_lwe_compact_ciphertext_list(&mut output_lwe_ciphertext_list, &self.ct_list);
}
// Parallelism allowed
#[cfg(any(not(feature = "__wasm_api"), feature = "parallel-wasm-api"))]
{
use crate::core_crypto::prelude::par_expand_lwe_compact_ciphertext_list;
par_expand_lwe_compact_ciphertext_list(&mut output_lwe_ciphertext_list, &self.ct_list);
}
output_lwe_ciphertext_list
.as_ref()
.chunks_exact(self.ct_list.lwe_size().0)
.map(|lwe_data| {
let ct = LweCiphertext::from_container(
lwe_data.to_vec(),
self.ct_list.ciphertext_modulus(),
);
CiphertextBase {
ct,
degree: self.degree,
message_modulus: self.message_modulus,
carry_modulus: self.carry_modulus,
_order_marker: self._order_marker,
}
})
.collect::<Vec<_>>()
}
pub fn size_elements(&self) -> usize {
self.ct_list.size_elements()
}
pub fn size_bytes(&self) -> usize {
self.ct_list.size_bytes()
}
}
#[cfg(test)]
mod tests {
use super::{BootstrapKeyswitch, KeyswitchBootstrap};

View File

@@ -189,17 +189,17 @@ pub(crate) type EngineResult<T> = Result<T, EngineError>;
/// This structs actually implements the logics into its methods.
pub struct ShortintEngine {
/// A structure containing a single CSPRNG to generate secret key coefficients.
secret_generator: SecretRandomGenerator<ActivatedRandomGenerator>,
pub(crate) secret_generator: SecretRandomGenerator<ActivatedRandomGenerator>,
/// A structure containing two CSPRNGs to generate material for encryption like public masks
/// and secret errors.
///
/// The [`EncryptionRandomGenerator`] contains two CSPRNGs, one publicly seeded used to
/// generate mask coefficients and one privately seeded used to generate errors during
/// encryption.
encryption_generator: EncryptionRandomGenerator<ActivatedRandomGenerator>,
pub(crate) encryption_generator: EncryptionRandomGenerator<ActivatedRandomGenerator>,
/// A seeder that can be called to generate 128 bits seeds, useful to create new
/// [`EncryptionRandomGenerator`] to encrypt seeded types.
seeder: DeterministicSeeder<ActivatedRandomGenerator>,
pub(crate) seeder: DeterministicSeeder<ActivatedRandomGenerator>,
computation_buffers: ComputationBuffers,
ciphertext_buffers: Memory,
}

View File

@@ -252,11 +252,13 @@ impl NamedParam for ShortintParameterSet {
PARAM_MESSAGE_7_CARRY_0,
PARAM_MESSAGE_7_CARRY_1,
PARAM_MESSAGE_8_CARRY_0,
PARAM_MESSAGE_2_CARRY_2_COMPACT_PK,
// Small
PARAM_SMALL_MESSAGE_1_CARRY_1,
PARAM_SMALL_MESSAGE_2_CARRY_2,
PARAM_SMALL_MESSAGE_3_CARRY_3,
PARAM_SMALL_MESSAGE_4_CARRY_4,
PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK,
// MultiBit Group 2
PARAM_MULTI_BIT_MESSAGE_1_CARRY_1_GROUP_2,
PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_2,

View File

@@ -67,6 +67,8 @@ pub use parameters::{
MultiBitPBSParameters, PBSParameters, ShortintParameterSet, WopbsParameters,
};
pub use public_key::{
CompactPublicKeyBase, CompactPublicKeyBig, CompactPublicKeySmall,
CompressedCompactPublicKeyBase, CompressedCompactPublicKeyBig, CompressedCompactPublicKeySmall,
CompressedPublicKeyBase, CompressedPublicKeyBig, CompressedPublicKeySmall, PublicKeyBase,
PublicKeyBig, PublicKeySmall,
};

View File

@@ -1297,6 +1297,27 @@ pub const PARAM_MULTI_BIT_MESSAGE_4_CARRY_4_GROUP_3: MultiBitPBSParameters =
grouping_factor: LweBskGroupingFactor(3),
};
pub const PARAM_MESSAGE_2_CARRY_2_COMPACT_PK: ClassicPBSParameters = ClassicPBSParameters {
lwe_dimension: LweDimension(1024),
glwe_dimension: GlweDimension(1),
polynomial_size: PolynomialSize(2048),
lwe_modular_std_dev: StandardDev(0.000000049029381729),
glwe_modular_std_dev: StandardDev(0.00000000000000031528),
pbs_base_log: DecompositionBaseLog(21),
pbs_level: DecompositionLevelCount(1),
ks_level: DecompositionLevelCount(2),
ks_base_log: DecompositionBaseLog(8),
message_modulus: MessageModulus(4),
carry_modulus: CarryModulus(4),
ciphertext_modulus: CiphertextModulus::new_native(),
encryption_key_choice: EncryptionKeyChoice::Big,
};
pub const PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK: ClassicPBSParameters = ClassicPBSParameters {
encryption_key_choice: EncryptionKeyChoice::Small,
..PARAM_MESSAGE_2_CARRY_2_COMPACT_PK
};
/// Return a parameter set from a message and carry moduli.
///
/// # Example

View File

@@ -19,5 +19,8 @@ pub use super::parameters::{
PARAM_MESSAGE_2_CARRY_6, PARAM_MESSAGE_3_CARRY_3, PARAM_MESSAGE_3_CARRY_4,
PARAM_MESSAGE_3_CARRY_5, PARAM_MESSAGE_4_CARRY_4,
};
pub use super::public_key::{PublicKeyBase, PublicKeyBig, PublicKeySmall};
pub use super::public_key::{
CompactPublicKeyBase, CompactPublicKeyBig, CompactPublicKeySmall, PublicKeyBase, PublicKeyBig,
PublicKeySmall,
};
pub use super::server_key::ServerKey;

View File

@@ -0,0 +1,255 @@
use serde::{Deserialize, Serialize};
use crate::core_crypto::prelude::{
allocate_and_generate_new_seeded_lwe_compact_public_key, generate_lwe_compact_public_key,
LweCiphertextCount, LweCiphertextOwned, LweCompactCiphertextListOwned,
LweCompactPublicKeyOwned, Plaintext, PlaintextList, SeededLweCompactPublicKeyOwned,
};
use crate::core_crypto::prelude::encrypt_lwe_ciphertext_with_compact_public_key;
use crate::shortint::ciphertext::{
BootstrapKeyswitch, CompactCiphertextList, Degree, KeyswitchBootstrap,
};
use crate::shortint::{CiphertextBase, ClientKey, PBSOrderMarker, ShortintParameterSet};
use crate::shortint::engine::ShortintEngine;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompactPublicKeyBase<OpOrder: PBSOrderMarker> {
pub(crate) key: LweCompactPublicKeyOwned<u64>,
pub parameters: ShortintParameterSet,
pub _order_marker: std::marker::PhantomData<OpOrder>,
}
pub type CompactPublicKeyBig = CompactPublicKeyBase<KeyswitchBootstrap>;
pub type CompactPublicKeySmall = CompactPublicKeyBase<BootstrapKeyswitch>;
fn to_plaintext_iterator(
message_iter: impl Iterator<Item = 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;
message_iter.map(move |message| {
//The delta is the one defined by the parameters
let delta = (1_u64 << 63) / (message_modulus * carry_modulus);
//The input is reduced modulus the message_modulus
let m = message % message_modulus;
let shifted_message = m * delta;
// encode the message
Plaintext(shifted_message)
})
}
impl<OpOrder: PBSOrderMarker> CompactPublicKeyBase<OpOrder> {
pub fn new(client_key: &ClientKey) -> CompactPublicKeyBase<OpOrder> {
Self::try_new(client_key).expect(
"Incompatible parameters, the lwe_dimension of the secret key must be a power of two",
)
}
pub fn try_new(client_key: &ClientKey) -> Option<CompactPublicKeyBase<OpOrder>> {
let parameters = client_key.parameters;
let (secret_encryption_key, encryption_noise) = match OpOrder::pbs_order() {
crate::shortint::PBSOrder::KeyswitchBootstrap => (
&client_key.large_lwe_secret_key,
parameters.glwe_modular_std_dev(),
),
crate::shortint::PBSOrder::BootstrapKeyswitch => (
&client_key.small_lwe_secret_key,
parameters.lwe_modular_std_dev(),
),
};
if !secret_encryption_key.lwe_dimension().0.is_power_of_two() {
return None;
}
let mut key = LweCompactPublicKeyOwned::new(
0u64,
secret_encryption_key.lwe_dimension(),
parameters.ciphertext_modulus(),
);
ShortintEngine::with_thread_local_mut(|engine| {
generate_lwe_compact_public_key(
secret_encryption_key,
&mut key,
encryption_noise,
&mut engine.encryption_generator,
);
});
Some(Self {
key,
parameters,
_order_marker: std::marker::PhantomData,
})
}
pub fn encrypt(&self, message: u64) -> CiphertextBase<OpOrder> {
let plain = to_plaintext_iterator([message].iter().copied(), &self.parameters)
.next()
.unwrap();
// This allocates the required ct
let mut encrypted_ct = LweCiphertextOwned::new(
0u64,
self.key.lwe_dimension().to_lwe_size(),
self.parameters.ciphertext_modulus(),
);
ShortintEngine::with_thread_local_mut(|engine| {
encrypt_lwe_ciphertext_with_compact_public_key(
&self.key,
&mut encrypted_ct,
plain,
self.parameters.glwe_modular_std_dev(),
self.parameters.lwe_modular_std_dev(),
&mut engine.secret_generator,
&mut engine.encryption_generator,
);
});
let message_modulus = self.parameters.message_modulus();
CiphertextBase {
ct: encrypted_ct,
degree: Degree(message_modulus.0 - 1),
message_modulus,
carry_modulus: self.parameters.carry_modulus(),
_order_marker: Default::default(),
}
}
pub fn encrypt_slice(&self, messages: &[u64]) -> CompactCiphertextList<OpOrder> {
self.encrypt_iter(messages.iter().copied())
}
pub fn encrypt_iter(
&self,
messages: impl Iterator<Item = u64>,
) -> CompactCiphertextList<OpOrder> {
let plaintext_container = to_plaintext_iterator(messages, &self.parameters)
.map(|plaintext| plaintext.0)
.collect::<Vec<_>>();
let plaintext_list = PlaintextList::from_container(plaintext_container);
let mut ct_list = LweCompactCiphertextListOwned::new(
0u64,
self.key.lwe_dimension().to_lwe_size(),
LweCiphertextCount(plaintext_list.plaintext_count().0),
self.parameters.ciphertext_modulus(),
);
// No parallelism allowed
#[cfg(all(feature = "__wasm_api", not(feature = "parallel-wasm-api")))]
{
use crate::core_crypto::prelude::encrypt_lwe_compact_ciphertext_list_with_compact_public_key;
ShortintEngine::with_thread_local_mut(|engine| {
encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
&self.key,
&mut ct_list,
&plaintext_list,
self.parameters.glwe_modular_std_dev(),
self.parameters.lwe_modular_std_dev(),
&mut engine.secret_generator,
&mut engine.encryption_generator,
);
});
}
// Parallelism allowed
#[cfg(any(not(feature = "__wasm_api"), feature = "parallel-wasm-api"))]
{
use crate::core_crypto::prelude::par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key;
ShortintEngine::with_thread_local_mut(|engine| {
par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
&self.key,
&mut ct_list,
&plaintext_list,
self.parameters.glwe_modular_std_dev(),
self.parameters.lwe_modular_std_dev(),
&mut engine.secret_generator,
&mut engine.encryption_generator,
);
});
}
let message_modulus = self.parameters.message_modulus();
CompactCiphertextList {
ct_list,
degree: Degree(message_modulus.0 - 1),
message_modulus,
carry_modulus: self.parameters.carry_modulus(),
_order_marker: Default::default(),
}
}
pub fn size_elements(&self) -> usize {
self.key.size_elements()
}
pub fn size_bytes(&self) -> usize {
self.key.size_bytes()
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompressedCompactPublicKeyBase<OpOrder: PBSOrderMarker> {
pub(crate) key: SeededLweCompactPublicKeyOwned<u64>,
pub parameters: ShortintParameterSet,
pub _order_marker: std::marker::PhantomData<OpOrder>,
}
pub type CompressedCompactPublicKeyBig = CompressedCompactPublicKeyBase<KeyswitchBootstrap>;
pub type CompressedCompactPublicKeySmall = CompressedCompactPublicKeyBase<BootstrapKeyswitch>;
impl<OpOrder: PBSOrderMarker> CompressedCompactPublicKeyBase<OpOrder> {
pub fn new(client_key: &ClientKey) -> Self {
let parameters = client_key.parameters;
let (secret_encryption_key, encryption_noise) = match OpOrder::pbs_order() {
crate::shortint::PBSOrder::KeyswitchBootstrap => (
&client_key.large_lwe_secret_key,
parameters.glwe_modular_std_dev(),
),
crate::shortint::PBSOrder::BootstrapKeyswitch => (
&client_key.small_lwe_secret_key,
parameters.lwe_modular_std_dev(),
),
};
let key = ShortintEngine::with_thread_local_mut(|engine| {
allocate_and_generate_new_seeded_lwe_compact_public_key(
secret_encryption_key,
encryption_noise,
parameters.ciphertext_modulus(),
&mut engine.seeder,
)
});
Self {
key,
parameters,
_order_marker: std::marker::PhantomData,
}
}
pub fn decompress(self) -> CompactPublicKeyBase<OpOrder> {
let decompressed_key = self.key.decompress_into_lwe_compact_public_key();
CompactPublicKeyBase {
key: decompressed_key,
parameters: self.parameters,
_order_marker: self._order_marker,
}
}
}
impl<OpOrder: PBSOrderMarker> From<CompressedCompactPublicKeyBase<OpOrder>>
for CompactPublicKeyBase<OpOrder>
{
fn from(value: CompressedCompactPublicKeyBase<OpOrder>) -> Self {
value.decompress()
}
}

View File

@@ -1,7 +1,12 @@
//! Module with the definition of the encryption PublicKey.
pub mod compact;
pub mod compressed;
pub mod standard;
pub use compact::{
CompactPublicKeyBase, CompactPublicKeyBig, CompactPublicKeySmall,
CompressedCompactPublicKeyBase, CompressedCompactPublicKeyBig, CompressedCompactPublicKeySmall,
};
pub use compressed::{CompressedPublicKeyBase, CompressedPublicKeyBig, CompressedPublicKeySmall};
pub use standard::{PublicKeyBase, PublicKeyBig, PublicKeySmall};

View File

@@ -911,7 +911,7 @@ impl ServerKey {
}
pub fn key_switching_key_size_bytes(&self) -> usize {
self.key_switching_key_size_elements() * std::mem::size_of::<u64>()
std::mem::size_of_val(self.key_switching_key.as_ref())
}
}

View File

@@ -2879,3 +2879,133 @@ where
println!("(msg_true - msg_false) * control_bit + msg_false = {clear_mux}, res = {dec_res}");
assert_eq!(clear_mux, dec_res);
}
#[test]
fn test_shortint_compact_public_key_smart_add_big() {
test_shortint_compact_public_key_base_smart_add::<
crate::shortint::ciphertext::KeyswitchBootstrap,
>(PARAM_MESSAGE_2_CARRY_2_COMPACT_PK);
}
#[test]
fn test_shortint_compact_public_key_smart_add_small() {
test_shortint_compact_public_key_base_smart_add::<
crate::shortint::ciphertext::BootstrapKeyswitch,
>(PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
}
fn test_shortint_compact_public_key_base_smart_add<OpOrder: crate::shortint::PBSOrderMarker>(
params: ClassicPBSParameters,
) {
let (cks, sks) = crate::shortint::gen_keys(params);
let pk = crate::shortint::CompactPublicKeyBase::<OpOrder>::new(&cks);
let mut rng = rand::thread_rng();
let modulus = cks.parameters.message_modulus().0 as u64;
for _ in 0..NB_TEST {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;
let mut ctxt_0 = pk.encrypt(clear_0);
let ctxt_1 = pk.encrypt(clear_1);
let d = cks.decrypt(&ctxt_0);
assert_eq!(d, clear_0);
let d = cks.decrypt(&ctxt_1);
assert_eq!(d, clear_1);
let mut ct_res = sks.unchecked_add(&ctxt_0, &ctxt_1);
let mut clear = clear_0 + clear_1;
let d = cks.decrypt(&ct_res);
assert_eq!(d, clear % modulus);
//add multiple times to raise the degree and test the smart operation
for _ in 0..40 {
sks.smart_add_assign(&mut ct_res, &mut ctxt_0);
clear += clear_0;
// decryption of ct_res
let dec_res = cks.decrypt(&ct_res);
// assert
assert_eq!(clear % modulus, dec_res);
}
}
}
#[test]
fn test_shortint_compact_public_key_list_smart_sub_big() {
test_shortint_compact_public_key_base_list_smart_sub::<
crate::shortint::ciphertext::KeyswitchBootstrap,
>(PARAM_MESSAGE_2_CARRY_2_COMPACT_PK);
}
#[test]
fn test_shortint_compact_public_key_list_smart_sub_small() {
test_shortint_compact_public_key_base_list_smart_sub::<
crate::shortint::ciphertext::BootstrapKeyswitch,
>(PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
}
fn test_shortint_compact_public_key_base_list_smart_sub<
OpOrder: crate::shortint::PBSOrderMarker,
>(
params: ClassicPBSParameters,
) {
let (cks, sks) = crate::shortint::gen_keys(params);
let pk = crate::shortint::CompactPublicKeyBase::<OpOrder>::new(&cks);
let mut rng = rand::thread_rng();
let modulus = cks.parameters.message_modulus().0 as u64;
const MAX_CT: usize = 5;
let mut first_clear_vec = Vec::with_capacity(MAX_CT);
let mut second_clear_vec = Vec::with_capacity(MAX_CT);
for _ in 0..(NB_TEST / 2).min(5) {
let num_ct_for_this_iter = rng.gen_range(1..=MAX_CT);
first_clear_vec.truncate(0);
second_clear_vec.truncate(0);
for _ in 0..num_ct_for_this_iter {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;
first_clear_vec.push(clear_0);
second_clear_vec.push(clear_1);
}
let first_compact_list = pk.encrypt_slice(&first_clear_vec);
let second_compact_list = pk.encrypt_slice(&second_clear_vec);
let mut first_expanded_vec = first_compact_list.expand();
let mut second_expanded_vec = second_compact_list.expand();
// decryption check
for i in 0..num_ct_for_this_iter {
let decrypted_0 = cks.decrypt(&first_expanded_vec[i]);
let decrypted_1 = cks.decrypt(&second_expanded_vec[i]);
assert_eq!(decrypted_0, first_clear_vec[i]);
assert_eq!(decrypted_1, second_clear_vec[i]);
}
for _ in 0..10 {
for i in 0..num_ct_for_this_iter {
sks.smart_sub_assign(&mut first_expanded_vec[i], &mut second_expanded_vec[i]);
first_clear_vec[i] = first_clear_vec[i].wrapping_sub(second_clear_vec[i]);
let decrypted_0 = cks.decrypt(&first_expanded_vec[i]);
let decrypted_1 = cks.decrypt(&second_expanded_vec[i]);
assert_eq!(decrypted_0, first_clear_vec[i] % modulus);
assert_eq!(decrypted_1, second_clear_vec[i] % modulus);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

View File

@@ -21,6 +21,15 @@
<input type="button" id="publicKeyTest" value="Public Key Test" disabled />
<input type="button" id="compressedPublicKeyTest" value="Compressed Public Key Test" 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" value="Compressed Compact Public Key Test 256 Bits Small" disabled />
<input type="button" id="compressedCompactPublicKeyTest256BitBig" value="Compressed Compact Public Key Test 256 Bits Big" disabled />
<input type="checkbox" id="testSuccess" disabled>
<label for="testSuccess"> TestSuccess </label><br>

View File

@@ -24,7 +24,16 @@ async function setup() {
);
const demos = await Comlink.wrap(worker).demos;
const demoNames = ['publicKeyTest', 'compressedPublicKeyTest']
const demoNames = [
'publicKeyTest',
'compressedPublicKeyTest',
'compactPublicKeyTest32BitBig',
'compactPublicKeyTest32BitSmall',
'compactPublicKeyTest256BitBig',
'compactPublicKeyTest256BitSmall',
'compressedCompactPublicKeyTest256BitBig',
'compressedCompactPublicKeyTest256BitSmall'
]
function setupBtn(id) {
// Handlers are named in the same way as buttons.
@@ -32,6 +41,7 @@ async function setup() {
let button = document.getElementById(id);
if (button === null) {
console.error("button with id: ", id , "not found")
return null;
}
@@ -42,11 +52,12 @@ async function setup() {
document.getElementById("testSuccess").checked = false
setButtonsDisabledState(demoNames, true);
console.log("Running: ", id)
try {
await fn()
document.getElementById("testSuccess").checked = true
} catch (error) {
console.error(error)
console.error(`Test Failed: ${error}`)
document.getElementById("testSuccess").checked = false
}
document.getElementById("loader").hidden = true

View File

@@ -5,9 +5,9 @@
"main": "index.js",
"scripts": {
"test": "jest ./test --runInBand",
"build": "cp -r ../../tfhe/pkg ./ && webpack build ./index.js --mode production -o dist --output-filename index.js && cp index.html dist/",
"build": "cp -r ../../tfhe/pkg ./ && webpack build ./index.js --mode production -o dist --output-filename index.js && cp index.html dist/ && cp favicon.ico dist/",
"server": "serve --config ../serve.json dist/",
"test-separate-processes": "jest --listTests | xargs -L 1 jest",
"test-separate-processes": "jest --listTests | xargs -L 1 jest --runInBand",
"test2": "mocha"
},
"author": "",

View File

@@ -12,6 +12,7 @@ async function runActualTest(page, buttonId) {
const testSuccessCheckbox = await page.waitForSelector(
successCheckBoxSelector
);
await page.waitForSelector(buttonSelector)
const isCheckedBefore = await testSuccessCheckbox?.evaluate(el => el.checked);
expect(isCheckedBefore).toBe(false);
@@ -28,7 +29,6 @@ async function runActualTest(page, buttonId) {
}
async function runTestAttachedToButton(buttonId) {
console.log(puppeteer.launch)
let browser
if (isRoot()) {
browser = await puppeteer.launch({
@@ -45,7 +45,7 @@ async function runTestAttachedToButton(buttonId) {
await page.goto('http://localhost:3000');
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
await page.reload({ waitUntil: ["networkidle0", "domcontentloaded"] });
let errorCaught = null;

View File

@@ -0,0 +1,26 @@
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')
});
it('Compressed Compact Public Key Test Big 256 Bit', async () => {
await runTestAttachedToButton('compressedCompactPublicKeyTest256BitBig')
});

View File

@@ -1,7 +0,0 @@
import { runTestAttachedToButton } from "./common.mjs";
it('Public Key Test', async () => {
await runTestAttachedToButton('publicKeyTest')
});

View File

@@ -3,18 +3,26 @@ import * as Comlink from 'comlink';
import init, {
initThreadPool,
init_panic_hook,
ShortintParametersName,
ShortintParameters,
TfheClientKey,
TfhePublicKey,
TfheCompressedPublicKey,
TfheCompressedServerKey,
TfheCompressedCompactPublicKey,
TfheCompactPublicKey,
TfheConfigBuilder,
CompressedFheUint8,
FheUint8,
FheUint32,
CompactFheUint32,
CompactFheUint32List,
CompressedFheUint128,
FheUint128,
CompressedFheUint256,
FheUint256
FheUint256,
CompactFheUint256,
CompactFheUint256List,
} from "./pkg/tfhe.js";
function assert(cond, text){
@@ -23,6 +31,12 @@ function assert(cond, text){
throw new Error(text || "Assertion failed!");
};
function assert_eq(a, b, text){
if( a === b ) return;
if( console.assert.useDebugger ) debugger;
throw new Error(text || `Equality assertion failed!: ${a} != ${b}`);
};
async function compressedPublicKeyTest() {
let config = TfheConfigBuilder.all_disabled()
.enable_default_integers_small()
@@ -51,7 +65,7 @@ async function compressedPublicKeyTest() {
console.log("Ciphertext Size", ser.length);
let decrypted = encrypted.decrypt(clientKey);
assert(decrypted === 255)
assert_eq(decrypted, 255)
}
async function publicKeyTest() {
@@ -75,9 +89,204 @@ async function publicKeyTest() {
console.log("Ciphertext Size", ser.length);
let decrypted = encrypted.decrypt(clientKey);
assert(decrypted === 255)
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);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(block_params)
.build();
await compactPublicKeyTest32BitOnConfig(config)
}
async function compactPublicKeyTest32BitSmall() {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(block_params)
.build();
await compactPublicKeyTest32BitOnConfig(config)
}
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]);
}
}
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);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(block_params)
.build();
await compactPublicKeyTest256BitOnConfig(config)
}
async function compactPublicKeyTest256BitSmall() {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(block_params)
.build();
await compactPublicKeyTest256BitOnConfig(config)
}
async function compressedCompactPublicKeyTest256BitOnConfig(config) {
console.time('ClientKey Gen')
let clientKey = TfheClientKey.generate(config);
console.timeEnd('ClientKey Gen')
console.time('CompressedCompactPublicKey Gen')
let publicKey = TfheCompressedCompactPublicKey.new(clientKey);
console.timeEnd('CompressedCompactPublicKey Gen')
let serialized_pk = publicKey.serialize();
console.log("Serialized CompressedCompactPublicKey size: ", serialized_pk.length);
console.time('CompressedCompactPublicKey Decompression')
publicKey = publicKey.decompress()
console.timeEnd('CompressedCompactPublicKey Decompression')
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]);
}
}
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 compressedCompactPublicKeyTest256BitBig() {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(block_params)
.build();
await compressedCompactPublicKeyTest256BitOnConfig(config)
}
async function compressedCompactPublicKeyTest256BitSmall() {
const block_params = new ShortintParameters(ShortintParametersName.PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_custom_integers(block_params)
.build();
await compressedCompactPublicKeyTest256BitOnConfig(config)
}
async function main() {
await init()
await initThreadPool(navigator.hardwareConcurrency);
@@ -85,7 +294,13 @@ async function main() {
return Comlink.proxy({
publicKeyTest,
compressedPublicKeyTest
compressedPublicKeyTest,
compactPublicKeyTest32BitSmall,
compactPublicKeyTest32BitBig,
compactPublicKeyTest256BitSmall,
compactPublicKeyTest256BitBig,
compressedCompactPublicKeyTest256BitSmall,
compressedCompactPublicKeyTest256BitBig,
})
}