mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ target/
|
||||
|
||||
# Some of our bench outputs
|
||||
/tfhe/benchmarks_parameters
|
||||
/tfhe/shortint_key_sizes.csv
|
||||
|
||||
18
Makefile
18
Makefile
@@ -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:
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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;
|
||||
U128 lhs_clear = {10, 20};
|
||||
U128 rhs_clear = {1, 2};
|
||||
U128 result_clear = {0};
|
||||
|
||||
ok = fhe_uint128_try_encrypt_trivial_u128(10, 20, &lhs);
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
213
tfhe/c_api_tests/test_high_level_custom_integers.c
Normal file
213
tfhe/c_api_tests/test_high_level_custom_integers.c
Normal 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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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, ¶ms);
|
||||
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, ¶ms);
|
||||
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, ¶ms);
|
||||
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, ¶ms);
|
||||
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, ¶ms);
|
||||
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);
|
||||
|
||||
@@ -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, ¶ms);
|
||||
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, ¶ms);
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
92
tfhe/examples/integer_compact_pk_ct_sizes.rs
Normal file
92
tfhe/examples/integer_compact_pk_ct_sizes.rs
Normal 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>();
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
)));
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
20
tfhe/src/c_api/high_level_api/u128.rs
Normal file
20
tfhe/src/c_api/high_level_api/u128.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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! {
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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`).
|
||||
|
||||
334
tfhe/src/core_crypto/entities/lwe_compact_ciphertext_list.rs
Normal file
334
tfhe/src/core_crypto/entities/lwe_compact_ciphertext_list.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
187
tfhe/src/core_crypto/entities/lwe_compact_public_key.rs
Normal file
187
tfhe/src/core_crypto/entities/lwe_compact_public_key.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
214
tfhe/src/core_crypto/entities/seeded_lwe_compact_public_key.rs
Normal file
214
tfhe/src/core_crypto/entities/seeded_lwe_compact_public_key.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>,
|
||||
|
||||
119
tfhe/src/high_level_api/integers/types/compact.rs
Normal file
119
tfhe/src/high_level_api/integers/types/compact.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
154
tfhe/src/integer/public_key/compact.rs
Normal file
154
tfhe/src/integer/public_key/compact.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
255
tfhe/src/shortint/public_key/compact.rs
Normal file
255
tfhe/src/shortint/public_key/compact.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
tfhe/web_wasm_parallel_tests/favicon.ico
Normal file
BIN
tfhe/web_wasm_parallel_tests/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 252 KiB |
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
@@ -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({
|
||||
|
||||
26
tfhe/web_wasm_parallel_tests/test/compact-public-key.test.js
Normal file
26
tfhe/web_wasm_parallel_tests/test/compact-public-key.test.js
Normal 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')
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
import { runTestAttachedToButton } from "./common.mjs";
|
||||
|
||||
|
||||
it('Public Key Test', async () => {
|
||||
await runTestAttachedToButton('publicKeyTest')
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user