feat(tfhe): plug zk-pok into all layers

This commit is contained in:
tmontaigu
2024-04-08 10:00:36 +02:00
committed by Arthur Meyre
parent f868bb2397
commit 2c106e8f01
73 changed files with 4490 additions and 336 deletions

View File

@@ -175,10 +175,18 @@ fmt_gpu: install_rs_check_toolchain
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt
cd "$(TFHECUDA_SRC)" && ./format_tfhe_cuda_backend.sh cd "$(TFHECUDA_SRC)" && ./format_tfhe_cuda_backend.sh
.PHONY: fmt_c_tests # Format c tests
fmt_c_tests:
find tfhe/c_api_tests/ -regex '.*\.\(cpp\|hpp\|cu\|c\|h\)' -exec clang-format -style=file -i {} \;
.PHONY: check_fmt # Check rust code format .PHONY: check_fmt # Check rust code format
check_fmt: install_rs_check_toolchain check_fmt: install_rs_check_toolchain
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt --check cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt --check
.PHONY: check_fmt_c_tests # Check C tests format
check_fmt_c_tests:
find tfhe/c_api_tests/ -regex '.*\.\(cpp\|hpp\|cu\|c\|h\)' -exec clang-format --dry-run --Werror -style=file {} \;
.PHONY: check_fmt_gpu # Check rust and cuda code format .PHONY: check_fmt_gpu # Check rust and cuda code format
check_fmt_gpu: install_rs_check_toolchain check_fmt_gpu: install_rs_check_toolchain
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt --check cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt --check
@@ -274,7 +282,7 @@ clippy_trivium: install_rs_check_toolchain
.PHONY: clippy_all_targets # Run clippy lints on all targets (benches, examples, etc.) .PHONY: clippy_all_targets # Run clippy lints on all targets (benches, examples, etc.)
clippy_all_targets: clippy_all_targets:
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \ RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache \ --features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,zk-pok-experimental \
-p $(TFHE_SPEC) -- --no-deps -D warnings -p $(TFHE_SPEC) -- --no-deps -D warnings
.PHONY: clippy_concrete_csprng # Run clippy lints on concrete-csprng .PHONY: clippy_concrete_csprng # Run clippy lints on concrete-csprng
@@ -353,14 +361,14 @@ symlink_c_libs_without_fingerprint:
.PHONY: build_c_api # Build the C API for boolean, shortint and integer .PHONY: build_c_api # Build the C API for boolean, shortint and integer
build_c_api: install_rs_check_toolchain build_c_api: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) build --profile $(CARGO_PROFILE) \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) build --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api,$(FORWARD_COMPAT_FEATURE) \ --features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api,zk-pok-experimental,$(FORWARD_COMPAT_FEATURE) \
-p $(TFHE_SPEC) -p $(TFHE_SPEC)
@"$(MAKE)" symlink_c_libs_without_fingerprint @"$(MAKE)" symlink_c_libs_without_fingerprint
.PHONY: build_c_api_gpu # Build the C API for boolean, shortint and integer .PHONY: build_c_api_gpu # Build the C API for boolean, shortint and integer
build_c_api_gpu: install_rs_check_toolchain build_c_api_gpu: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) build --profile $(CARGO_PROFILE) \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) build --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api,gpu \ --features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api,zk-pok-experimental,gpu \
-p $(TFHE_SPEC) -p $(TFHE_SPEC)
@"$(MAKE)" symlink_c_libs_without_fingerprint @"$(MAKE)" symlink_c_libs_without_fingerprint
@@ -376,7 +384,7 @@ build_web_js_api: install_rs_build_toolchain install_wasm_pack
cd tfhe && \ cd tfhe && \
RUSTFLAGS="$(WASM_RUSTFLAGS)" rustup run "$(RS_BUILD_TOOLCHAIN)" \ RUSTFLAGS="$(WASM_RUSTFLAGS)" rustup run "$(RS_BUILD_TOOLCHAIN)" \
wasm-pack build --release --target=web \ wasm-pack build --release --target=web \
-- --features=boolean-client-js-wasm-api,shortint-client-js-wasm-api,integer-client-js-wasm-api -- --features=boolean-client-js-wasm-api,shortint-client-js-wasm-api,integer-client-js-wasm-api,zk-pok-experimental
.PHONY: build_web_js_api_parallel # Build the js API targeting the web browser with parallelism support .PHONY: build_web_js_api_parallel # Build the js API targeting the web browser with parallelism support
build_web_js_api_parallel: install_rs_check_toolchain install_wasm_pack build_web_js_api_parallel: install_rs_check_toolchain install_wasm_pack
@@ -384,7 +392,7 @@ build_web_js_api_parallel: install_rs_check_toolchain install_wasm_pack
rustup component add rust-src --toolchain $(RS_CHECK_TOOLCHAIN) && \ rustup component add rust-src --toolchain $(RS_CHECK_TOOLCHAIN) && \
RUSTFLAGS="$(WASM_RUSTFLAGS) -C target-feature=+atomics,+bulk-memory,+mutable-globals" rustup run $(RS_CHECK_TOOLCHAIN) \ RUSTFLAGS="$(WASM_RUSTFLAGS) -C target-feature=+atomics,+bulk-memory,+mutable-globals" rustup run $(RS_CHECK_TOOLCHAIN) \
wasm-pack build --release --target=web \ wasm-pack build --release --target=web \
-- --features=boolean-client-js-wasm-api,shortint-client-js-wasm-api,integer-client-js-wasm-api,parallel-wasm-api \ -- --features=boolean-client-js-wasm-api,shortint-client-js-wasm-api,integer-client-js-wasm-api,parallel-wasm-api,zk-pok-experimental \
-Z build-std=panic_abort,std -Z build-std=panic_abort,std
.PHONY: build_node_js_api # Build the js API targeting nodejs .PHONY: build_node_js_api # Build the js API targeting nodejs
@@ -392,7 +400,7 @@ build_node_js_api: install_rs_build_toolchain install_wasm_pack
cd tfhe && \ cd tfhe && \
RUSTFLAGS="$(WASM_RUSTFLAGS)" rustup run "$(RS_BUILD_TOOLCHAIN)" \ RUSTFLAGS="$(WASM_RUSTFLAGS)" rustup run "$(RS_BUILD_TOOLCHAIN)" \
wasm-pack build --release --target=nodejs \ wasm-pack build --release --target=nodejs \
-- --features=boolean-client-js-wasm-api,shortint-client-js-wasm-api,integer-client-js-wasm-api -- --features=boolean-client-js-wasm-api,shortint-client-js-wasm-api,integer-client-js-wasm-api,zk-pok-experimental
.PHONY: build_concrete_csprng # Build concrete_csprng .PHONY: build_concrete_csprng # Build concrete_csprng
build_concrete_csprng: install_rs_build_toolchain build_concrete_csprng: install_rs_build_toolchain
@@ -402,10 +410,10 @@ build_concrete_csprng: install_rs_build_toolchain
.PHONY: test_core_crypto # Run the tests of the core_crypto module including experimental ones .PHONY: test_core_crypto # Run the tests of the core_crypto module including experimental ones
test_core_crypto: install_rs_build_toolchain install_rs_check_toolchain test_core_crypto: install_rs_build_toolchain install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),experimental -p $(TFHE_SPEC) -- core_crypto:: --features=$(TARGET_ARCH_FEATURE),experimental,zk-pok-experimental -p $(TFHE_SPEC) -- core_crypto::
@if [[ "$(AVX512_SUPPORT)" == "ON" ]]; then \ @if [[ "$(AVX512_SUPPORT)" == "ON" ]]; then \
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) test --profile $(CARGO_PROFILE) \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),experimental,$(AVX512_FEATURE) -p $(TFHE_SPEC) -- core_crypto::; \ --features=$(TARGET_ARCH_FEATURE),experimental,zk-pok-experimental,$(AVX512_FEATURE) -p $(TFHE_SPEC) -- core_crypto::; \
fi fi
.PHONY: test_core_crypto_cov # Run the tests of the core_crypto module with code coverage .PHONY: test_core_crypto_cov # Run the tests of the core_crypto module with code coverage
@@ -576,7 +584,7 @@ test_integer_cov: install_rs_check_toolchain install_tarpaulin
.PHONY: test_high_level_api # Run all the tests for high_level_api .PHONY: test_high_level_api # Run all the tests for high_level_api
test_high_level_api: install_rs_build_toolchain test_high_level_api: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache -p $(TFHE_SPEC) \ --features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,zk-pok-experimental -p $(TFHE_SPEC) \
-- high_level_api:: -- high_level_api::
test_high_level_api_gpu: install_rs_build_toolchain install_cargo_nextest test_high_level_api_gpu: install_rs_build_toolchain install_cargo_nextest
@@ -587,14 +595,14 @@ test_high_level_api_gpu: install_rs_build_toolchain install_cargo_nextest
.PHONY: test_user_doc # Run tests from the .md documentation .PHONY: test_user_doc # Run tests from the .md documentation
test_user_doc: install_rs_build_toolchain test_user_doc: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) --doc \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) --doc \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,pbs-stats \ --features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,pbs-stats,zk-pok-experimental \
-p $(TFHE_SPEC) \ -p $(TFHE_SPEC) \
-- test_user_docs:: -- test_user_docs::
.PHONY: test_user_doc_gpu # Run tests for GPU from the .md documentation .PHONY: test_user_doc_gpu # Run tests for GPU from the .md documentation
test_user_doc_gpu: install_rs_build_toolchain test_user_doc_gpu: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) --doc \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) --doc \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,gpu -p $(TFHE_SPEC) \ --features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,gpu,zk-pok-experimental -p $(TFHE_SPEC) \
-- test_user_docs:: -- test_user_docs::
.PHONY: test_fhe_strings # Run tests for fhe_strings example .PHONY: test_fhe_strings # Run tests for fhe_strings example
@@ -633,7 +641,7 @@ test_concrete_csprng:
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE) -p concrete-csprng --features=$(TARGET_ARCH_FEATURE) -p concrete-csprng
.PHONY: test_zk_pok # Run tfhe-zk-pok tests .PHONY: test_zk_pok # Run tfhe-zk-pok-experimental tests
test_zk_pok: test_zk_pok:
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \ RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
-p tfhe-zk-pok -p tfhe-zk-pok

View File

@@ -162,7 +162,7 @@ cargo "${RUST_TOOLCHAIN}" nextest run \
--cargo-profile "${cargo_profile}" \ --cargo-profile "${cargo_profile}" \
--package "${tfhe_package}" \ --package "${tfhe_package}" \
--profile ci \ --profile ci \
--features="${ARCH_FEATURE}",integer,internal-keycache,"${avx512_feature}" \ --features="${ARCH_FEATURE}",integer,internal-keycache,zk-pok-experimental,"${avx512_feature}" \
--test-threads "${test_threads}" \ --test-threads "${test_threads}" \
-E "$filter_expression" -E "$filter_expression"

View File

@@ -120,7 +120,7 @@ and not test(~smart_add_and_mul)""" # This test is too slow
--cargo-profile "${cargo_profile}" \ --cargo-profile "${cargo_profile}" \
--package "${tfhe_package}" \ --package "${tfhe_package}" \
--profile ci \ --profile ci \
--features="${ARCH_FEATURE}",shortint,internal-keycache \ --features="${ARCH_FEATURE}",shortint,internal-keycache,zk-pok-experimental \
--test-threads "${n_threads_small}" \ --test-threads "${n_threads_small}" \
-E "${filter_expression_small_params}" -E "${filter_expression_small_params}"

View File

@@ -51,12 +51,6 @@ impl<G: Curve> PublicParams<G> {
} }
} }
#[allow(dead_code)]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct PrivateParams<G: Curve> {
alpha: G::Zp,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Proof<G: Curve> { pub struct Proof<G: Curve> {
c_hat: G::G2, c_hat: G::G2,
@@ -104,27 +98,24 @@ pub fn crs_gen<G: Curve>(
q: u64, q: u64,
t: u64, t: u64,
rng: &mut dyn RngCore, rng: &mut dyn RngCore,
) -> (PublicParams<G>, PrivateParams<G>) { ) -> PublicParams<G> {
let alpha = G::Zp::rand(rng); let alpha = G::Zp::rand(rng);
let b_r = d as u64 / 2 + 1; let b_r = d as u64 / 2 + 1;
let big_d = let big_d =
d + k * t.ilog2() as usize + (d + k) * (2 + b.ilog2() as usize + b_r.ilog2() as usize); d + k * t.ilog2() as usize + (d + k) * (2 + b.ilog2() as usize + b_r.ilog2() as usize);
let n = big_d + 1; let n = big_d + 1;
( PublicParams {
PublicParams { g_lists: GroupElements::<G>::new(n, alpha),
g_lists: GroupElements::<G>::new(n, alpha), big_d,
big_d, n,
n, d,
d, k,
k, b,
b, b_r,
b_r, q,
q, t,
t, }
},
PrivateParams { alpha },
)
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -990,8 +981,7 @@ mod tests {
m_roundtrip[i] = result; m_roundtrip[i] = result;
} }
let (public_param, _private_param) = let public_param = crs_gen::<crate::curve_api::Bls12_446>(d, k, b_i, q, t, rng);
crs_gen::<crate::curve_api::Bls12_446>(d, k, b_i, q, t, rng);
for use_fake_e1 in [false, true] { for use_fake_e1 in [false, true] {
for use_fake_e2 in [false, true] { for use_fake_e2 in [false, true] {

View File

@@ -69,6 +69,8 @@ paste = "1.0.7"
fs2 = { version = "0.4.3", optional = true } fs2 = { version = "0.4.3", optional = true }
# While we wait for repeat_n in rust standard library # While we wait for repeat_n in rust standard library
itertools = "0.11.0" itertools = "0.11.0"
rand_core = { version = "0.6.4", features = ["std"] }
tfhe-zk-pok = { version = "0.1.0", path = "../tfhe-zk-pok", optional = true }
# wasm deps # wasm deps
wasm-bindgen = { version = "0.2.86", features = [ wasm-bindgen = { version = "0.2.86", features = [
@@ -87,6 +89,7 @@ shortint = []
integer = ["shortint"] integer = ["shortint"]
internal-keycache = ["dep:lazy_static", "dep:fs2"] internal-keycache = ["dep:lazy_static", "dep:fs2"]
gpu = ["tfhe-cuda-backend"] gpu = ["tfhe-cuda-backend"]
zk-pok-experimental = ["dep:tfhe-zk-pok"]
pbs-stats = [] pbs-stats = []

View File

@@ -57,6 +57,8 @@ fn gen_c_api() {
"integer", "integer",
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
"gpu", "gpu",
#[cfg(feature = "zk-pok-experimental")]
"zk-pok-experimental",
]; ];
let parse_expand_vec = if parse_expand_features_vec.is_empty() { let parse_expand_vec = if parse_expand_features_vec.is_empty() {

View File

@@ -0,0 +1,109 @@
#include "tfhe.h"
#include <assert.h>
#include <stdlib.h>
#include <tfhe.h>
int main(void) {
// We want to use zk-proof, which requires bounded random distributions
// tfhe-rs has the `TUniform` as an available bounded distribution.
// Note that simply changing parameters like this does not yield secure parameters
// Its only done for the example / tests
ShortintPBSParameters params = SHORTINT_PARAM_MESSAGE_2_CARRY_2_KS_PBS;
params.glwe_noise_distribution = new_t_uniform(9);
assert(params.encryption_key_choice == ShortintEncryptionKeyChoiceBig);
int status;
ConfigBuilder *builder;
status = config_builder_default(&builder);
assert(status == 0);
status = config_builder_use_custom_parameters(&builder, params);
assert(status == 0);
Config *config;
status = config_builder_build(builder, &config);
assert(status == 0);
// Compute the CRS
// Note that we do that before generating the client key
// as client_key_generate thakes ownership of the config
CompactPkeCrs *crs;
size_t max_num_bits = 32;
status = compact_pke_crs_from_config(config, max_num_bits, &crs);
assert(status == 0);
CompactPkePublicParams *public_params;
status = compact_pke_crs_public_params(crs, &public_params);
assert(status == 0);
ClientKey *client_key;
status = client_key_generate(config, &client_key);
assert(status == 0);
// zk proofs of encryption works only using the CompactPublicKey
CompactPublicKey *pk;
status = compact_public_key_new(client_key, &pk);
assert(status == 0);
// Demo of ProvenCompactFheUint32
{
uint32_t msg = 8328937;
ProvenCompactFheUint32 *proven_fhe_uint;
status = proven_compact_fhe_uint32_try_encrypt(msg, public_params, pk, ZkComputeLoadProof,
&proven_fhe_uint);
assert(status == 0);
FheUint32 *fhe_uint;
// This function does not take ownership of the proven fhe uint, so we have to cleanup later
status =
proven_compact_fhe_uint32_verify_and_expand(proven_fhe_uint, public_params, pk, &fhe_uint);
assert(status == 0);
uint32_t decrypted;
status = fhe_uint32_decrypt(fhe_uint, client_key, &decrypted);
assert(status == 0);
assert(decrypted == msg);
fhe_uint32_destroy(fhe_uint);
proven_compact_fhe_uint32_destroy(proven_fhe_uint);
}
// Demo of ProvenCompactFheUint32List
{
uint32_t msgs[4] = {8328937, 217521191, 2753219039, 91099540};
ProvenCompactFheUint32List *proven_fhe_list;
status = proven_compact_fhe_uint32_list_try_encrypt(msgs, 4, public_params, pk,
ZkComputeLoadProof, &proven_fhe_list);
assert(status == 0);
size_t list_len;
status = proven_compact_fhe_uint32_list_len(proven_fhe_list, &list_len);
assert(status == 0);
assert(list_len == 4);
FheUint32 *fhe_uints[4];
// This function does not take ownership of the proven fhe uint, so we have to cleanup later
status = proven_compact_fhe_uint32_list_verify_and_expand(proven_fhe_list, public_params, pk,
&fhe_uints[0], 4);
assert(status == 0);
for (size_t i = 0; i < 4; ++i) {
uint32_t decrypted;
status = fhe_uint32_decrypt(fhe_uints[i], client_key, &decrypted);
assert(status == 0);
assert(decrypted == msgs[i]);
fhe_uint32_destroy(fhe_uints[i]);
}
proven_compact_fhe_uint32_list_destroy(proven_fhe_list);
}
compact_pke_public_params_destroy(public_params);
compact_pke_crs_destroy(crs);
compact_public_key_destroy(pk);
client_key_destroy(client_key);
return EXIT_SUCCESS;
}

View File

@@ -28,6 +28,8 @@ fn main() {
This example shows how to use compact public keys. The main difference is in the ConfigBuilder, where the parameter set has been changed. This example shows how to use compact public keys. The main difference is in the ConfigBuilder, where the parameter set has been changed.
See [the guide on ZK proofs](zk-pok.md) to see how to encrypt data using compact public keys and generate a zero knowledge proof of correct encryption at the same time.
```rust ```rust
use tfhe::prelude::*; use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8, CompactPublicKey}; use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8, CompactPublicKey};

View File

@@ -0,0 +1,69 @@
# Zero Knowledge proof for Compact Public Key encryption
TFHE-rs enables the generation of a zero-knowledge proof to verify that a compact public key encryption process has been correctly performed. In other words, the creation of a proof reveals nothing about the encrypted message, except for its already known range. This technique is derived from [Liberts work](https://eprint.iacr.org/2023/800).
{% hint style="info" %}
You can enable this feature using the flag: `--features=zk-pok-experimental` when building TFHE-rs.
{% endhint %}
Deploying this feature is straightforward: the client generates the proof at the time of encryption, while the server verifies it before proceeding with homomorphic computations. Below is an example demonstrating how a client can encrypt and prove a ciphertext, and how a server can verify the ciphertext and carry out computations on it:
```rust
use rand::prelude::*;
use tfhe::prelude::FheDecrypt;
use tfhe::shortint::parameters::DynamicDistribution;
use tfhe::set_server_key;
use tfhe::zk::{CompactPkeCrs, ZkComputeLoad};
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut rng = thread_rng();
let max_num_message = 1;
let mut params = tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS;
params.glwe_noise_distribution = DynamicDistribution::new_t_uniform(9);
let client_key = tfhe::ClientKey::generate(tfhe::ConfigBuilder::with_custom_parameters(params, None));
// This is done in an offline phase and the CRS is shared to all clients and the server
let crs = CompactPkeCrs::from_shortint_params(params, max_num_message).unwrap();
let public_zk_params = crs.public_params();
let server_key = tfhe::ServerKey::new(&client_key);
let public_key = tfhe::CompactPublicKey::try_new(&client_key).unwrap();
let clear_a = rng.gen::<u64>();
let clear_b = rng.gen::<u64>();
let a = tfhe::ProvenCompactFheUint64::try_encrypt(
clear_a,
public_zk_params,
&public_key,
ZkComputeLoad::Proof,
)?;
let b = tfhe::ProvenCompactFheUint64::try_encrypt(
clear_b,
public_zk_params,
&public_key,
ZkComputeLoad::Proof,
)?;
// Server side
let result = {
set_server_key(server_key);
// Verify the ciphertexts
let a = a.verify_and_expand(&public_zk_params, &public_key)?;
let b = b.verify_and_expand(&public_zk_params, &public_key)?;
a + b
};
// Back on the client side
let a_plus_b: u64 = result.decrypt(&client_key);
assert_eq!(a_plus_b, clear_a.wrapping_add(clear_b));
Ok(())
}
```
Encrypting and proving a CompactFheUint64 takes 6.9 s on a Dell XPS 15 9500, simulating a client machine, the verification on an hpc7a.96xlarge available on AWS takes 123 ms.

View File

@@ -3,6 +3,7 @@ const assert = require('node:assert').strict;
const {performance} = require('perf_hooks'); const {performance} = require('perf_hooks');
const { const {
init_panic_hook, init_panic_hook,
Shortint,
ShortintParametersName, ShortintParametersName,
ShortintParameters, ShortintParameters,
TfheClientKey, TfheClientKey,
@@ -21,9 +22,15 @@ const {
CompressedFheUint256, CompressedFheUint256,
CompactFheUint256, CompactFheUint256,
CompactFheUint256List, CompactFheUint256List,
ProvenCompactFheUint64,
ProvenCompactFheUint64List,
CompactPkeCrs,
ZkComputeLoad,
FheUint256 FheUint256
} = require("../pkg/tfhe.js"); } = require("../pkg/tfhe.js");
const {
randomBytes,
} = require('node:crypto');
const U256_MAX = BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935"); const U256_MAX = BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935");
const U128_MAX = BigInt("340282366920938463463374607431768211455"); const U128_MAX = BigInt("340282366920938463463374607431768211455");
@@ -639,3 +646,50 @@ test('hlapi_compact_public_key_encrypt_decrypt_uint256_big_list_compact', (t) =>
hlapi_compact_public_key_encrypt_decrypt_uint256_list_compact(config); hlapi_compact_public_key_encrypt_decrypt_uint256_list_compact(config);
}); });
function generateRandomBigInt(bitLength) {
const bytesNeeded = Math.ceil(bitLength / 8);
const randomBytesBuffer = randomBytes(bytesNeeded);
// Convert random bytes to BigInt
const randomBigInt = BigInt(`0x${randomBytesBuffer.toString('hex')}`);
return randomBigInt;
}
test('hlapi_compact_public_key_encrypt_and_prove_compact_uint256', (t) => {
let block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
block_params.set_lwe_noise_distribution(Shortint.try_new_t_uniform(9));
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
let crs = CompactPkeCrs.from_parameters(block_params, 128);
let public_params = crs.public_params();
{
let input = generateRandomBigInt(64)
let encrypted = ProvenCompactFheUint64.encrypt_with_compact_public_key(
input, public_params, publicKey, ZkComputeLoad.Proof);
assert.deepStrictEqual(encrypted.verifies(public_params, publicKey), true);
let expanded = encrypted.verify_and_expand(public_params, publicKey);
let decrypted = expanded.decrypt(clientKey);
assert.deepStrictEqual(decrypted, input);
}
{
let inputs = [generateRandomBigInt(64), generateRandomBigInt(64), generateRandomBigInt(64), generateRandomBigInt(64)];
let encrypted = ProvenCompactFheUint64List.encrypt_with_compact_public_key(
inputs, public_params, publicKey, ZkComputeLoad.Proof);
assert.deepStrictEqual(encrypted.verifies(public_params, publicKey), true);
let expanded_list = encrypted.verify_and_expand(public_params, publicKey);
for (let i = 0; i < inputs.length; i++) {
let decrypted = expanded_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, inputs[i]);
}
}
});

View File

@@ -139,3 +139,122 @@ pub unsafe extern "C" fn compact_fhe_bool_list_expand(
} }
}) })
} }
#[cfg(feature = "zk-pok-experimental")]
mod zk {
use crate::c_api::high_level_api::utils::{
impl_clone_on_type, impl_destroy_on_type, impl_safe_serialize_on_type,
impl_serialize_deserialize_on_type,
};
use std::ffi::c_int;
pub struct ProvenCompactFheBool(crate::high_level_api::ProvenCompactFheBool);
impl_destroy_on_type!(ProvenCompactFheBool);
impl_clone_on_type!(ProvenCompactFheBool);
impl_serialize_deserialize_on_type!(ProvenCompactFheBool);
impl_safe_serialize_on_type!(ProvenCompactFheBool);
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_try_encrypt(
message: bool,
public_params: &crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &crate::c_api::high_level_api::keys::CompactPublicKey,
compute_load: crate::c_api::high_level_api::zk::ZkComputeLoad,
out_result: *mut *mut ProvenCompactFheBool,
) -> c_int {
crate::c_api::utils::catch_panic(|| {
let result = crate::high_level_api::ProvenCompactFheBool::try_encrypt(
message,
&public_params.0,
&pk.0,
compute_load.into(),
)
.unwrap();
*out_result = Box::into_raw(Box::new(ProvenCompactFheBool(result)));
})
}
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_verify_and_expand(
ct: *const ProvenCompactFheBool,
public_params: &crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &crate::c_api::high_level_api::keys::CompactPublicKey,
out_result: *mut *mut super::FheBool,
) -> c_int {
crate::c_api::utils::catch_panic(|| {
let ct = crate::c_api::utils::get_ref_checked(ct).unwrap();
let result =
ct.0.clone()
.verify_and_expand(&public_params.0, &pk.0)
.unwrap();
*out_result = Box::into_raw(Box::new(super::FheBool(result)));
})
}
pub struct ProvenCompactFheBoolList(crate::high_level_api::ProvenCompactFheBoolList);
impl_destroy_on_type!(ProvenCompactFheBoolList);
impl_clone_on_type!(ProvenCompactFheBoolList);
impl_serialize_deserialize_on_type!(ProvenCompactFheBoolList);
impl_safe_serialize_on_type!(ProvenCompactFheBoolList);
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_list_try_encrypt(
input: *const bool,
input_len: usize,
public_params: &crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &crate::c_api::high_level_api::keys::CompactPublicKey,
compute_load: crate::c_api::high_level_api::zk::ZkComputeLoad,
out_result: *mut *mut ProvenCompactFheBoolList,
) -> ::std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
let messages = std::slice::from_raw_parts(input, input_len);
let result = crate::high_level_api::ProvenCompactFheBoolList::try_encrypt(
messages,
&public_params.0,
&pk.0,
compute_load.into(),
)
.unwrap();
*out_result = Box::into_raw(Box::new(ProvenCompactFheBoolList(result)));
})
}
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_list_len(
sself: *const ProvenCompactFheBoolList,
result: *mut usize,
) -> ::std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
let list = crate::c_api::utils::get_ref_checked(sself).unwrap();
*result = list.0.len();
})
}
#[no_mangle]
pub unsafe extern "C" fn proven_compact_fhe_bool_list_verify_and_expand(
list: &ProvenCompactFheBoolList,
public_params: &crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &crate::c_api::high_level_api::keys::CompactPublicKey,
output: *mut *mut super::FheBool,
output_len: usize,
) -> ::std::os::raw::c_int {
crate::c_api::utils::catch_panic(|| {
let expanded = list.0.verify_and_expand(&public_params.0, &pk.0).unwrap();
let num_to_take = output_len.max(list.0.len());
let iter = expanded.into_iter().take(num_to_take).enumerate();
for (i, fhe_uint) in iter {
let ptr = output.wrapping_add(i);
*ptr = Box::into_raw(Box::new(super::FheBool(fhe_uint)));
}
})
}
}

View File

@@ -388,6 +388,7 @@ macro_rules! create_integer_wrapper_type {
}) })
} }
} }
// The compact list version of the ciphertext type // The compact list version of the ciphertext type
::paste::paste! { ::paste::paste! {
pub struct [<Compact $name List>]($crate::high_level_api::[<Compact $name List>]); pub struct [<Compact $name List>]($crate::high_level_api::[<Compact $name List>]);
@@ -434,6 +435,135 @@ macro_rules! create_integer_wrapper_type {
}) })
} }
} }
// The zk compact proven version of the compact ciphertext type
#[cfg(feature = "zk-pok-experimental")]
::paste::paste! {
pub struct [<ProvenCompact $name>]($crate::high_level_api::[<ProvenCompact $name>]);
impl_destroy_on_type!([<ProvenCompact $name>]);
impl_clone_on_type!([<ProvenCompact $name>]);
impl_serialize_deserialize_on_type!([<ProvenCompact $name>]);
impl_safe_serialize_on_type!([<ProvenCompact $name>]);
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _try_encrypt>](
message: $clear_scalar_type,
public_params: &$crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &$crate::c_api::high_level_api::keys::CompactPublicKey,
compute_load: $crate::c_api::high_level_api::zk::ZkComputeLoad,
out_result: *mut *mut [<ProvenCompact $name>],
) -> c_int {
$crate::c_api::utils::catch_panic(|| {
let message = <$clear_scalar_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(message);
let result = $crate::high_level_api::[<ProvenCompact $name>]::try_encrypt(
message,
&public_params.0,
&pk.0,
compute_load.into()
).unwrap();
*out_result = Box::into_raw(Box::new([<ProvenCompact $name>](result)));
})
}
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _verify_and_expand>](
ct: *const [<ProvenCompact $name>],
public_params: &$crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &$crate::c_api::high_level_api::keys::CompactPublicKey,
out_result: *mut *mut $name,
) -> c_int {
$crate::c_api::utils::catch_panic(|| {
let ct = $crate::c_api::utils::get_ref_checked(ct).unwrap();
let result = ct.0.clone().verify_and_expand(&public_params.0, &pk.0).unwrap();
*out_result = Box::into_raw(Box::new($name(result)));
})
}
}
// The zk compact proven version of the compact ciphertext list type
#[cfg(feature = "zk-pok-experimental")]
::paste::paste! {
pub struct [<ProvenCompact $name List>]($crate::high_level_api::[<ProvenCompact $name List>]);
impl_destroy_on_type!([<ProvenCompact $name List>]);
impl_clone_on_type!([<ProvenCompact $name List>]);
impl_serialize_deserialize_on_type!([<ProvenCompact $name List>]);
impl_safe_serialize_on_type!([<ProvenCompact $name List>]);
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _list_try_encrypt>](
input: *const $clear_scalar_type,
input_len: usize,
public_params: &$crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &$crate::c_api::high_level_api::keys::CompactPublicKey,
compute_load: $crate::c_api::high_level_api::zk::ZkComputeLoad,
out_result: *mut *mut [<ProvenCompact $name List>],
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let messages = std::slice::from_raw_parts(input, input_len)
.iter()
.copied()
.map(|value| {
<$clear_scalar_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(value)
})
.collect::<Vec<_>>();
let result = $crate::high_level_api::[<ProvenCompact $name List>]::try_encrypt(
&messages,
&public_params.0,
&pk.0,
compute_load.into()
).unwrap();
*out_result = Box::into_raw(Box::new([<ProvenCompact $name List>](result)));
})
}
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _list_len>](
sself: *const [<ProvenCompact $name List>],
result: *mut usize,
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let list = $crate::c_api::utils::get_ref_checked(sself).unwrap();
*result = list.0.len();
})
}
#[no_mangle]
pub unsafe extern "C" fn [<proven_compact_ $name:snake _list_verify_and_expand>](
list: &[<ProvenCompact $name List>],
public_params: &$crate::c_api::high_level_api::zk::CompactPkePublicParams,
pk: &$crate::c_api::high_level_api::keys::CompactPublicKey,
output: *mut *mut $name,
output_len: usize
) -> ::std::os::raw::c_int {
$crate::c_api::utils::catch_panic(|| {
let expanded = list.0.verify_and_expand(&public_params.0, &pk.0).unwrap();
let num_to_take = output_len.max(list.0.len());
let iter = expanded.into_iter().take(num_to_take).enumerate();
for (i, fhe_uint) in iter {
let ptr = output.wrapping_add(i);
*ptr = Box::into_raw(Box::new($name(fhe_uint)));
}
})
}
}
}; };
// This entry point is meant for unsigned types // This entry point is meant for unsigned types
@@ -803,7 +933,6 @@ macro_rules! impl_oprf_for_int {
crate::high_level_api::SignedRandomizationSpec::FullSigned, crate::high_level_api::SignedRandomizationSpec::FullSigned,
); );
*out_result = Box::into_raw(Box::new($name(result))); *out_result = Box::into_raw(Box::new($name(result)));
}) })
} }
} }

View File

@@ -9,3 +9,5 @@ mod threading;
pub mod u128; pub mod u128;
pub mod u256; pub mod u256;
mod utils; mod utils;
#[cfg(feature = "zk-pok-experimental")]
mod zk;

View File

@@ -0,0 +1,59 @@
use super::utils::*;
use crate::c_api::high_level_api::config::Config;
use crate::c_api::utils::get_ref_checked;
use std::ffi::c_int;
#[repr(C)]
#[derive(Copy, Clone)]
pub enum ZkComputeLoad {
ZkComputeLoadProof,
ZkComputeLoadVerify,
}
impl From<ZkComputeLoad> for crate::zk::ZkComputeLoad {
fn from(value: ZkComputeLoad) -> Self {
match value {
ZkComputeLoad::ZkComputeLoadProof => Self::Proof,
ZkComputeLoad::ZkComputeLoadVerify => Self::Verify,
}
}
}
pub struct CompactPkePublicParams(pub(crate) crate::core_crypto::entities::CompactPkePublicParams);
impl_destroy_on_type!(CompactPkePublicParams);
impl_serialize_deserialize_on_type!(CompactPkePublicParams);
pub struct CompactPkeCrs(pub(crate) crate::core_crypto::entities::CompactPkeCrs);
impl_destroy_on_type!(CompactPkeCrs);
impl_serialize_deserialize_on_type!(CompactPkeCrs);
#[no_mangle]
pub unsafe extern "C" fn compact_pke_crs_from_config(
config: *const Config,
max_num_bits: usize,
out_result: *mut *mut CompactPkeCrs,
) -> c_int {
crate::c_api::utils::catch_panic(|| {
let config = get_ref_checked(config).unwrap();
let crs = crate::core_crypto::entities::CompactPkeCrs::from_config(config.0, max_num_bits)
.unwrap();
*out_result = Box::into_raw(Box::new(CompactPkeCrs(crs)));
})
}
#[no_mangle]
pub unsafe extern "C" fn compact_pke_crs_public_params(
crs: *const CompactPkeCrs,
out_public_params: *mut *mut CompactPkePublicParams,
) -> c_int {
crate::c_api::utils::catch_panic(|| {
let crs = get_ref_checked(crs).unwrap();
*out_public_params = Box::into_raw(Box::new(CompactPkePublicParams(
crs.0.public_params().clone(),
)));
})
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
use crate::core_crypto::entities::{LweCompactCiphertextList, LweCompactPublicKey};
use crate::core_crypto::prelude::{CastFrom, Container, LweCiphertext, UnsignedInteger};
use crate::zk::{CompactPkeProof, CompactPkePublicParams, ZkVerificationOutCome};
use tfhe_zk_pok::proofs::pke::{verify, PublicCommit};
/// Verifies with the given proof that a [`LweCompactCiphertextList`](LweCompactCiphertextList)
/// is valid.
pub fn verify_lwe_compact_ciphertext_list<Scalar, ListCont, KeyCont>(
lwe_compact_list: &LweCompactCiphertextList<ListCont>,
compact_public_key: &LweCompactPublicKey<KeyCont>,
proof: &CompactPkeProof,
public_params: &CompactPkePublicParams,
) -> ZkVerificationOutCome
where
Scalar: UnsignedInteger,
i64: CastFrom<Scalar>,
ListCont: Container<Element = Scalar>,
KeyCont: Container<Element = Scalar>,
{
if Scalar::BITS > 64 {
return ZkVerificationOutCome::Invalid;
}
let public_commit = PublicCommit::new(
compact_public_key
.get_mask()
.as_ref()
.iter()
.copied()
.map(|x| i64::cast_from(x))
.collect(),
compact_public_key
.get_body()
.as_ref()
.iter()
.copied()
.map(|x| i64::cast_from(x))
.collect(),
lwe_compact_list
.get_mask_list()
.as_ref()
.iter()
.copied()
.map(|x| i64::cast_from(x))
.collect(),
lwe_compact_list
.get_body_list()
.as_ref()
.iter()
.copied()
.map(|x| i64::cast_from(x))
.collect(),
);
match verify(proof, (public_params, &public_commit)) {
Ok(_) => ZkVerificationOutCome::Valid,
Err(_) => ZkVerificationOutCome::Invalid,
}
}
pub fn verify_lwe_ciphertext<Scalar, Cont, KeyCont>(
lwe_ciphertext: &LweCiphertext<Cont>,
compact_public_key: &LweCompactPublicKey<KeyCont>,
proof: &CompactPkeProof,
public_params: &CompactPkePublicParams,
) -> ZkVerificationOutCome
where
Scalar: UnsignedInteger,
i64: CastFrom<Scalar>,
Cont: Container<Element = Scalar>,
KeyCont: Container<Element = Scalar>,
{
if Scalar::BITS > 64 {
return ZkVerificationOutCome::Invalid;
}
let public_commit = PublicCommit::new(
compact_public_key
.get_mask()
.as_ref()
.iter()
.copied()
.map(|x| i64::cast_from(x))
.collect(),
compact_public_key
.get_body()
.as_ref()
.iter()
.copied()
.map(|x| i64::cast_from(x))
.collect(),
lwe_ciphertext
.get_mask()
.as_ref()
.iter()
.copied()
.map(|x| i64::cast_from(x))
.collect(),
vec![i64::cast_from(*lwe_ciphertext.get_body().data); 1],
);
match verify(proof, (public_params, &public_commit)) {
Ok(_) => ZkVerificationOutCome::Valid,
Err(_) => ZkVerificationOutCome::Invalid,
}
}

View File

@@ -27,6 +27,8 @@ pub mod lwe_programmable_bootstrapping;
pub mod lwe_public_key_generation; pub mod lwe_public_key_generation;
pub mod lwe_secret_key_generation; pub mod lwe_secret_key_generation;
pub mod lwe_wopbs; pub mod lwe_wopbs;
#[cfg(feature = "zk-pok-experimental")]
pub mod lwe_zero_knowledge_verification;
pub mod misc; pub mod misc;
pub mod polynomial_algorithms; pub mod polynomial_algorithms;
pub mod seeded_ggsw_ciphertext_decompression; pub mod seeded_ggsw_ciphertext_decompression;
@@ -73,6 +75,8 @@ pub use lwe_programmable_bootstrapping::*;
pub use lwe_public_key_generation::*; pub use lwe_public_key_generation::*;
pub use lwe_secret_key_generation::*; pub use lwe_secret_key_generation::*;
pub use lwe_wopbs::*; pub use lwe_wopbs::*;
#[cfg(feature = "zk-pok-experimental")]
pub use lwe_zero_knowledge_verification::*;
pub use seeded_ggsw_ciphertext_decompression::*; pub use seeded_ggsw_ciphertext_decompression::*;
pub use seeded_ggsw_ciphertext_list_decompression::*; pub use seeded_ggsw_ciphertext_list_decompression::*;
pub use seeded_glwe_ciphertext_decompression::*; pub use seeded_glwe_ciphertext_decompression::*;

View File

@@ -1,6 +1,10 @@
use super::*; use super::*;
use crate::core_crypto::commons::generators::DeterministicSeeder; use crate::core_crypto::commons::generators::DeterministicSeeder;
#[cfg(feature = "zk-pok-experimental")]
use crate::core_crypto::commons::math::random::RandomGenerator;
use crate::core_crypto::commons::test_tools; use crate::core_crypto::commons::test_tools;
#[cfg(feature = "zk-pok-experimental")]
use rand::Rng;
#[cfg(not(tarpaulin))] #[cfg(not(tarpaulin))]
const NB_TESTS: usize = 10; const NB_TESTS: usize = 10;
@@ -182,7 +186,7 @@ fn lwe_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(params: ClassicTestPara
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -237,7 +241,7 @@ fn lwe_allocate_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -290,7 +294,7 @@ fn lwe_trivial_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -340,7 +344,7 @@ fn lwe_allocate_trivial_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -403,7 +407,7 @@ fn lwe_list_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(params: ClassicTes
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&list, &list,
ciphertext_modulus ciphertext_modulus,
)); ));
let mut plaintext_list = let mut plaintext_list =
@@ -474,7 +478,7 @@ fn lwe_list_par_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus + Sync + Send>(
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&list, &list,
ciphertext_modulus ciphertext_modulus,
)); ));
let mut plaintext_list = let mut plaintext_list =
@@ -548,7 +552,7 @@ fn lwe_public_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(params: ClassicT
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -623,7 +627,7 @@ fn lwe_seeded_public_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -689,7 +693,7 @@ fn lwe_seeded_list_par_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus + Sync +
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&seeded_list, &seeded_list,
ciphertext_modulus ciphertext_modulus,
)); ));
let mut plaintext_list = PlaintextList::new( let mut plaintext_list = PlaintextList::new(
@@ -701,7 +705,7 @@ fn lwe_seeded_list_par_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus + Sync +
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&lwe_list, &lwe_list,
ciphertext_modulus ciphertext_modulus,
)); ));
decrypt_lwe_ciphertext_list(&lwe_sk, &lwe_list, &mut plaintext_list); decrypt_lwe_ciphertext_list(&lwe_sk, &lwe_list, &mut plaintext_list);
@@ -765,14 +769,14 @@ fn lwe_seeded_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(params: ClassicT
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&std::slice::from_ref(seeded_ct.get_body().data), &std::slice::from_ref(seeded_ct.get_body().data),
ciphertext_modulus ciphertext_modulus,
)); ));
let ct = seeded_ct.decompress_into_lwe_ciphertext(); let ct = seeded_ct.decompress_into_lwe_ciphertext();
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -826,14 +830,14 @@ fn lwe_seeded_allocate_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&std::slice::from_ref(seeded_ct.get_body().data), &std::slice::from_ref(seeded_ct.get_body().data),
ciphertext_modulus ciphertext_modulus,
)); ));
let ct = seeded_ct.decompress_into_lwe_ciphertext(); let ct = seeded_ct.decompress_into_lwe_ciphertext();
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -971,7 +975,7 @@ fn lwe_compact_public_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(
assert!(check_encrypted_content_respects_mod( assert!(check_encrypted_content_respects_mod(
&ct, &ct,
ciphertext_modulus ciphertext_modulus,
)); ));
let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct);
@@ -991,3 +995,324 @@ fn lwe_compact_public_encrypt_decrypt_custom_mod<Scalar: UnsignedTorus>(
create_parametrized_test!(lwe_compact_public_encrypt_decrypt_custom_mod { create_parametrized_test!(lwe_compact_public_encrypt_decrypt_custom_mod {
TEST_PARAMS_4_BITS_NATIVE_U64 TEST_PARAMS_4_BITS_NATIVE_U64
}); });
#[cfg(feature = "zk-pok-experimental")]
fn lwe_compact_public_encrypt_prove_verify_decrypt_custom_mod<Scalar>(
params: ClassicTestParams<Scalar>,
) where
Scalar: UnsignedTorus + CastFrom<u64>,
Scalar::Signed: CastFrom<u64>,
i64: CastFrom<Scalar>,
u64: CastFrom<Scalar> + CastInto<Scalar::Signed>,
rand_distr::Standard: rand_distr::Distribution<Scalar>,
{
let lwe_dimension = LweDimension(params.polynomial_size.0);
let glwe_noise_distribution = TUniform::new(9);
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();
let mut random_generator = RandomGenerator::<ActivatedRandomGenerator>::new(rsc.seeder.seed());
let msg_modulus = Scalar::ONE.shl(message_modulus_log.0);
let mut msg = msg_modulus;
let delta: Scalar = encoding_with_padding / msg_modulus;
let crs = CompactPkeCrs::new(
lwe_dimension,
1,
glwe_noise_distribution,
ciphertext_modulus,
msg_modulus * Scalar::TWO,
&mut random_generator,
)
.unwrap();
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_noise_distribution,
ciphertext_modulus,
&mut rsc.encryption_random_generator,
);
let mut ct = LweCiphertext::new(
Scalar::ZERO,
lwe_dimension.to_lwe_size(),
ciphertext_modulus,
);
let proof = encrypt_and_prove_lwe_ciphertext_with_compact_public_key(
&pk,
&mut ct,
Cleartext(msg),
delta,
glwe_noise_distribution,
glwe_noise_distribution,
&mut rsc.secret_random_generator,
&mut rsc.encryption_random_generator,
&mut random_generator,
crs.public_params(),
ZkComputeLoad::Proof,
)
.unwrap();
assert!(check_encrypted_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);
// Verify the proof
assert!(verify_lwe_ciphertext(&ct, &pk, &proof, crs.public_params()).is_valid());
// verify proof with invalid ciphertext
let index = random_generator.gen::<usize>() % ct.as_ref().len();
let value_to_add = random_generator.gen::<Scalar>();
ct.as_mut()[index] = ct.as_mut()[index].wrapping_add(value_to_add);
assert!(verify_lwe_ciphertext(&ct, &pk, &proof, crs.public_params()).is_invalid());
}
// In coverage, we break after one while loop iteration, changing message values does not
// yield higher coverage
#[cfg(tarpaulin)]
break;
}
}
#[cfg(feature = "zk-pok-experimental")]
create_parametrized_test!(lwe_compact_public_encrypt_prove_verify_decrypt_custom_mod {
TEST_PARAMS_4_BITS_NATIVE_U64
});
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_par_compact_lwe_list_public_key_encryption_and_proof() {
use rand::Rng;
let lwe_dimension = LweDimension(2048);
let glwe_noise_distribution = TUniform::new(9);
let ciphertext_modulus = CiphertextModulus::new_native();
let delta_log = 59;
let delta = 1u64 << delta_log;
let message_modulus = 1u64 << (64 - (delta_log + 1));
let plaintext_modulus = 1u64 << (64 - delta_log);
let mut thread_rng = rand::thread_rng();
let max_num_body = 512;
let crs = CompactPkeCrs::new(
lwe_dimension,
max_num_body,
glwe_noise_distribution,
ciphertext_modulus,
plaintext_modulus,
&mut thread_rng,
)
.unwrap();
for _ in 0..4 {
let ct_count = thread_rng.gen_range(1..=max_num_body);
let lwe_ciphertext_count = LweCiphertextCount(ct_count);
println!("{lwe_dimension:?} {ct_count:?}");
let seed = test_tools::random_seed();
let cleartexts = (0..ct_count)
.map(|_| thread_rng.gen::<u64>() % message_modulus)
.collect::<Vec<_>>();
let par_lwe_ct_list = {
let mut deterministic_seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
let mut random_generator =
RandomGenerator::<ActivatedRandomGenerator>::new(deterministic_seeder.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_noise_distribution,
&mut encryption_random_generator,
);
let mut output_compact_ct_list = LweCompactCiphertextList::new(
0u64,
lwe_dimension.to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
let proof = par_encrypt_and_prove_lwe_compact_ciphertext_list_with_compact_public_key(
&compact_lwe_pk,
&mut output_compact_ct_list,
&cleartexts,
delta,
glwe_noise_distribution,
glwe_noise_distribution,
&mut secret_random_generator,
&mut encryption_random_generator,
&mut random_generator,
crs.public_params(),
ZkComputeLoad::Proof,
)
.unwrap();
assert!(verify_lwe_compact_ciphertext_list(
&output_compact_ct_list,
&compact_lwe_pk,
&proof,
crs.public_params()
)
.is_valid());
let mut output_plaintext_list = PlaintextList::new(0u64, PlaintextCount(ct_count));
let lwe_ciphertext_list = output_compact_ct_list
.clone()
.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(5), DecompositionLevelCount(1));
output_plaintext_list
.iter_mut()
.for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0) >> delta_log);
assert_eq!(cleartexts.as_slice(), output_plaintext_list.as_ref());
// verify proof with invalid ciphertext
let index = random_generator.gen::<usize>() % output_compact_ct_list.as_ref().len();
let value_to_add = random_generator.gen();
output_compact_ct_list.as_mut()[index] =
output_compact_ct_list.as_mut()[index].wrapping_add(value_to_add);
assert!(verify_lwe_compact_ciphertext_list(
&output_compact_ct_list,
&compact_lwe_pk,
&proof,
crs.public_params()
)
.is_invalid());
lwe_ciphertext_list
};
let ser_lwe_ct_list = {
let mut deterministic_seeder =
DeterministicSeeder::<ActivatedRandomGenerator>::new(seed);
let mut random_generator =
RandomGenerator::<ActivatedRandomGenerator>::new(deterministic_seeder.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_noise_distribution,
&mut encryption_random_generator,
);
let mut output_compact_ct_list = LweCompactCiphertextList::new(
0u64,
lwe_dimension.to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
let proof = par_encrypt_and_prove_lwe_compact_ciphertext_list_with_compact_public_key(
&compact_lwe_pk,
&mut output_compact_ct_list,
&cleartexts,
delta,
glwe_noise_distribution,
glwe_noise_distribution,
&mut secret_random_generator,
&mut encryption_random_generator,
&mut random_generator,
crs.public_params(),
ZkComputeLoad::Proof,
)
.unwrap();
assert!(verify_lwe_compact_ciphertext_list(
&output_compact_ct_list,
&compact_lwe_pk,
&proof,
crs.public_params()
)
.is_valid());
let mut output_plaintext_list = PlaintextList::new(0u64, PlaintextCount(ct_count));
let lwe_ciphertext_list = output_compact_ct_list
.clone()
.expand_into_lwe_ciphertext_list();
decrypt_lwe_ciphertext_list(&lwe_sk, &lwe_ciphertext_list, &mut output_plaintext_list);
let signed_decomposer =
SignedDecomposer::new(DecompositionBaseLog(5), DecompositionLevelCount(1));
output_plaintext_list
.iter_mut()
.for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0) >> delta_log);
assert_eq!(cleartexts.as_slice(), output_plaintext_list.as_ref());
// verify proof with invalid ciphertext
let index = random_generator.gen::<usize>() % output_compact_ct_list.as_ref().len();
let value_to_add = random_generator.gen();
output_compact_ct_list.as_mut()[index] =
output_compact_ct_list.as_mut()[index].wrapping_add(value_to_add);
assert!(verify_lwe_compact_ciphertext_list(
&output_compact_ct_list,
&compact_lwe_pk,
&proof,
crs.public_params()
)
.is_invalid());
lwe_ciphertext_list
};
assert_eq!(ser_lwe_ct_list, par_lwe_ct_list);
}
}

View File

@@ -1,6 +1,5 @@
use super::*; use super::*;
use crate::core_crypto::commons::math::torus::FromTorus; use crate::core_crypto::commons::math::torus::FromTorus;
use crate::core_crypto::commons::numeric::{CastInto, Numeric};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// Clippy false positive, does not repro with smaller code // Clippy false positive, does not repro with smaller code

View File

@@ -796,3 +796,28 @@ impl<G: ParallelByteRandomGenerator> RandomGenerator<G> {
.map(|iter| iter.map(Self)) .map(|iter| iter.map(Self))
} }
} }
impl<G: ByteRandomGenerator> rand_core::RngCore for RandomGenerator<G> {
fn next_u32(&mut self) -> u32 {
<u32 as RandomGenerable<Uniform>>::generate_one(self, Uniform)
}
fn next_u64(&mut self) -> u64 {
<u64 as RandomGenerable<Uniform>>::generate_one(self, Uniform)
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
dest.iter_mut().for_each(|b| *b = self.generate_next());
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
if let Some(limit) = self.remaining_bytes() {
if limit < dest.len() {
return Err(rand_core::Error::new(format!("The random generator is bounded and cannot fill the slice {} bytes requested, {limit} possible", dest.len()))
);
}
}
self.fill_bytes(dest);
Ok(())
}
}

View File

@@ -15,7 +15,9 @@
//! [`RandomGenerator`] instead. //! [`RandomGenerator`] instead.
use crate::core_crypto::commons::dispersion::{DispersionParameter, StandardDev, Variance}; use crate::core_crypto::commons::dispersion::{DispersionParameter, StandardDev, Variance};
use crate::core_crypto::commons::numeric::{FloatingPoint, UnsignedInteger}; use crate::core_crypto::commons::numeric::{FloatingPoint, UnsignedInteger};
use std::ops::Bound;
use crate::core_crypto::prelude::{CastInto, Numeric};
/// Convenience alias for the most efficient CSPRNG implementation available. /// Convenience alias for the most efficient CSPRNG implementation available.
pub use activated_random_generator::ActivatedRandomGenerator; pub use activated_random_generator::ActivatedRandomGenerator;
pub use gaussian::*; pub use gaussian::*;
@@ -102,6 +104,82 @@ impl Distribution for UniformTernary {}
impl<T: FloatingPoint> Distribution for Gaussian<T> {} impl<T: FloatingPoint> Distribution for Gaussian<T> {}
impl<T: UnsignedInteger> Distribution for TUniform<T> {} impl<T: UnsignedInteger> Distribution for TUniform<T> {}
pub trait BoundedDistribution<T>: Distribution {
fn low_bound(&self) -> Bound<T>;
fn high_bound(&self) -> Bound<T>;
fn contains(self, value: T) -> bool
where
T: Numeric,
{
{
match self.low_bound() {
Bound::Included(inclusive_low) => {
if value < inclusive_low {
return false;
}
}
Bound::Excluded(exclusive_low) => {
if value <= exclusive_low {
return false;
}
}
Bound::Unbounded => {}
}
}
{
match self.high_bound() {
Bound::Included(inclusive_high) => {
if value > inclusive_high {
return false;
}
}
Bound::Excluded(exclusive_high) => {
if value >= exclusive_high {
return false;
}
}
Bound::Unbounded => {}
}
}
true
}
}
impl<T> BoundedDistribution<T::Signed> for TUniform<T>
where
T: UnsignedInteger,
{
fn low_bound(&self) -> Bound<T::Signed> {
Bound::Included(self.min_value_inclusive())
}
fn high_bound(&self) -> Bound<T::Signed> {
Bound::Included(self.max_value_inclusive())
}
}
impl<T> BoundedDistribution<T::Signed> for DynamicDistribution<T>
where
T: UnsignedInteger,
{
fn low_bound(&self) -> Bound<T::Signed> {
match self {
Self::Gaussian(_) => Bound::Unbounded,
Self::TUniform(tu) => tu.low_bound(),
}
}
fn high_bound(&self) -> Bound<T::Signed> {
match self {
Self::Gaussian(_) => Bound::Unbounded,
Self::TUniform(tu) => tu.high_bound(),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum DynamicDistribution<T: UnsignedInteger> { pub enum DynamicDistribution<T: UnsignedInteger> {
Gaussian(Gaussian<f64>), Gaussian(Gaussian<f64>),

View File

@@ -1,5 +1,4 @@
use super::*; use super::*;
use crate::core_crypto::commons::numeric::Numeric;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// The distribution $TUniform(1, -2^b, 2^b)$ is defined as follows, any value in the interval /// The distribution $TUniform(1, -2^b, 2^b)$ is defined as follows, any value in the interval

View File

@@ -41,6 +41,10 @@ pub trait SignedInteger:
/// Return a bit representation of the integer, where blocks of length `block_length` are /// Return a bit representation of the integer, where blocks of length `block_length` are
/// separated by whitespaces to increase the readability. /// separated by whitespaces to increase the readability.
fn to_bits_string(&self, block_length: usize) -> String; fn to_bits_string(&self, block_length: usize) -> String;
/// Return the absoluted balue
#[must_use]
fn wrapping_abs(self) -> Self;
} }
macro_rules! implement { macro_rules! implement {
@@ -77,6 +81,11 @@ macro_rules! implement {
} }
strn strn
} }
#[inline]
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
} }
}; };
} }

View File

@@ -87,6 +87,8 @@ pub trait UnsignedInteger:
#[must_use] #[must_use]
fn is_power_of_two(self) -> bool; fn is_power_of_two(self) -> bool;
#[must_use] #[must_use]
fn next_power_of_two(self) -> Self;
#[must_use]
fn ilog2(self) -> u32; fn ilog2(self) -> u32;
#[must_use] #[must_use]
fn ceil_ilog2(self) -> u32 { fn ceil_ilog2(self) -> u32 {
@@ -240,6 +242,10 @@ macro_rules! implement {
self.is_power_of_two() self.is_power_of_two()
} }
#[inline] #[inline]
fn next_power_of_two(self) -> Self {
self.next_power_of_two()
}
#[inline]
fn ilog2(self) -> u32 { fn ilog2(self) -> u32 {
self.ilog2() self.ilog2()
} }

View File

@@ -53,6 +53,8 @@ pub use crate::core_crypto::fft_impl::fft64::crypto::ggsw::{
FourierGgswCiphertext, FourierGgswCiphertextList, FourierGgswLevelMatrix, FourierGgswLevelRow, FourierGgswCiphertext, FourierGgswCiphertextList, FourierGgswLevelMatrix, FourierGgswLevelRow,
}; };
pub use crate::core_crypto::fft_impl::fft64::math::polynomial::FourierPolynomial; pub use crate::core_crypto::fft_impl::fft64::math::polynomial::FourierPolynomial;
#[cfg(feature = "zk-pok-experimental")]
pub use crate::zk::*;
pub use cleartext::*; pub use cleartext::*;
pub use ggsw_ciphertext::*; pub use ggsw_ciphertext::*;
pub use ggsw_ciphertext_list::*; pub use ggsw_ciphertext_list::*;

59
tfhe/src/error.rs Normal file
View File

@@ -0,0 +1,59 @@
use std::fmt::{Debug, Display, Formatter};
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ErrorKind {
Message(String),
/// The zero knowledge proof and the content it is supposed to prove
/// failed to correctly prove
#[cfg(feature = "zk-pok-experimental")]
InvalidZkProof,
}
#[derive(Debug, Clone)]
pub struct Error {
kind: ErrorKind,
}
impl Error {
pub(crate) fn new(message: String) -> Self {
Self::from(ErrorKind::Message(message))
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.kind() {
ErrorKind::Message(msg) => {
write!(f, "{msg}")
}
#[cfg(feature = "zk-pok-experimental")]
ErrorKind::InvalidZkProof => {
write!(f, "The zero knowledge proof and the content it is supposed to prove were not valid")
}
}
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Self { kind }
}
}
impl<'a> From<&'a str> for Error {
fn from(message: &'a str) -> Self {
Self::new(message.to_string())
}
}
impl From<String> for Error {
fn from(message: String) -> Self {
Self::new(message)
}
}
impl std::error::Error for Error {}

View File

@@ -36,7 +36,7 @@ use crate::{CompactPublicKey, FheBoolConformanceParams, ServerKey};
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))] #[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "integer")))]
#[derive(Clone, serde::Deserialize, serde::Serialize)] #[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct CompactFheBool { pub struct CompactFheBool {
list: CompactCiphertextList, pub(in crate::high_level_api) list: CompactCiphertextList,
} }
impl CompactFheBool { impl CompactFheBool {
@@ -55,7 +55,7 @@ impl CompactFheBool {
} }
impl FheTryEncrypt<bool, CompactPublicKey> for CompactFheBool { impl FheTryEncrypt<bool, CompactPublicKey> for CompactFheBool {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: bool, key: &CompactPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: bool, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let mut ciphertext = key.key.try_encrypt_compact(&[u8::from(value)], 1); let mut ciphertext = key.key.try_encrypt_compact(&[u8::from(value)], 1);
@@ -145,7 +145,7 @@ impl CompactFheBoolList {
} }
impl<'a> FheTryEncrypt<&'a [bool], CompactPublicKey> for CompactFheBoolList { impl<'a> FheTryEncrypt<&'a [bool], CompactPublicKey> for CompactFheBoolList {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
/// Encrypts a slice of bool /// Encrypts a slice of bool
/// ///

View File

@@ -58,7 +58,7 @@ impl CompressedFheBool {
} }
impl FheTryEncrypt<bool, ClientKey> for CompressedFheBool { impl FheTryEncrypt<bool, ClientKey> for CompressedFheBool {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
/// Creates a compressed encryption of a boolean value /// Creates a compressed encryption of a boolean value
fn try_encrypt(value: bool, key: &ClientKey) -> Result<Self, Self::Error> { fn try_encrypt(value: bool, key: &ClientKey) -> Result<Self, Self::Error> {

View File

@@ -12,7 +12,7 @@ use crate::shortint::ciphertext::Degree;
use crate::{ClientKey, CompactPublicKey, CompressedPublicKey, PublicKey}; use crate::{ClientKey, CompactPublicKey, CompressedPublicKey, PublicKey};
impl FheTryEncrypt<bool, ClientKey> for FheBool { impl FheTryEncrypt<bool, ClientKey> for FheBool {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: bool, key: &ClientKey) -> Result<Self, Self::Error> { fn try_encrypt(value: bool, key: &ClientKey) -> Result<Self, Self::Error> {
let integer_client_key = &key.key.key; let integer_client_key = &key.key.key;
@@ -23,7 +23,7 @@ impl FheTryEncrypt<bool, ClientKey> for FheBool {
} }
impl FheTryEncrypt<bool, CompactPublicKey> for FheBool { impl FheTryEncrypt<bool, CompactPublicKey> for FheBool {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: bool, key: &CompactPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: bool, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let mut ciphertext = key.key.key.encrypt_radix(value as u8, 1); let mut ciphertext = key.key.key.encrypt_radix(value as u8, 1);
@@ -66,7 +66,7 @@ impl FheTrivialEncrypt<bool> for FheBool {
} }
impl FheTryEncrypt<bool, CompressedPublicKey> for FheBool { impl FheTryEncrypt<bool, CompressedPublicKey> for FheBool {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: bool, key: &CompressedPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: bool, key: &CompressedPublicKey) -> Result<Self, Self::Error> {
let key = &key.key; let key = &key.key;
@@ -77,7 +77,7 @@ impl FheTryEncrypt<bool, CompressedPublicKey> for FheBool {
} }
impl FheTryEncrypt<bool, PublicKey> for FheBool { impl FheTryEncrypt<bool, PublicKey> for FheBool {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: bool, key: &PublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: bool, key: &PublicKey) -> Result<Self, Self::Error> {
let key = &key.key; let key = &key.key;
@@ -95,7 +95,7 @@ impl FheDecrypt<bool> for FheBool {
} }
impl FheTryTrivialEncrypt<bool> for FheBool { impl FheTryTrivialEncrypt<bool> for FheBool {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt_trivial(value: bool) -> Result<Self, Self::Error> { fn try_encrypt_trivial(value: bool) -> Result<Self, Self::Error> {
let ciphertext = global_state::with_internal_keys(|key| match key { let ciphertext = global_state::with_internal_keys(|key| match key {

View File

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

View File

@@ -825,7 +825,57 @@ mod cpu {
let expanded_list = deserialized_list.expand(); let expanded_list = deserialized_list.expand();
for (fhe_uint, expected) in expanded_list.iter().zip(clears.into_iter()) { for (fhe_uint, expected) in expanded_list.iter().zip(clears.into_iter()) {
let decrypted: bool = fhe_uint.decrypt(&client_key); let decrypted: bool = fhe_uint.decrypt(&client_key);
assert_eq!(decrypted, expected); assert_eq!(decrypted, expected)
}
}
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_fhe_bool_zk() {
use crate::core_crypto::prelude::DynamicDistribution;
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
let mut params = crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
params.glwe_noise_distribution = DynamicDistribution::new_t_uniform(9);
let config = ConfigBuilder::with_custom_parameters(params, None).build();
let crs = CompactPkeCrs::from_config(config, 2).unwrap();
let ck = ClientKey::generate(config);
let pk = CompactPublicKey::new(&ck);
for msg in [true, false] {
let proven_compact_fhe_bool = crate::ProvenCompactFheBool::try_encrypt(
msg,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_bool = proven_compact_fhe_bool
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = fhe_bool.decrypt(&ck);
assert_eq!(decrypted, msg);
assert_degree_is_ok(&fhe_bool);
}
let proven_compact_fhe_bool_list = crate::ProvenCompactFheBoolList::try_encrypt(
&[true, false],
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_bools = proven_compact_fhe_bool_list
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = fhe_bools
.iter()
.map(|fb| fb.decrypt(&ck))
.collect::<Vec<_>>();
assert_eq!(decrypted.as_slice(), &[true, false]);
for fhe_bool in fhe_bools {
assert_degree_is_ok(&fhe_bool);
} }
} }
} }

View File

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

View File

@@ -1,7 +1,7 @@
use crate::high_level_api::keys::IntegerConfig; use crate::high_level_api::keys::IntegerConfig;
/// The config type /// The config type
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Config { pub struct Config {
pub(crate) inner: IntegerConfig, pub(crate) inner: IntegerConfig,
} }

View File

@@ -38,43 +38,3 @@ impl Display for UninitializedServerKey {
} }
impl std::error::Error for UninitializedServerKey {} impl std::error::Error for UninitializedServerKey {}
/// Error when trying to create a short integer from a value that was too big to be represented
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct OutOfRangeError;
impl Display for OutOfRangeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Value is out of range")
}
}
impl std::error::Error for OutOfRangeError {}
#[non_exhaustive]
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
OutOfRange,
UninitializedServerKey,
}
impl From<OutOfRangeError> for Error {
fn from(_: OutOfRangeError) -> Self {
Self::OutOfRange
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::OutOfRange => {
write!(f, "{OutOfRangeError}")
}
Self::UninitializedServerKey => {
write!(f, "{UninitializedServerKey}")
}
}
}
}
impl std::error::Error for Error {}

View File

@@ -66,7 +66,7 @@ where
T: crate::integer::block_decomposition::DecomposableInto<u64>, T: crate::integer::block_decomposition::DecomposableInto<u64>,
Id: FheIntId, Id: FheIntId,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let id = Id::default(); let id = Id::default();
@@ -169,7 +169,7 @@ where
T: crate::integer::block_decomposition::DecomposableInto<u64>, T: crate::integer::block_decomposition::DecomposableInto<u64>,
Id: FheIntId, Id: FheIntId,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(values: &'a [T], key: &CompactPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(values: &'a [T], key: &CompactPublicKey) -> Result<Self, Self::Error> {
let id = Id::default(); let id = Id::default();

View File

@@ -88,7 +88,7 @@ where
Id: FheIntId, Id: FheIntId,
T: DecomposableInto<u64> + SignedNumeric, T: DecomposableInto<u64> + SignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &ClientKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &ClientKey) -> Result<Self, Self::Error> {
let integer_client_key = &key.key.key; let integer_client_key = &key.key.key;

View File

@@ -43,7 +43,7 @@ where
Id: FheIntId, Id: FheIntId,
T: DecomposableInto<u64> + SignedNumeric, T: DecomposableInto<u64> + SignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &ClientKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &ClientKey) -> Result<Self, Self::Error> {
let ciphertext = key let ciphertext = key
@@ -59,7 +59,7 @@ where
Id: FheIntId, Id: FheIntId,
T: DecomposableInto<u64> + SignedNumeric, T: DecomposableInto<u64> + SignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &PublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &PublicKey) -> Result<Self, Self::Error> {
let ciphertext = key let ciphertext = key
@@ -74,7 +74,7 @@ where
Id: FheIntId, Id: FheIntId,
T: DecomposableInto<u64> + SignedNumeric, T: DecomposableInto<u64> + SignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &CompressedPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &CompressedPublicKey) -> Result<Self, Self::Error> {
let ciphertext = key let ciphertext = key
@@ -89,7 +89,7 @@ where
Id: FheIntId, Id: FheIntId,
T: DecomposableInto<u64> + SignedNumeric, T: DecomposableInto<u64> + SignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let ciphertext = key let ciphertext = key
@@ -105,7 +105,7 @@ where
T: DecomposableInto<u64>, T: DecomposableInto<u64>,
Id: FheIntId, Id: FheIntId,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
/// Creates a trivial encryption of a signed integer. /// Creates a trivial encryption of a signed integer.
/// ///

View File

@@ -9,6 +9,8 @@ mod scalar_ops;
mod static_; mod static_;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
#[cfg(feature = "zk-pok-experimental")]
mod zk;
pub use base::{FheInt, FheIntId}; pub use base::{FheInt, FheIntId};
pub use compact::{CompactFheInt, CompactFheIntList}; pub use compact::{CompactFheInt, CompactFheIntList};

View File

@@ -1,3 +1,5 @@
#[cfg(feature = "zk-pok-experimental")]
use super::zk::{ProvenCompactFheInt, ProvenCompactFheIntList};
use crate::high_level_api::integers::signed::base::{FheInt, FheIntConformanceParams, FheIntId}; use crate::high_level_api::integers::signed::base::{FheInt, FheIntConformanceParams, FheIntId};
use crate::high_level_api::integers::signed::compact::{ use crate::high_level_api::integers::signed::compact::{
CompactFheInt, CompactFheIntList, CompactFheIntListConformanceParams, CompactFheInt, CompactFheIntList, CompactFheIntListConformanceParams,
@@ -57,6 +59,13 @@ macro_rules! static_int_type {
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))] #[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact FheInt $num_bits ListConformanceParams>] = CompactFheIntListConformanceParams<[<FheInt $num_bits Id>]>; pub type [<Compact FheInt $num_bits ListConformanceParams>] = CompactFheIntListConformanceParams<[<FheInt $num_bits Id>]>;
// Zero-knowledge Stuff
#[cfg(feature = "zk-pok-experimental")]
pub type [<ProvenCompactFheInt $num_bits>] = ProvenCompactFheInt<[<FheInt $num_bits Id>]>;
#[cfg(feature = "zk-pok-experimental")]
pub type [<ProvenCompactFheInt $num_bits List>] = ProvenCompactFheIntList<[<FheInt $num_bits Id>]>;
} }
}; };
} }

View File

@@ -801,3 +801,50 @@ fn test_safe_deserialize_conformant_compact_fhe_int32_list() {
)); ));
assert!(deserialized_list.is_conformant(&params)); assert!(deserialized_list.is_conformant(&params));
} }
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_fhe_int_zk() {
use crate::core_crypto::prelude::DynamicDistribution;
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
let mut params = crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
params.glwe_noise_distribution = DynamicDistribution::new_t_uniform(9);
let config = ConfigBuilder::with_custom_parameters(params, None).build();
let crs = CompactPkeCrs::from_config(config, 32).unwrap();
let ck = ClientKey::generate(config);
let pk = CompactPublicKey::new(&ck);
let msg = random::<i32>();
let proven_compact_fhe_uint = crate::ProvenCompactFheInt32::try_encrypt(
msg,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_uint = proven_compact_fhe_uint
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted: i32 = fhe_uint.decrypt(&ck);
assert_eq!(decrypted, msg);
let messages = (0..4).map(|_| random()).collect::<Vec<i32>>();
let proven_compact_fhe_uint_list = crate::ProvenCompactFheInt32List::try_encrypt(
&messages,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_uints = proven_compact_fhe_uint_list
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = fhe_uints
.iter()
.map(|fb| fb.decrypt(&ck))
.collect::<Vec<i32>>();
assert_eq!(decrypted.as_slice(), &messages);
}

View File

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

View File

@@ -69,7 +69,7 @@ where
T: crate::integer::block_decomposition::DecomposableInto<u64>, T: crate::integer::block_decomposition::DecomposableInto<u64>,
Id: FheUintId, Id: FheUintId,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let ciphertext = key let ciphertext = key
@@ -175,7 +175,7 @@ where
T: crate::integer::block_decomposition::DecomposableInto<u64>, T: crate::integer::block_decomposition::DecomposableInto<u64>,
Id: FheUintId, Id: FheUintId,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(values: &'a [T], key: &CompactPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(values: &'a [T], key: &CompactPublicKey) -> Result<Self, Self::Error> {
let ciphertext = key let ciphertext = key

View File

@@ -91,7 +91,7 @@ where
Id: FheUintId, Id: FheUintId,
T: DecomposableInto<u64> + UnsignedNumeric, T: DecomposableInto<u64> + UnsignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &ClientKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &ClientKey) -> Result<Self, Self::Error> {
let inner = key let inner = key

View File

@@ -45,7 +45,7 @@ where
Id: FheUintId, Id: FheUintId,
T: DecomposableInto<u64> + UnsignedNumeric, T: DecomposableInto<u64> + UnsignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &ClientKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &ClientKey) -> Result<Self, Self::Error> {
let cpu_ciphertext = key let cpu_ciphertext = key
@@ -65,7 +65,7 @@ where
Id: FheUintId, Id: FheUintId,
T: DecomposableInto<u64> + UnsignedNumeric, T: DecomposableInto<u64> + UnsignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &PublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &PublicKey) -> Result<Self, Self::Error> {
let cpu_ciphertext = key let cpu_ciphertext = key
@@ -84,7 +84,7 @@ where
Id: FheUintId, Id: FheUintId,
T: DecomposableInto<u64> + UnsignedNumeric, T: DecomposableInto<u64> + UnsignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &CompressedPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &CompressedPublicKey) -> Result<Self, Self::Error> {
let cpu_ciphertext = key let cpu_ciphertext = key
@@ -102,7 +102,7 @@ where
Id: FheUintId, Id: FheUintId,
T: DecomposableInto<u64> + UnsignedNumeric, T: DecomposableInto<u64> + UnsignedNumeric,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> { fn try_encrypt(value: T, key: &CompactPublicKey) -> Result<Self, Self::Error> {
let cpu_ciphertext = key let cpu_ciphertext = key
@@ -121,7 +121,7 @@ where
T: DecomposableInto<u64> + UnsignedNumeric, T: DecomposableInto<u64> + UnsignedNumeric,
Id: FheUintId, Id: FheUintId,
{ {
type Error = crate::high_level_api::errors::Error; type Error = crate::Error;
fn try_encrypt_trivial(value: T) -> Result<Self, Self::Error> { fn try_encrypt_trivial(value: T) -> Result<Self, Self::Error> {
global_state::with_internal_keys(|key| match key { global_state::with_internal_keys(|key| match key {

View File

@@ -23,3 +23,5 @@ mod overflowing_ops;
mod scalar_ops; mod scalar_ops;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
#[cfg(feature = "zk-pok-experimental")]
mod zk;

View File

@@ -1,3 +1,5 @@
#[cfg(feature = "zk-pok-experimental")]
use super::zk::{ProvenCompactFheUint, ProvenCompactFheUintList};
use crate::high_level_api::integers::unsigned::base::{ use crate::high_level_api::integers::unsigned::base::{
FheUint, FheUintConformanceParams, FheUintId, FheUint, FheUintConformanceParams, FheUintId,
}; };
@@ -56,6 +58,12 @@ macro_rules! static_int_type {
#[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))] #[cfg_attr(all(doc, not(doctest)), cfg(feature = "integer"))]
pub type [<Compact FheUint $num_bits ListConformanceParams>] = CompactFheUintListConformanceParams<[<FheUint $num_bits Id>]>; pub type [<Compact FheUint $num_bits ListConformanceParams>] = CompactFheUintListConformanceParams<[<FheUint $num_bits Id>]>;
#[cfg(feature = "zk-pok-experimental")]
pub type [<ProvenCompactFheUint $num_bits>] = ProvenCompactFheUint<[<FheUint $num_bits Id>]>;
#[cfg(feature = "zk-pok-experimental")]
pub type [<ProvenCompactFheUint $num_bits List>] = ProvenCompactFheUintList<[<FheUint $num_bits Id>]>;
} }
}; };
} }

View File

@@ -10,7 +10,7 @@ use crate::{
CompressedPublicKey, Config, FheInt16, FheInt32, FheInt8, FheUint128, FheUint16, FheUint256, CompressedPublicKey, Config, FheInt16, FheInt32, FheInt8, FheUint128, FheUint16, FheUint256,
FheUint32, FheUint32ConformanceParams, FheUint32, FheUint32ConformanceParams,
}; };
use rand::{random, Rng}; use rand::prelude::*;
fn setup_cpu(params: Option<impl Into<PBSParameters>>) -> ClientKey { fn setup_cpu(params: Option<impl Into<PBSParameters>>) -> ClientKey {
let config = params let config = params
@@ -543,3 +543,49 @@ fn test_safe_deserialize_conformant_compact_fhe_uint32_list() {
assert_eq!(decrypted, expected); assert_eq!(decrypted, expected);
} }
} }
#[cfg(feature = "zk-pok-experimental")]
#[test]
fn test_fhe_uint_zk() {
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
let mut params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
params.glwe_noise_distribution = DynamicDistribution::new_t_uniform(9);
let config = ConfigBuilder::with_custom_parameters(params, None).build();
let crs = CompactPkeCrs::from_config(config, 32).unwrap();
let ck = ClientKey::generate(config);
let pk = CompactPublicKey::new(&ck);
let msg = random::<u32>();
let proven_compact_fhe_uint = crate::ProvenCompactFheUint32::try_encrypt(
msg,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_uint = proven_compact_fhe_uint
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted: u32 = fhe_uint.decrypt(&ck);
assert_eq!(decrypted, msg);
let messages = (0..4).map(|_| random()).collect::<Vec<u32>>();
let proven_compact_fhe_uint_list = crate::ProvenCompactFheUint32List::try_encrypt(
&messages,
crs.public_params(),
&pk,
ZkComputeLoad::Proof,
)
.unwrap();
let fhe_uints = proven_compact_fhe_uint_list
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = fhe_uints
.iter()
.map(|fb| fb.decrypt(&ck))
.collect::<Vec<u32>>();
assert_eq!(decrypted.as_slice(), &messages);
}

View File

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

View File

@@ -23,6 +23,13 @@ macro_rules! expand_pub_use_fhe_type(
)* )*
}; };
#[cfg(feature = "zk-pok-experimental")]
pub use $module_path::{
$(
[<ProvenCompact $fhe_type_name>],
[<ProvenCompact $fhe_type_name List>],
)*
};
} }
} }
); );
@@ -30,7 +37,6 @@ macro_rules! expand_pub_use_fhe_type(
pub use crate::core_crypto::commons::math::random::Seed; pub use crate::core_crypto::commons::math::random::Seed;
pub use crate::integer::oprf::SignedRandomizationSpec; pub use crate::integer::oprf::SignedRandomizationSpec;
pub use config::{Config, ConfigBuilder}; 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 global_state::{set_server_key, unset_server_key, with_server_key_as_context};
pub use integers::{ pub use integers::{
@@ -51,6 +57,8 @@ pub use crate::high_level_api::booleans::{
CompactFheBool, CompactFheBoolList, CompactFheBoolListConformanceParams, CompressedFheBool, CompactFheBool, CompactFheBoolList, CompactFheBoolListConformanceParams, CompressedFheBool,
FheBool, FheBoolConformanceParams, FheBool, FheBoolConformanceParams,
}; };
#[cfg(feature = "zk-pok-experimental")]
pub use crate::high_level_api::booleans::{ProvenCompactFheBool, ProvenCompactFheBoolList};
expand_pub_use_fhe_type!( expand_pub_use_fhe_type!(
pub use crate::high_level_api::integers{ pub use crate::high_level_api::integers{
FheUint2, FheUint4, FheUint6, FheUint8, FheUint10, FheUint12, FheUint14, FheUint16, FheUint2, FheUint4, FheUint6, FheUint8, FheUint10, FheUint12, FheUint14, FheUint16,
@@ -60,6 +68,7 @@ expand_pub_use_fhe_type!(
FheInt32, FheInt64, FheInt128, FheInt160, FheInt256 FheInt32, FheInt64, FheInt128, FheInt160, FheInt256
}; };
); );
pub use safe_serialize::safe_serialize; pub use safe_serialize::safe_serialize;
mod config; mod config;
@@ -68,12 +77,14 @@ mod keys;
mod traits; mod traits;
mod booleans; mod booleans;
pub mod errors; mod errors;
mod integers; mod integers;
pub(in crate::high_level_api) mod details; pub(in crate::high_level_api) mod details;
/// The tfhe prelude. /// The tfhe prelude.
pub mod prelude; pub mod prelude;
#[cfg(feature = "zk-pok-experimental")]
mod zk;
/// Devices supported by tfhe-rs /// Devices supported by tfhe-rs
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]

View File

@@ -99,9 +99,9 @@ fn test_with_seed() {
let builder = ConfigBuilder::default(); let builder = ConfigBuilder::default();
let config = builder.build(); let config = builder.build();
let cks1 = ClientKey::generate_with_seed(config.clone(), Seed(125)); let cks1 = ClientKey::generate_with_seed(config, Seed(125));
let cks2 = ClientKey::generate(config.clone()); let cks2 = ClientKey::generate(config);
let cks3 = ClientKey::generate_with_seed(config.clone(), Seed(125)); let cks3 = ClientKey::generate_with_seed(config, Seed(125));
let cks4 = ClientKey::generate_with_seed(config, Seed(127)); let cks4 = ClientKey::generate_with_seed(config, Seed(127));
let cks1_serialized = bincode::serialize(&cks1).unwrap(); let cks1_serialized = bincode::serialize(&cks1).unwrap();

View File

@@ -0,0 +1,11 @@
use crate::zk::CompactPkeCrs;
use crate::Config;
impl CompactPkeCrs {
pub fn from_config(config: Config, max_bit_size: usize) -> crate::Result<Self> {
let max_num_message =
max_bit_size / config.inner.block_parameters.message_modulus().0.ilog2() as usize;
let crs = Self::from_shortint_params(config.inner.block_parameters, max_num_message)?;
Ok(crs)
}
}

View File

@@ -27,6 +27,11 @@ pub trait Recomposable:
+ Shl<u32, Output = Self> + Shl<u32, Output = Self>
+ Sub<Self, Output = Self> + Sub<Self, Output = Self>
{ {
// TODO: need for wrapping arithmetic traits
// This is a wrapping add but to avoid conflicts with other parts of the code using external
// wrapping traits definition we change the name here
#[must_use]
fn recomposable_wrapping_add(self, other: Self) -> Self;
} }
// Convenience traits have simpler bounds // Convenience traits have simpler bounds
@@ -39,7 +44,12 @@ macro_rules! impl_recomposable_decomposable {
) => { ) => {
$( $(
impl Decomposable for $type { } impl Decomposable for $type { }
impl Recomposable for $type { } impl Recomposable for $type {
#[inline]
fn recomposable_wrapping_add(self, other: Self) -> Self {
self.wrapping_add(other)
}
}
impl RecomposableFrom<u64> for $type { } impl RecomposableFrom<u64> for $type { }
impl DecomposableInto<u64> for $type { } impl DecomposableInto<u64> for $type { }
impl RecomposableFrom<u8> for $type { } impl RecomposableFrom<u8> for $type { }
@@ -51,14 +61,26 @@ macro_rules! impl_recomposable_decomposable {
impl_recomposable_decomposable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128,); impl_recomposable_decomposable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128,);
impl<const N: usize> Decomposable for StaticSignedBigInt<N> {} impl<const N: usize> Decomposable for StaticSignedBigInt<N> {}
impl<const N: usize> Recomposable for StaticSignedBigInt<N> {} impl<const N: usize> Recomposable for StaticSignedBigInt<N> {
#[inline]
fn recomposable_wrapping_add(mut self, other: Self) -> Self {
self.add_assign(other);
self
}
}
impl<const N: usize> RecomposableFrom<u64> for StaticSignedBigInt<N> {} impl<const N: usize> RecomposableFrom<u64> for StaticSignedBigInt<N> {}
impl<const N: usize> RecomposableFrom<u8> for StaticSignedBigInt<N> {} impl<const N: usize> RecomposableFrom<u8> for StaticSignedBigInt<N> {}
impl<const N: usize> DecomposableInto<u64> for StaticSignedBigInt<N> {} impl<const N: usize> DecomposableInto<u64> for StaticSignedBigInt<N> {}
impl<const N: usize> DecomposableInto<u8> for StaticSignedBigInt<N> {} impl<const N: usize> DecomposableInto<u8> for StaticSignedBigInt<N> {}
impl<const N: usize> Decomposable for StaticUnsignedBigInt<N> {} impl<const N: usize> Decomposable for StaticUnsignedBigInt<N> {}
impl<const N: usize> Recomposable for StaticUnsignedBigInt<N> {} impl<const N: usize> Recomposable for StaticUnsignedBigInt<N> {
#[inline]
fn recomposable_wrapping_add(mut self, other: Self) -> Self {
self.add_assign(other);
self
}
}
impl<const N: usize> RecomposableFrom<u64> for StaticUnsignedBigInt<N> {} impl<const N: usize> RecomposableFrom<u64> for StaticUnsignedBigInt<N> {}
impl<const N: usize> RecomposableFrom<u8> for StaticUnsignedBigInt<N> {} impl<const N: usize> RecomposableFrom<u8> for StaticUnsignedBigInt<N> {}
impl<const N: usize> DecomposableInto<u64> for StaticUnsignedBigInt<N> {} impl<const N: usize> DecomposableInto<u64> for StaticUnsignedBigInt<N> {}
@@ -258,7 +280,7 @@ where
} }
block <<= self.bit_pos; block <<= self.bit_pos;
self.data += block; self.data = self.data.recomposable_wrapping_add(block);
self.bit_pos += self.num_bits_in_block; self.bit_pos += self.num_bits_in_block;
true true

View File

@@ -65,6 +65,11 @@ pub mod wopbs;
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
pub mod gpu; pub mod gpu;
#[cfg(feature = "zk-pok-experimental")]
mod zk;
#[cfg(feature = "zk-pok-experimental")]
pub use zk::ProvenCompactCiphertextList;
pub use bigint::i256::I256; pub use bigint::i256::I256;
pub use bigint::i512::I512; pub use bigint::i512::I512;
@@ -76,7 +81,9 @@ pub use ciphertext::{
SignedRadixCiphertext, SignedRadixCiphertext,
}; };
pub use client_key::{ClientKey, CrtClientKey, RadixClientKey}; pub use client_key::{ClientKey, CrtClientKey, RadixClientKey};
pub use public_key::{CompressedCompactPublicKey, CompressedPublicKey, PublicKey}; pub use public_key::{
CompactPublicKey, CompressedCompactPublicKey, CompressedPublicKey, PublicKey,
};
pub use server_key::{CheckError, CompressedServerKey, ServerKey}; pub use server_key::{CheckError, CompressedServerKey, ServerKey};
/// Enum to indicate which kind of computations the [`ServerKey`] will be performing, this changes /// Enum to indicate which kind of computations the [`ServerKey`] will be performing, this changes

View File

@@ -241,6 +241,7 @@ impl ServerKey {
let counter_num_blocks = ((num_bits_in_ciphertext - 1).ilog2() + 1 + 1) let counter_num_blocks = ((num_bits_in_ciphertext - 1).ilog2() + 1 + 1)
.div_ceil(self.message_modulus().0.ilog2()) as usize; .div_ceil(self.message_modulus().0.ilog2()) as usize;
// 11111000
// x.ilog2() = (x.num_bit() - 1) - x.leading_zeros() // x.ilog2() = (x.num_bit() - 1) - x.leading_zeros()
// - (x.num_bit() - 1) is trivially known // - (x.num_bit() - 1) is trivially known
// - we can get leading zeros via a sum // - we can get leading zeros via a sum

139
tfhe/src/integer/zk.rs Normal file
View File

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

View File

@@ -91,6 +91,9 @@ macro_rules! create_wrapper_type_non_native_type (
compressed_type_name: $compressed_type_name:ident, compressed_type_name: $compressed_type_name:ident,
compact_type_name: $compact_type_name:ident, compact_type_name: $compact_type_name:ident,
compact_list_type_name: $compact_list_type_name:ident, compact_list_type_name: $compact_list_type_name:ident,
proven_type: $proven_type:ident,
proven_compact_type_name: $proven_compact_type_name:ident,
proven_compact_list_type_name: $proven_compact_list_type_name:ident,
rust_type: $rust_type:ty $(,)? rust_type: $rust_type:ty $(,)?
} }
) => { ) => {
@@ -376,6 +379,146 @@ macro_rules! create_wrapper_type_non_native_type (
}) })
} }
} }
#[cfg(feature = "zk-pok-experimental")]
#[wasm_bindgen]
pub struct $proven_compact_type_name(pub(crate) crate::high_level_api::$proven_compact_type_name);
#[cfg(feature = "zk-pok-experimental")]
#[wasm_bindgen]
impl $proven_compact_type_name {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
value: JsValue,
public_params: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey,
compute_load: crate::js_on_wasm_api::js_high_level_api::zk::ZkComputeLoad,
) -> Result<$proven_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::$proven_compact_type_name::try_encrypt(
value,
&public_params.0,
&public_key.0,
compute_load.into()
).map($proven_compact_type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn verifies(
&self,
public_parameters: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey
) -> bool {
self.0.verify(&public_parameters.0, &public_key.0).is_valid()
}
#[wasm_bindgen]
pub fn verify_and_expand(
&self,
public_parameters: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey
) -> Result<$type_name, JsError> {
catch_panic(||{
self.0
.clone()
.verify_and_expand(&public_parameters.0, &public_key.0)
.map($type_name)
.unwrap()
})
}
#[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<$proven_compact_type_name, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map($proven_compact_type_name)
.map_err(into_js_error)
})
}
}
#[cfg(feature = "zk-pok-experimental")]
#[wasm_bindgen]
pub struct $proven_compact_list_type_name(pub(crate) crate::high_level_api::$proven_compact_list_type_name);
#[cfg(feature = "zk-pok-experimental")]
#[wasm_bindgen]
impl $proven_compact_list_type_name {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
values: Vec<JsValue>,
public_params: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey,
compute_load: crate::js_on_wasm_api::js_high_level_api::zk::ZkComputeLoad,
) -> Result<$proven_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::$proven_compact_list_type_name::try_encrypt(
&values,
&public_params.0,
&public_key.0,
compute_load.into()
).map($proven_compact_list_type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn verifies(
&self,
public_parameters: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey
) -> bool {
self.0.verify(&public_parameters.0, &public_key.0).is_valid()
}
#[wasm_bindgen]
pub fn verify_and_expand(
&self,
public_parameters: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey
) -> Result<Vec<$type_name>, JsError> {
catch_panic(||{
self.0
.clone()
.verify_and_expand(&public_parameters.0, &public_key.0)
.map(|vec| vec.into_iter().map($type_name).collect::<Vec<_>>())
.unwrap()
})
}
#[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<$proven_compact_list_type_name, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map($proven_compact_list_type_name)
.map_err(into_js_error)
})
}
}
}; };
( (
@@ -385,6 +528,9 @@ macro_rules! create_wrapper_type_non_native_type (
compressed_type_name: $compressed_type_name:ident, compressed_type_name: $compressed_type_name:ident,
compact_type_name: $compact_type_name:ident, compact_type_name: $compact_type_name:ident,
compact_list_type_name: $compact_list_type_name:ident, compact_list_type_name: $compact_list_type_name:ident,
proven_type: $proven_type:ident,
proven_compact_type_name: $proven_compact_type_name:ident,
proven_compact_list_type_name: $proven_compact_list_type_name:ident,
rust_type: $rust_type:ty $(,)? rust_type: $rust_type:ty $(,)?
} }
),* ),*
@@ -397,6 +543,9 @@ macro_rules! create_wrapper_type_non_native_type (
compressed_type_name: $compressed_type_name, compressed_type_name: $compressed_type_name,
compact_type_name: $compact_type_name, compact_type_name: $compact_type_name,
compact_list_type_name: $compact_list_type_name, compact_list_type_name: $compact_list_type_name,
proven_type: $proven_type,
proven_compact_type_name: $proven_compact_type_name,
proven_compact_list_type_name: $proven_compact_list_type_name,
rust_type: $rust_type rust_type: $rust_type
} }
); );
@@ -405,25 +554,34 @@ macro_rules! create_wrapper_type_non_native_type (
); );
create_wrapper_type_non_native_type!( create_wrapper_type_non_native_type!(
{
type_name: FheUint160,
compressed_type_name: CompressedFheUint160,
compact_type_name: CompactFheUint160,
compact_list_type_name: CompactFheUint160List,
rust_type: U256,
},
{ {
type_name: FheUint128, type_name: FheUint128,
compressed_type_name: CompressedFheUint128, compressed_type_name: CompressedFheUint128,
compact_type_name: CompactFheUint128, compact_type_name: CompactFheUint128,
compact_list_type_name: CompactFheUint128List, compact_list_type_name: CompactFheUint128List,
proven_type: ProvenFheUint128,
proven_compact_type_name: ProvenCompactFheUint128,
proven_compact_list_type_name: ProvenCompactFheUint128List,
rust_type: u128, rust_type: u128,
}, },
{
type_name: FheUint160,
compressed_type_name: CompressedFheUint160,
compact_type_name: CompactFheUint160,
compact_list_type_name: CompactFheUint160List,
proven_type: ProvenFheUint160,
proven_compact_type_name: ProvenCompactFheUint160,
proven_compact_list_type_name: ProvenCompactFheUint160List,
rust_type: U256,
},
{ {
type_name: FheUint256, type_name: FheUint256,
compressed_type_name: CompressedFheUint256, compressed_type_name: CompressedFheUint256,
compact_type_name: CompactFheUint256, compact_type_name: CompactFheUint256,
compact_list_type_name: CompactFheUint256List, compact_list_type_name: CompactFheUint256List,
proven_type: ProvenFheUint256,
proven_compact_type_name: ProvenCompactFheUint256,
proven_compact_list_type_name: ProvenCompactFheUint256List,
rust_type: U256, rust_type: U256,
}, },
// Signed // Signed
@@ -432,6 +590,9 @@ create_wrapper_type_non_native_type!(
compressed_type_name: CompressedFheInt128, compressed_type_name: CompressedFheInt128,
compact_type_name: CompactFheInt128, compact_type_name: CompactFheInt128,
compact_list_type_name: CompactFheInt128List, compact_list_type_name: CompactFheInt128List,
proven_type: ProvenFheInt128,
proven_compact_type_name: ProvenCompactFheInt128,
proven_compact_list_type_name: ProvenCompactFheInt128List,
rust_type: i128, rust_type: i128,
}, },
{ {
@@ -439,6 +600,9 @@ create_wrapper_type_non_native_type!(
compressed_type_name: CompressedFheInt160, compressed_type_name: CompressedFheInt160,
compact_type_name: CompactFheInt160, compact_type_name: CompactFheInt160,
compact_list_type_name: CompactFheInt160List, compact_list_type_name: CompactFheInt160List,
proven_type: ProvenFheInt160,
proven_compact_type_name: ProvenCompactFheInt160,
proven_compact_list_type_name: ProvenCompactFheInt160List,
rust_type: I256, rust_type: I256,
}, },
{ {
@@ -446,6 +610,9 @@ create_wrapper_type_non_native_type!(
compressed_type_name: CompressedFheInt256, compressed_type_name: CompressedFheInt256,
compact_type_name: CompactFheInt256, compact_type_name: CompactFheInt256,
compact_list_type_name: CompactFheInt256List, compact_list_type_name: CompactFheInt256List,
proven_type: ProvenFheInt256,
proven_compact_type_name: ProvenCompactFheInt256,
proven_compact_list_type_name: ProvenCompactFheInt256List,
rust_type: I256, rust_type: I256,
}, },
); );
@@ -460,6 +627,9 @@ macro_rules! create_wrapper_type_that_has_native_type (
compressed_type_name: $compressed_type_name:ident, compressed_type_name: $compressed_type_name:ident,
compact_type_name: $compact_type_name:ident, compact_type_name: $compact_type_name:ident,
compact_list_type_name: $compact_list_type_name:ident, compact_list_type_name: $compact_list_type_name:ident,
proven_type: $proven_type:ident,
proven_compact_type_name: $proven_compact_type_name:ident,
proven_compact_list_type_name: $proven_compact_list_type_name:ident,
native_type: $native_type:ty $(,)? native_type: $native_type:ty $(,)?
} }
) => { ) => {
@@ -708,6 +878,116 @@ macro_rules! create_wrapper_type_that_has_native_type (
} }
} }
#[cfg(feature = "zk-pok-experimental")]
#[wasm_bindgen]
pub struct $proven_compact_type_name(pub(crate) crate::high_level_api::$proven_compact_type_name);
#[cfg(feature = "zk-pok-experimental")]
#[wasm_bindgen]
impl $proven_compact_type_name {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
value: $native_type,
public_params: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey,
compute_load: crate::js_on_wasm_api::js_high_level_api::zk::ZkComputeLoad,
) -> Result<$proven_compact_type_name, JsError> {
catch_panic_result(|| {
crate::high_level_api::$proven_compact_type_name::try_encrypt(
value,
&public_params.0,
&public_key.0,
compute_load.into()
).map($proven_compact_type_name)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn verifies(
&self,
public_parameters: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey
) -> bool {
self.0.verify(&public_parameters.0, &public_key.0).is_valid()
}
#[wasm_bindgen]
pub fn verify_and_expand(
&self,
public_parameters: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey
) -> Result<$type_name, JsError> {
catch_panic(||{
self.0
.clone()
.verify_and_expand(&public_parameters.0, &public_key.0)
.map($type_name)
.unwrap()
})
}
#[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<$proven_compact_type_name, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map($proven_compact_type_name)
.map_err(into_js_error)
})
}
}
#[cfg(feature = "zk-pok-experimental")]
#[wasm_bindgen]
pub struct $proven_compact_list_type_name(pub(crate) crate::high_level_api::$proven_compact_list_type_name);
#[cfg(feature = "zk-pok-experimental")]
#[wasm_bindgen]
impl $proven_compact_list_type_name {
#[wasm_bindgen]
pub fn verifies(
&self,
public_parameters: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey
) -> bool {
self.0.verify(&public_parameters.0, &public_key.0).is_valid()
}
#[wasm_bindgen]
pub fn verify_and_expand(
&self,
public_parameters: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey
) -> Result<Vec<$type_name>, JsError> {
catch_panic(||{
self.0
.clone()
.verify_and_expand(&public_parameters.0, &public_key.0)
.map(|vec| vec.into_iter().map($type_name).collect::<Vec<_>>())
.unwrap()
})
}
#[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<$proven_compact_list_type_name, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map($proven_compact_list_type_name)
.map_err(into_js_error)
})
}
}
}; };
( (
$( $(
@@ -716,6 +996,9 @@ macro_rules! create_wrapper_type_that_has_native_type (
compressed_type_name: $compressed_type_name:ident, compressed_type_name: $compressed_type_name:ident,
compact_type_name: $compact_type_name:ident, compact_type_name: $compact_type_name:ident,
compact_list_type_name: $compact_list_type_name:ident, compact_list_type_name: $compact_list_type_name:ident,
proven_type: $proven_type:ident,
proven_compact_type_name: $proven_compact_type_name:ident,
proven_compact_list_type_name: $proven_compact_list_type_name:ident,
native_type: $native_type:ty $(,)? native_type: $native_type:ty $(,)?
} }
),* ),*
@@ -728,6 +1011,9 @@ macro_rules! create_wrapper_type_that_has_native_type (
compressed_type_name: $compressed_type_name, compressed_type_name: $compressed_type_name,
compact_type_name: $compact_type_name, compact_type_name: $compact_type_name,
compact_list_type_name: $compact_list_type_name, compact_list_type_name: $compact_list_type_name,
proven_type: $proven_type,
proven_compact_type_name: $proven_compact_type_name,
proven_compact_list_type_name: $proven_compact_list_type_name,
native_type: $native_type native_type: $native_type
} }
); );
@@ -741,6 +1027,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheBool, compressed_type_name: CompressedFheBool,
compact_type_name: CompactFheBool, compact_type_name: CompactFheBool,
compact_list_type_name: CompactFheBoolList, compact_list_type_name: CompactFheBoolList,
proven_type: ProvenFheBool,
proven_compact_type_name: ProvenCompactFheBool,
proven_compact_list_type_name: ProvenCompactFheBoolList,
native_type: bool, native_type: bool,
}, },
{ {
@@ -748,6 +1037,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint2, compressed_type_name: CompressedFheUint2,
compact_type_name: CompactFheUint2, compact_type_name: CompactFheUint2,
compact_list_type_name: CompactFheUint2List, compact_list_type_name: CompactFheUint2List,
proven_type: ProvenFheUint2,
proven_compact_type_name: ProvenCompactFheUint2,
proven_compact_list_type_name: ProvenCompactFheUint2List,
native_type: u8, native_type: u8,
}, },
{ {
@@ -755,6 +1047,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint4, compressed_type_name: CompressedFheUint4,
compact_type_name: CompactFheUint4, compact_type_name: CompactFheUint4,
compact_list_type_name: CompactFheUint4List, compact_list_type_name: CompactFheUint4List,
proven_type: ProvenFheUint4,
proven_compact_type_name: ProvenCompactFheUint4,
proven_compact_list_type_name: ProvenCompactFheUint4List,
native_type: u8, native_type: u8,
}, },
{ {
@@ -762,6 +1057,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint6, compressed_type_name: CompressedFheUint6,
compact_type_name: CompactFheUint6, compact_type_name: CompactFheUint6,
compact_list_type_name: CompactFheUint6List, compact_list_type_name: CompactFheUint6List,
proven_type: ProvenFheUint6,
proven_compact_type_name: ProvenCompactFheUint6,
proven_compact_list_type_name: ProvenCompactFheUint6List,
native_type: u8, native_type: u8,
}, },
{ {
@@ -769,6 +1067,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint8, compressed_type_name: CompressedFheUint8,
compact_type_name: CompactFheUint8, compact_type_name: CompactFheUint8,
compact_list_type_name: CompactFheUint8List, compact_list_type_name: CompactFheUint8List,
proven_type: ProvenFheUint8,
proven_compact_type_name: ProvenCompactFheUint8,
proven_compact_list_type_name: ProvenCompactFheUint8List,
native_type: u8, native_type: u8,
}, },
{ {
@@ -776,6 +1077,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint10, compressed_type_name: CompressedFheUint10,
compact_type_name: CompactFheUint10, compact_type_name: CompactFheUint10,
compact_list_type_name: CompactFheUint10List, compact_list_type_name: CompactFheUint10List,
proven_type: ProvenFheUint10,
proven_compact_type_name: ProvenCompactFheUint10,
proven_compact_list_type_name: ProvenCompactFheUint10List,
native_type: u16, native_type: u16,
}, },
{ {
@@ -783,6 +1087,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint12, compressed_type_name: CompressedFheUint12,
compact_type_name: CompactFheUint12, compact_type_name: CompactFheUint12,
compact_list_type_name: CompactFheUint12List, compact_list_type_name: CompactFheUint12List,
proven_type: ProvenFheUint12,
proven_compact_type_name: ProvenCompactFheUint12,
proven_compact_list_type_name: ProvenCompactFheUint12List,
native_type: u16, native_type: u16,
}, },
{ {
@@ -790,6 +1097,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint14, compressed_type_name: CompressedFheUint14,
compact_type_name: CompactFheUint14, compact_type_name: CompactFheUint14,
compact_list_type_name: CompactFheUint14List, compact_list_type_name: CompactFheUint14List,
proven_type: ProvenFheUint14,
proven_compact_type_name: ProvenCompactFheUint14,
proven_compact_list_type_name: ProvenCompactFheUint14List,
native_type: u16, native_type: u16,
}, },
{ {
@@ -797,6 +1107,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint16, compressed_type_name: CompressedFheUint16,
compact_type_name: CompactFheUint16, compact_type_name: CompactFheUint16,
compact_list_type_name: CompactFheUint16List, compact_list_type_name: CompactFheUint16List,
proven_type: ProvenFheUint16,
proven_compact_type_name: ProvenCompactFheUint16,
proven_compact_list_type_name: ProvenCompactFheUint16List,
native_type: u16, native_type: u16,
}, },
{ {
@@ -804,6 +1117,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint32, compressed_type_name: CompressedFheUint32,
compact_type_name: CompactFheUint32, compact_type_name: CompactFheUint32,
compact_list_type_name: CompactFheUint32List, compact_list_type_name: CompactFheUint32List,
proven_type: ProvenFheUint32,
proven_compact_type_name: ProvenCompactFheUint32,
proven_compact_list_type_name: ProvenCompactFheUint32List,
native_type: u32, native_type: u32,
}, },
{ {
@@ -811,6 +1127,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheUint64, compressed_type_name: CompressedFheUint64,
compact_type_name: CompactFheUint64, compact_type_name: CompactFheUint64,
compact_list_type_name: CompactFheUint64List, compact_list_type_name: CompactFheUint64List,
proven_type: ProvenFheUint64,
proven_compact_type_name: ProvenCompactFheUint64,
proven_compact_list_type_name: ProvenCompactFheUint64List,
native_type: u64, native_type: u64,
}, },
// Signed // Signed
@@ -819,6 +1138,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt2, compressed_type_name: CompressedFheInt2,
compact_type_name: CompactFheInt2, compact_type_name: CompactFheInt2,
compact_list_type_name: CompactFheInt2List, compact_list_type_name: CompactFheInt2List,
proven_type: ProvenFheInt2,
proven_compact_type_name: ProvenCompactFheInt2,
proven_compact_list_type_name: ProvenCompactFheInt2List,
native_type: i8, native_type: i8,
}, },
{ {
@@ -826,6 +1148,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt4, compressed_type_name: CompressedFheInt4,
compact_type_name: CompactFheInt4, compact_type_name: CompactFheInt4,
compact_list_type_name: CompactFheInt4List, compact_list_type_name: CompactFheInt4List,
proven_type: ProvenFheInt4,
proven_compact_type_name: ProvenCompactFheInt4,
proven_compact_list_type_name: ProvenCompactFheInt4List,
native_type: i8, native_type: i8,
}, },
{ {
@@ -833,6 +1158,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt6, compressed_type_name: CompressedFheInt6,
compact_type_name: CompactFheInt6, compact_type_name: CompactFheInt6,
compact_list_type_name: CompactFheInt6List, compact_list_type_name: CompactFheInt6List,
proven_type: ProvenFheInt6,
proven_compact_type_name: ProvenCompactFheInt6,
proven_compact_list_type_name: ProvenCompactFheInt6List,
native_type: i8, native_type: i8,
}, },
{ {
@@ -840,6 +1168,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt8, compressed_type_name: CompressedFheInt8,
compact_type_name: CompactFheInt8, compact_type_name: CompactFheInt8,
compact_list_type_name: CompactFheInt8List, compact_list_type_name: CompactFheInt8List,
proven_type: ProvenFheInt8,
proven_compact_type_name: ProvenCompactFheInt8,
proven_compact_list_type_name: ProvenCompactFheInt8List,
native_type: i8, native_type: i8,
}, },
{ {
@@ -847,6 +1178,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt10, compressed_type_name: CompressedFheInt10,
compact_type_name: CompactFheInt10, compact_type_name: CompactFheInt10,
compact_list_type_name: CompactFheInt10List, compact_list_type_name: CompactFheInt10List,
proven_type: ProvenFheInt10,
proven_compact_type_name: ProvenCompactFheInt10,
proven_compact_list_type_name: ProvenCompactFheInt10List,
native_type: i16, native_type: i16,
}, },
{ {
@@ -854,6 +1188,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt12, compressed_type_name: CompressedFheInt12,
compact_type_name: CompactFheInt12, compact_type_name: CompactFheInt12,
compact_list_type_name: CompactFheInt12List, compact_list_type_name: CompactFheInt12List,
proven_type: ProvenFheInt12,
proven_compact_type_name: ProvenCompactFheInt12,
proven_compact_list_type_name: ProvenCompactFheInt12List,
native_type: i16, native_type: i16,
}, },
{ {
@@ -861,6 +1198,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt14, compressed_type_name: CompressedFheInt14,
compact_type_name: CompactFheInt14, compact_type_name: CompactFheInt14,
compact_list_type_name: CompactFheInt14List, compact_list_type_name: CompactFheInt14List,
proven_type: ProvenFheInt14,
proven_compact_type_name: ProvenCompactFheInt14,
proven_compact_list_type_name: ProvenCompactFheInt14List,
native_type: i16, native_type: i16,
}, },
{ {
@@ -868,6 +1208,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt16, compressed_type_name: CompressedFheInt16,
compact_type_name: CompactFheInt16, compact_type_name: CompactFheInt16,
compact_list_type_name: CompactFheInt16List, compact_list_type_name: CompactFheInt16List,
proven_type: ProvenFheInt16,
proven_compact_type_name: ProvenCompactFheInt16,
proven_compact_list_type_name: ProvenCompactFheInt16List,
native_type: i16, native_type: i16,
}, },
{ {
@@ -875,6 +1218,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt32, compressed_type_name: CompressedFheInt32,
compact_type_name: CompactFheInt32, compact_type_name: CompactFheInt32,
compact_list_type_name: CompactFheInt32List, compact_list_type_name: CompactFheInt32List,
proven_type: ProvenFheInt32,
proven_compact_type_name: ProvenCompactFheInt32,
proven_compact_list_type_name: ProvenCompactFheInt32List,
native_type: i32, native_type: i32,
}, },
{ {
@@ -882,6 +1228,9 @@ create_wrapper_type_that_has_native_type!(
compressed_type_name: CompressedFheInt64, compressed_type_name: CompressedFheInt64,
compact_type_name: CompactFheInt64, compact_type_name: CompactFheInt64,
compact_list_type_name: CompactFheInt64List, compact_list_type_name: CompactFheInt64List,
proven_type: ProvenFheInt64,
proven_compact_type_name: ProvenCompactFheInt64,
proven_compact_list_type_name: ProvenCompactFheInt64List,
native_type: i64, native_type: i64,
}, },
); );
@@ -967,3 +1316,93 @@ impl CompactFheBoolList {
}) })
} }
} }
#[cfg(feature = "zk-pok-experimental")]
macro_rules! define_prove_and_encrypt_list_with_compact_public_key {
(
$(
{$proven_compact_list_type_name:ident, $native_type:ty}
),*
$(,)?
) => {
$(
#[wasm_bindgen]
impl $proven_compact_list_type_name {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
values: Vec<$native_type>,
public_params: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey,
compute_load: crate::js_on_wasm_api::js_high_level_api::zk::ZkComputeLoad,
) -> Result<$proven_compact_list_type_name, JsError> {
catch_panic_result(|| {
$crate::high_level_api::$proven_compact_list_type_name::try_encrypt(
&values,
&public_params.0,
&public_key.0,
compute_load.into(),
).map($proven_compact_list_type_name)
.map_err(into_js_error)
})
}
}
)*
};
}
#[cfg(feature = "zk-pok-experimental")]
define_prove_and_encrypt_list_with_compact_public_key!(
{ProvenCompactFheUint2List, u8},
{ProvenCompactFheUint4List, u8},
{ProvenCompactFheUint6List, u8},
{ProvenCompactFheUint8List, u8},
{ProvenCompactFheUint12List, u16},
{ProvenCompactFheUint14List, u16},
{ProvenCompactFheUint16List, u16},
{ProvenCompactFheUint32List, u32},
{ProvenCompactFheUint64List, u64},
// Signed
{ProvenCompactFheInt2List, i8},
{ProvenCompactFheInt4List, i8},
{ProvenCompactFheInt6List, i8},
{ProvenCompactFheInt8List, i8},
{ProvenCompactFheInt12List, i16},
{ProvenCompactFheInt14List, i16},
{ProvenCompactFheInt16List, i16},
{ProvenCompactFheInt32List, i32},
{ProvenCompactFheInt64List, i64},
);
#[cfg(feature = "zk-pok-experimental")]
#[allow(clippy::use_self)]
#[allow(clippy::needless_pass_by_value)]
#[wasm_bindgen]
impl ProvenCompactFheBoolList {
#[wasm_bindgen]
pub fn encrypt_with_compact_public_key(
values: Vec<JsValue>,
public_params: &crate::js_on_wasm_api::js_high_level_api::zk::CompactPkePublicParams,
public_key: &crate::js_on_wasm_api::js_high_level_api::keys::TfheCompactPublicKey,
compute_load: crate::js_on_wasm_api::js_high_level_api::zk::ZkComputeLoad,
) -> Result<ProvenCompactFheBoolList, JsError> {
catch_panic_result(|| {
let booleans = values
.iter()
.map(|jsvalue| {
jsvalue
.as_bool()
.ok_or_else(|| JsError::new("Value is not a boolean"))
})
.collect::<Result<Vec<_>, JsError>>()?;
crate::high_level_api::ProvenCompactFheBoolList::try_encrypt(
&booleans,
&public_params.0,
&public_key.0,
compute_load.into(),
)
.map(ProvenCompactFheBoolList)
.map_err(into_js_error)
})
}
}

View File

@@ -15,7 +15,7 @@ pub struct TfheClientKey(pub(crate) hlapi::ClientKey);
impl TfheClientKey { impl TfheClientKey {
#[wasm_bindgen] #[wasm_bindgen]
pub fn generate(config: &TfheConfig) -> Result<TfheClientKey, JsError> { pub fn generate(config: &TfheConfig) -> Result<TfheClientKey, JsError> {
catch_panic(|| Self(hlapi::ClientKey::generate(config.0.clone()))) catch_panic(|| Self(hlapi::ClientKey::generate(config.0)))
} }
#[wasm_bindgen] #[wasm_bindgen]
@@ -26,7 +26,7 @@ impl TfheClientKey {
catch_panic_result(|| { catch_panic_result(|| {
let seed = let seed =
u128::try_from(seed).map_err(|_| JsError::new("Value does not fit in a u128"))?; 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)); let key = hlapi::ClientKey::generate_with_seed(config.0, crate::Seed(seed));
Ok(Self(key)) Ok(Self(key))
}) })
} }

View File

@@ -5,6 +5,8 @@ pub(crate) mod integers;
// using Self does not work well with #[wasm_bindgen] macro // using Self does not work well with #[wasm_bindgen] macro
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
pub(crate) mod keys; pub(crate) mod keys;
#[cfg(feature = "zk-pok-experimental")]
mod zk;
pub(crate) fn into_js_error<E: std::fmt::Debug>(e: E) -> wasm_bindgen::JsError { pub(crate) fn into_js_error<E: std::fmt::Debug>(e: E) -> wasm_bindgen::JsError {
wasm_bindgen::JsError::new(format!("{e:?}").as_str()) wasm_bindgen::JsError::new(format!("{e:?}").as_str())

View File

@@ -0,0 +1,76 @@
use wasm_bindgen::prelude::*;
use crate::js_on_wasm_api::js_high_level_api::config::TfheConfig;
use crate::js_on_wasm_api::js_high_level_api::{catch_panic_result, into_js_error};
use crate::js_on_wasm_api::shortint::ShortintParameters;
#[derive(Copy, Clone, Eq, PartialEq)]
#[wasm_bindgen]
pub enum ZkComputeLoad {
Proof,
Verify,
}
impl Into<crate::zk::ZkComputeLoad> for ZkComputeLoad {
fn into(self) -> crate::zk::ZkComputeLoad {
match self {
Self::Proof => crate::zk::ZkComputeLoad::Proof,
Self::Verify => crate::zk::ZkComputeLoad::Verify,
}
}
}
#[wasm_bindgen]
pub struct CompactPkeCrs(pub(crate) crate::core_crypto::entities::CompactPkeCrs);
#[wasm_bindgen]
pub struct CompactPkePublicParams(pub(crate) crate::zk::CompactPkePublicParams);
#[wasm_bindgen]
impl CompactPkePublicParams {
#[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<CompactPkePublicParams, JsError> {
catch_panic_result(|| {
bincode::deserialize(buffer)
.map(CompactPkePublicParams)
.map_err(into_js_error)
})
}
}
#[wasm_bindgen]
impl CompactPkeCrs {
#[wasm_bindgen]
pub fn from_parameters(
parameters: ShortintParameters,
max_num_message: usize,
) -> Result<CompactPkeCrs, JsError> {
catch_panic_result(|| {
crate::core_crypto::entities::CompactPkeCrs::from_shortint_params(
parameters.0,
max_num_message,
)
.map(CompactPkeCrs)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn from_config(config: &TfheConfig, max_num_bits: usize) -> Result<CompactPkeCrs, JsError> {
catch_panic_result(|| {
crate::core_crypto::entities::CompactPkeCrs::from_config(config.0, max_num_bits)
.map(CompactPkeCrs)
.map_err(into_js_error)
})
}
#[wasm_bindgen]
pub fn public_params(&self) -> CompactPkePublicParams {
CompactPkePublicParams(self.0.public_params().clone())
}
}

View File

@@ -30,13 +30,136 @@ pub struct Shortint {}
#[wasm_bindgen] #[wasm_bindgen]
pub struct ShortintParameters(pub(crate) crate::shortint::ClassicPBSParameters); pub struct ShortintParameters(pub(crate) crate::shortint::ClassicPBSParameters);
#[wasm_bindgen]
impl ShortintParameters {
#[wasm_bindgen]
pub fn lwe_dimension(&self) -> usize {
self.0.lwe_dimension.0
}
#[wasm_bindgen]
pub fn set_lwe_dimension(&mut self, new_value: usize) {
self.0.lwe_dimension.0 = new_value;
}
#[wasm_bindgen]
pub fn glwe_dimension(&self) -> usize {
self.0.glwe_dimension.0
}
#[wasm_bindgen]
pub fn set_glwe_dimension(&mut self, new_value: usize) {
self.0.glwe_dimension.0 = new_value;
}
#[wasm_bindgen]
pub fn polynomial_size(&self) -> usize {
self.0.polynomial_size.0
}
#[wasm_bindgen]
pub fn set_polynomial_size(&mut self, new_value: usize) {
self.0.polynomial_size.0 = new_value;
}
#[wasm_bindgen]
pub fn lwe_noise_distribution(&self) -> ShortintNoiseDistribution {
ShortintNoiseDistribution(self.0.lwe_noise_distribution)
}
#[wasm_bindgen]
pub fn set_lwe_noise_distribution(&mut self, new_value: &ShortintNoiseDistribution) {
self.0.lwe_noise_distribution = new_value.0;
}
#[wasm_bindgen]
pub fn glwe_noise_distribution(&self) -> ShortintNoiseDistribution {
ShortintNoiseDistribution(self.0.lwe_noise_distribution)
}
#[wasm_bindgen]
pub fn set_glwe_noise_distribution(&mut self, new_value: &ShortintNoiseDistribution) {
self.0.glwe_noise_distribution = new_value.0;
}
#[wasm_bindgen]
pub fn pbs_base_log(&self) -> usize {
self.0.pbs_base_log.0
}
#[wasm_bindgen]
pub fn set_pbs_base_log(&mut self, new_value: usize) {
self.0.pbs_base_log.0 = new_value;
}
#[wasm_bindgen]
pub fn pbs_level(&self) -> usize {
self.0.pbs_level.0
}
#[wasm_bindgen]
pub fn set_pbs_level(&mut self, new_value: usize) {
self.0.pbs_level.0 = new_value;
}
#[wasm_bindgen]
pub fn ks_base_log(&self) -> usize {
self.0.ks_base_log.0
}
#[wasm_bindgen]
pub fn set_ks_base_log(&mut self, new_value: usize) {
self.0.ks_base_log.0 = new_value;
}
#[wasm_bindgen]
pub fn ks_level(&self) -> usize {
self.0.ks_level.0
}
#[wasm_bindgen]
pub fn set_ks_level(&mut self, new_value: usize) {
self.0.ks_level.0 = new_value;
}
#[wasm_bindgen]
pub fn message_modulus(&self) -> usize {
self.0.message_modulus.0
}
#[wasm_bindgen]
pub fn set_message_modulus(&mut self, new_value: usize) {
self.0.message_modulus.0 = new_value;
}
#[wasm_bindgen]
pub fn carry_modulus(&self) -> usize {
self.0.carry_modulus.0
}
#[wasm_bindgen]
pub fn set_carry_modulus(&mut self, new_value: usize) {
self.0.carry_modulus.0 = new_value;
}
#[wasm_bindgen]
pub fn encryption_key_choice(&self) -> ShortintEncryptionKeyChoice {
self.0.encryption_key_choice.into()
}
#[wasm_bindgen]
pub fn set_encryption_key_choice(&mut self, new_value: ShortintEncryptionKeyChoice) {
self.0.encryption_key_choice = new_value.into();
}
}
#[wasm_bindgen] #[wasm_bindgen]
pub enum ShortintEncryptionKeyChoice { pub enum ShortintEncryptionKeyChoice {
Big, Big,
Small, Small,
} }
impl From<ShortintEncryptionKeyChoice> for crate::shortint::parameters::EncryptionKeyChoice { impl From<ShortintEncryptionKeyChoice> for EncryptionKeyChoice {
fn from(value: ShortintEncryptionKeyChoice) -> Self { fn from(value: ShortintEncryptionKeyChoice) -> Self {
match value { match value {
ShortintEncryptionKeyChoice::Big => Self::Big, ShortintEncryptionKeyChoice::Big => Self::Big,
@@ -45,6 +168,15 @@ impl From<ShortintEncryptionKeyChoice> for crate::shortint::parameters::Encrypti
} }
} }
impl From<EncryptionKeyChoice> for ShortintEncryptionKeyChoice {
fn from(value: EncryptionKeyChoice) -> Self {
match value {
EncryptionKeyChoice::Big => Self::Big,
EncryptionKeyChoice::Small => Self::Small,
}
}
}
#[wasm_bindgen] #[wasm_bindgen]
pub struct ShortintNoiseDistribution( pub struct ShortintNoiseDistribution(
pub(crate) crate::core_crypto::commons::math::random::DynamicDistribution<u64>, pub(crate) crate::core_crypto::commons::math::random::DynamicDistribution<u64>,

View File

@@ -109,7 +109,8 @@ mod js_on_wasm_api;
doctest, doctest,
feature = "shortint", feature = "shortint",
feature = "boolean", feature = "boolean",
feature = "integer" feature = "integer",
feature = "zk-pok-experimental"
))] ))]
mod test_user_docs; mod test_user_docs;
@@ -129,3 +130,10 @@ pub mod safe_deserialization;
pub mod conformance; pub mod conformance;
pub mod named; pub mod named;
pub mod error;
#[cfg(feature = "zk-pok-experimental")]
pub mod zk;
pub use error::{Error, ErrorKind};
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -9,3 +9,8 @@ pub use compact_list::*;
pub use compressed::*; pub use compressed::*;
pub use compressed_modulus_switched_ciphertext::*; pub use compressed_modulus_switched_ciphertext::*;
pub use standard::*; pub use standard::*;
#[cfg(feature = "zk-pok-experimental")]
pub use zk::*;
#[cfg(feature = "zk-pok-experimental")]
mod zk;

View File

@@ -0,0 +1,194 @@
use crate::core_crypto::algorithms::verify_lwe_compact_ciphertext_list;
use crate::core_crypto::prelude::verify_lwe_ciphertext;
use crate::shortint::ciphertext::CompactCiphertextList;
use crate::shortint::{Ciphertext, CompactPublicKey, EncryptionKeyChoice};
use crate::zk::{CompactPkeCrs, CompactPkeProof, CompactPkePublicParams, ZkVerificationOutCome};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
impl CompactPkeCrs {
/// Construct the CRS that corresponds to the given parameters
///
/// max_num_message is how many message a single proof can prove
pub fn from_shortint_params(
params: impl Into<crate::shortint::PBSParameters>,
max_num_message: usize,
) -> crate::Result<Self> {
let params = params.into();
let (size, noise_distribution) = match params.encryption_key_choice() {
EncryptionKeyChoice::Big => {
let size = params
.glwe_dimension()
.to_equivalent_lwe_dimension(params.polynomial_size());
(size, params.glwe_noise_distribution())
}
EncryptionKeyChoice::Small => (params.lwe_dimension(), params.lwe_noise_distribution()),
};
let mut plaintext_modulus = (params.message_modulus().0 * params.carry_modulus().0) as u64;
// Our plaintext modulus does not take into account the bit of padding
plaintext_modulus *= 2;
crate::shortint::engine::ShortintEngine::with_thread_local_mut(|engine| {
Self::new(
size,
max_num_message,
noise_distribution,
params.ciphertext_modulus(),
plaintext_modulus,
&mut engine.random_generator,
)
})
}
}
/// A Ciphertext tied to a zero-knowledge proof
///
/// The proof can only be generated during the encryption with a [CompactPublicKey]
pub struct ProvenCiphertext {
pub(crate) ciphertext: Ciphertext,
pub(crate) proof: CompactPkeProof,
}
impl ProvenCiphertext {
pub fn ciphertext(&self) -> &Ciphertext {
&self.ciphertext
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
verify_lwe_ciphertext(
&self.ciphertext.ct,
&public_key.key,
&self.proof,
public_params,
)
}
}
/// A List of CompactCiphertext with their zero-knowledge proofs
///
/// The proofs can only be generated during the encryption with a [CompactPublicKey]
#[derive(Clone, Serialize, Deserialize)]
pub struct ProvenCompactCiphertextList {
pub(crate) proved_lists: Vec<(CompactCiphertextList, CompactPkeProof)>,
}
impl ProvenCompactCiphertextList {
pub fn ciphertext_count(&self) -> usize {
self.proved_lists
.iter()
.map(|(list, _)| list.ct_list.lwe_ciphertext_count().0)
.sum()
}
pub fn verify_and_expand(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> crate::Result<Vec<Ciphertext>> {
let not_all_valid = self.proved_lists.par_iter().any(|(ct_list, proof)| {
verify_lwe_compact_ciphertext_list(
&ct_list.ct_list,
&public_key.key,
proof,
public_params,
)
.is_invalid()
});
if not_all_valid {
return Err(crate::ErrorKind::InvalidZkProof.into());
}
let expanded = self
.proved_lists
.iter()
.flat_map(|(ct_list, _proof)| ct_list.expand())
.collect();
Ok(expanded)
}
pub fn verify(
&self,
public_params: &CompactPkePublicParams,
public_key: &CompactPublicKey,
) -> ZkVerificationOutCome {
let all_valid = self.proved_lists.par_iter().all(|(ct_list, proof)| {
verify_lwe_compact_ciphertext_list(
&ct_list.ct_list,
&public_key.key,
proof,
public_params,
)
.is_valid()
});
if all_valid {
ZkVerificationOutCome::Valid
} else {
ZkVerificationOutCome::Invalid
}
}
}
#[cfg(test)]
mod tests {
use crate::shortint::parameters::DynamicDistribution;
use crate::shortint::prelude::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
use crate::shortint::{ClientKey, CompactPublicKey};
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
use rand::random;
#[test]
fn test_zk_ciphertext_encryption_ci_run_filter() {
let mut params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
params.glwe_noise_distribution = DynamicDistribution::new_t_uniform(9);
let crs = CompactPkeCrs::from_shortint_params(params, 4).unwrap();
let cks = ClientKey::new(params);
let pk = CompactPublicKey::new(&cks);
let msg = random::<u64>() % params.message_modulus.0 as u64;
let proven_ct = pk
.encrypt_and_prove(msg, crs.public_params(), ZkComputeLoad::Proof)
.unwrap();
assert!(proven_ct.verify(crs.public_params(), &pk).is_valid());
let decrypted = cks.decrypt(proven_ct.ciphertext());
assert_eq!(msg, decrypted);
}
#[test]
fn test_zk_compact_ciphertext_list_encryption_ci_run_filter() {
let mut params = PARAM_MESSAGE_2_CARRY_2_KS_PBS;
params.glwe_noise_distribution = DynamicDistribution::new_t_uniform(9);
let crs = CompactPkeCrs::from_shortint_params(params, 512).unwrap();
let cks = ClientKey::new(params);
let pk = CompactPublicKey::new(&cks);
let msgs = (0..512)
.map(|_| random::<u64>() % params.message_modulus.0 as u64)
.collect::<Vec<_>>();
let proven_ct = pk
.encrypt_and_prove_slice(&msgs, crs.public_params(), ZkComputeLoad::Proof)
.unwrap();
assert!(proven_ct.verify(crs.public_params(), &pk).is_valid());
let expanded = proven_ct
.verify_and_expand(crs.public_params(), &pk)
.unwrap();
let decrypted = expanded
.iter()
.map(|ciphertext| cks.decrypt(ciphertext))
.collect::<Vec<_>>();
assert_eq!(msgs, decrypted);
}
}

View File

@@ -7,6 +7,8 @@ use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
use crate::core_crypto::commons::generators::{ use crate::core_crypto::commons::generators::{
DeterministicSeeder, EncryptionRandomGenerator, SecretRandomGenerator, DeterministicSeeder, EncryptionRandomGenerator, SecretRandomGenerator,
}; };
#[cfg(feature = "zk-pok-experimental")]
use crate::core_crypto::commons::math::random::RandomGenerator;
use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder}; use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder};
use crate::core_crypto::entities::*; use crate::core_crypto::entities::*;
use crate::core_crypto::prelude::ContainerMut; use crate::core_crypto::prelude::ContainerMut;
@@ -284,6 +286,8 @@ pub struct ShortintEngine {
/// A seeder that can be called to generate 128 bits seeds, useful to create new /// A seeder that can be called to generate 128 bits seeds, useful to create new
/// [`EncryptionRandomGenerator`] to encrypt seeded types. /// [`EncryptionRandomGenerator`] to encrypt seeded types.
pub(crate) seeder: DeterministicSeeder<ActivatedRandomGenerator>, pub(crate) seeder: DeterministicSeeder<ActivatedRandomGenerator>,
#[cfg(feature = "zk-pok-experimental")]
pub(crate) random_generator: RandomGenerator<ActivatedRandomGenerator>,
pub(crate) computation_buffers: ComputationBuffers, pub(crate) computation_buffers: ComputationBuffers,
ciphertext_buffers: Memory, ciphertext_buffers: Memory,
} }
@@ -327,6 +331,8 @@ impl ShortintEngine {
deterministic_seeder.seed(), deterministic_seeder.seed(),
&mut deterministic_seeder, &mut deterministic_seeder,
), ),
#[cfg(feature = "zk-pok-experimental")]
random_generator: RandomGenerator::new(deterministic_seeder.seed()),
seeder: deterministic_seeder, seeder: deterministic_seeder,
computation_buffers: ComputationBuffers::default(), computation_buffers: ComputationBuffers::default(),
ciphertext_buffers: Memory::default(), ciphertext_buffers: Memory::default(),

View File

@@ -1,3 +1,7 @@
#[cfg(feature = "zk-pok-experimental")]
use crate::core_crypto::algorithms::encrypt_and_prove_lwe_ciphertext_with_compact_public_key;
#[cfg(feature = "zk-pok-experimental")]
use crate::core_crypto::entities::Cleartext;
use crate::core_crypto::prelude::{ use crate::core_crypto::prelude::{
allocate_and_generate_new_seeded_lwe_compact_public_key, allocate_and_generate_new_seeded_lwe_compact_public_key,
encrypt_lwe_ciphertext_with_compact_public_key, generate_lwe_compact_public_key, encrypt_lwe_ciphertext_with_compact_public_key, generate_lwe_compact_public_key,
@@ -5,8 +9,12 @@ use crate::core_crypto::prelude::{
LweCompactPublicKeyOwned, Plaintext, PlaintextList, SeededLweCompactPublicKeyOwned, LweCompactPublicKeyOwned, Plaintext, PlaintextList, SeededLweCompactPublicKeyOwned,
}; };
use crate::shortint::ciphertext::{CompactCiphertextList, Degree, NoiseLevel}; use crate::shortint::ciphertext::{CompactCiphertextList, Degree, NoiseLevel};
#[cfg(feature = "zk-pok-experimental")]
use crate::shortint::ciphertext::{ProvenCiphertext, ProvenCompactCiphertextList};
use crate::shortint::engine::ShortintEngine; use crate::shortint::engine::ShortintEngine;
use crate::shortint::{Ciphertext, ClientKey, PBSOrder, ShortintParameterSet}; use crate::shortint::{Ciphertext, ClientKey, PBSOrder, ShortintParameterSet};
#[cfg(feature = "zk-pok-experimental")]
use crate::zk::{CompactPkePublicParams, ZkComputeLoad};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::iter::once; use std::iter::once;
@@ -162,12 +170,8 @@ impl CompactPublicKey {
); );
let encryption_noise_distribution = match self.pbs_order { let encryption_noise_distribution = match self.pbs_order {
crate::shortint::PBSOrder::KeyswitchBootstrap => { PBSOrder::KeyswitchBootstrap => self.parameters.glwe_noise_distribution(),
self.parameters.glwe_noise_distribution() PBSOrder::BootstrapKeyswitch => self.parameters.lwe_noise_distribution(),
}
crate::shortint::PBSOrder::BootstrapKeyswitch => {
self.parameters.lwe_noise_distribution()
}
}; };
ShortintEngine::with_thread_local_mut(|engine| { ShortintEngine::with_thread_local_mut(|engine| {
@@ -193,6 +197,58 @@ impl CompactPublicKey {
) )
} }
#[cfg(feature = "zk-pok-experimental")]
pub fn encrypt_and_prove(
&self,
message: u64,
public_params: &CompactPkePublicParams,
load: ZkComputeLoad,
) -> crate::Result<ProvenCiphertext> {
// This allocates the required ct
let mut encrypted_ct = LweCiphertextOwned::new(
0u64,
self.key.lwe_dimension().to_lwe_size(),
self.parameters.ciphertext_modulus(),
);
let encryption_noise_distribution = match self.pbs_order {
PBSOrder::KeyswitchBootstrap => self.parameters.glwe_noise_distribution(),
PBSOrder::BootstrapKeyswitch => self.parameters.lwe_noise_distribution(),
};
let plaintext_modulus =
(self.parameters.message_modulus().0 * self.parameters.carry_modulus().0) as u64;
let delta = (1u64 << 63) / plaintext_modulus;
let proof = ShortintEngine::with_thread_local_mut(|engine| {
encrypt_and_prove_lwe_ciphertext_with_compact_public_key(
&self.key,
&mut encrypted_ct,
Cleartext(message),
delta,
encryption_noise_distribution,
encryption_noise_distribution,
&mut engine.secret_generator,
&mut engine.encryption_generator,
&mut engine.random_generator,
public_params,
load,
)
})?;
let message_modulus = self.parameters.message_modulus();
let ciphertext = Ciphertext::new(
encrypted_ct,
Degree::new(message_modulus.0 - 1),
NoiseLevel::NOMINAL,
message_modulus,
self.parameters.carry_modulus(),
self.pbs_order,
);
Ok(ProvenCiphertext { ciphertext, proof })
}
pub fn encrypt_slice(&self, messages: &[u64]) -> CompactCiphertextList { pub fn encrypt_slice(&self, messages: &[u64]) -> CompactCiphertextList {
self.encrypt_iter(messages.iter().copied()) self.encrypt_iter(messages.iter().copied())
} }
@@ -211,12 +267,8 @@ impl CompactPublicKey {
); );
let encryption_noise_distribution = match self.pbs_order { let encryption_noise_distribution = match self.pbs_order {
crate::shortint::PBSOrder::KeyswitchBootstrap => { PBSOrder::KeyswitchBootstrap => self.parameters.glwe_noise_distribution(),
self.parameters.glwe_noise_distribution() PBSOrder::BootstrapKeyswitch => self.parameters.lwe_noise_distribution(),
}
crate::shortint::PBSOrder::BootstrapKeyswitch => {
self.parameters.lwe_noise_distribution()
}
}; };
// No parallelism allowed // No parallelism allowed
@@ -264,6 +316,91 @@ impl CompactPublicKey {
} }
} }
#[cfg(feature = "zk-pok-experimental")]
pub fn encrypt_and_prove_slice(
&self,
messages: &[u64],
public_params: &CompactPkePublicParams,
load: ZkComputeLoad,
) -> crate::Result<ProvenCompactCiphertextList> {
let plaintext_modulus =
(self.parameters.message_modulus().0 * self.parameters.carry_modulus().0) as u64;
let delta = (1u64 << 63) / plaintext_modulus;
let max_num_message = public_params.k;
let num_lists = messages.len().div_ceil(max_num_message);
let mut proved_lists = Vec::with_capacity(num_lists);
for message_chunk in messages.chunks(max_num_message) {
let mut ct_list = LweCompactCiphertextListOwned::new(
0u64,
self.key.lwe_dimension().to_lwe_size(),
LweCiphertextCount(message_chunk.len()),
self.parameters.ciphertext_modulus(),
);
let encryption_noise_distribution = match self.pbs_order {
PBSOrder::KeyswitchBootstrap => self.parameters.glwe_noise_distribution(),
PBSOrder::BootstrapKeyswitch => self.parameters.lwe_noise_distribution(),
};
// No parallelism allowed
#[cfg(all(feature = "__wasm_api", not(feature = "parallel-wasm-api")))]
let proof = {
use crate::core_crypto::prelude::encrypt_and_prove_lwe_compact_ciphertext_list_with_compact_public_key;
ShortintEngine::with_thread_local_mut(|engine| {
encrypt_and_prove_lwe_compact_ciphertext_list_with_compact_public_key(
&self.key,
&mut ct_list,
&message_chunk,
delta,
encryption_noise_distribution,
encryption_noise_distribution,
&mut engine.secret_generator,
&mut engine.encryption_generator,
&mut engine.random_generator,
public_params,
load,
)
})
}?;
// Parallelism allowed /
#[cfg(any(not(feature = "__wasm_api"), feature = "parallel-wasm-api"))]
let proof = {
use crate::core_crypto::prelude::par_encrypt_and_prove_lwe_compact_ciphertext_list_with_compact_public_key;
ShortintEngine::with_thread_local_mut(|engine| {
par_encrypt_and_prove_lwe_compact_ciphertext_list_with_compact_public_key(
&self.key,
&mut ct_list,
&message_chunk,
delta,
encryption_noise_distribution,
encryption_noise_distribution,
&mut engine.secret_generator,
&mut engine.encryption_generator,
&mut engine.random_generator,
public_params,
load,
)
})
}?;
let message_modulus = self.parameters.message_modulus();
let ciphertext = CompactCiphertextList {
ct_list,
degree: Degree::new(message_modulus.0 - 1),
message_modulus,
carry_modulus: self.parameters.carry_modulus(),
pbs_order: self.pbs_order,
noise_level: NoiseLevel::NOMINAL,
};
proved_lists.push((ciphertext, proof));
}
Ok(ProvenCompactCiphertextList { proved_lists })
}
pub fn size_elements(&self) -> usize { pub fn size_elements(&self) -> usize {
self.key.size_elements() self.key.size_elements()
} }

View File

@@ -61,6 +61,7 @@ mod test_cpu_doc {
"../docs/guides/trivial_ciphertext.md", "../docs/guides/trivial_ciphertext.md",
guides_trivial_ciphertext guides_trivial_ciphertext
); );
doctest!("../docs/guides/zk-pok.md", guides_zk_pok);
// REFERENCES // REFERENCES

140
tfhe/src/zk.rs Normal file
View File

@@ -0,0 +1,140 @@
use crate::core_crypto::commons::math::random::{BoundedDistribution, Deserialize, Serialize};
use crate::core_crypto::prelude::*;
use rand_core::RngCore;
use std::cmp::Ordering;
use std::collections::Bound;
use std::fmt::Debug;
use tfhe_zk_pok::proofs::pke::crs_gen;
pub use tfhe_zk_pok::proofs::ComputeLoad as ZkComputeLoad;
type Curve = tfhe_zk_pok::curve_api::Bls12_446;
pub type CompactPkeProof = tfhe_zk_pok::proofs::pke::Proof<Curve>;
pub type CompactPkePublicParams = tfhe_zk_pok::proofs::pke::PublicParams<Curve>;
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum ZkVerificationOutCome {
/// The proof ands its entity were valid
Valid,
/// The proof ands its entity were not
Invalid,
}
impl ZkVerificationOutCome {
pub fn is_valid(self) -> bool {
self == Self::Valid
}
pub fn is_invalid(self) -> bool {
self == Self::Invalid
}
}
#[derive(Serialize, Deserialize)]
pub struct CompactPkeCrs {
public_params: CompactPkePublicParams,
}
impl CompactPkeCrs {
pub fn new<Scalar, NoiseDistribution>(
lwe_dim: LweDimension,
max_num_cleartext: usize,
noise_distribution: NoiseDistribution,
ciphertext_modulus: CiphertextModulus<Scalar>,
plaintext_modulus: Scalar,
rng: &mut impl RngCore,
) -> crate::Result<Self>
where
Scalar: UnsignedInteger + CastInto<u64> + Debug,
NoiseDistribution: BoundedDistribution<Scalar::Signed>,
{
// The bound for the crs has to be a power of two,
// it is [-b, b) (non-inclusive for the high bound)
// so we may have to give a bound that is bigger than
// what the distribution generates
let high_bound = match noise_distribution.high_bound() {
Bound::Included(high_b) => {
let high_b = high_b.wrapping_abs().into_unsigned();
if high_b.is_power_of_two() {
high_b * Scalar::TWO
} else {
high_b.next_power_of_two()
}
}
Bound::Excluded(high_b) => {
let high_b = high_b.wrapping_abs().into_unsigned();
if high_b.is_power_of_two() {
high_b
} else {
high_b.next_power_of_two()
}
}
Bound::Unbounded => {
return Err("requires bounded distribution".into());
}
};
let abs_low_bound = match noise_distribution.low_bound() {
Bound::Included(low_b) => {
let low_b = low_b.wrapping_abs().into_unsigned();
if low_b.is_power_of_two() {
low_b * Scalar::TWO
} else {
low_b.next_power_of_two()
}
}
Bound::Excluded(low_b) => {
let low_b = low_b.wrapping_abs().into_unsigned();
if low_b.is_power_of_two() {
low_b
} else {
low_b.next_power_of_two()
}
}
Bound::Unbounded => {
return Err("requires bounded distribution".into());
}
};
let noise_bound = abs_low_bound.max(high_bound);
if Scalar::BITS > 64 && noise_bound >= (Scalar::ONE << 64usize) {
return Err("noise bounds exceeds 64 bits modulus".into());
}
if Scalar::BITS > 64 && plaintext_modulus >= (Scalar::ONE << 64usize) {
return Err("Plaintext modulus exceeds 64 bits modulus".into());
}
let q = if ciphertext_modulus.is_native_modulus() {
match Scalar::BITS.cmp(&64) {
Ordering::Greater => Err(
"Zero Knowledge proof do not support ciphertext modulus > 64 bits".to_string(),
),
Ordering::Equal => Ok(0u64),
Ordering::Less => Ok(1u64 << Scalar::BITS),
}
} else {
let custom_modulus = ciphertext_modulus.get_custom_modulus();
if custom_modulus > (u64::MAX) as u128 {
Err("Zero Knowledge proof do not support ciphertext modulus > 64 bits".to_string())
} else {
Ok(custom_modulus as u64)
}
}?;
let public_params = crs_gen(
lwe_dim.0,
max_num_cleartext,
noise_bound.cast_into(),
q,
plaintext_modulus.cast_into(),
rng,
);
Ok(Self { public_params })
}
pub fn public_params(&self) -> &CompactPkePublicParams {
&self.public_params
}
}

View File

@@ -68,7 +68,12 @@
value="Compressed Compact Public Key Test 256 Bits Big" value="Compressed Compact Public Key Test 256 Bits Big"
disabled disabled
/> />
<input
type="button"
id="compactPublicKeyZeroKnowledge"
value="Compact Public Key Test 64 Bits Big With Zero Knowledge Proof"
disabled
/>
<input type="checkbox" id="testSuccess" disabled /> <input type="checkbox" id="testSuccess" disabled />
<label for="testSuccess"> TestSuccess </label><br /> <label for="testSuccess"> TestSuccess </label><br />
@@ -110,6 +115,12 @@
value="Compressed Server Key Bench 2_2" value="Compressed Server Key Bench 2_2"
disabled disabled
/> />
<input
type="button"
id="compactPublicKeyZeroKnowledgeBench"
value="Compact ZK Bench"
disabled
/>
<input type="text" id="benchmarkResults" disabled /> <input type="text" id="benchmarkResults" disabled />
<label for="benchmarkResults"> BenchmarkResults </label><br /> <label for="benchmarkResults"> BenchmarkResults </label><br />

View File

@@ -31,12 +31,14 @@ async function setup() {
"compactPublicKeyTest256BitSmall", "compactPublicKeyTest256BitSmall",
"compressedCompactPublicKeyTest256BitBig", "compressedCompactPublicKeyTest256BitBig",
"compressedCompactPublicKeyTest256BitSmall", "compressedCompactPublicKeyTest256BitSmall",
"compactPublicKeyZeroKnowledge",
"compactPublicKeyBench32BitBig", "compactPublicKeyBench32BitBig",
"compactPublicKeyBench32BitSmall", "compactPublicKeyBench32BitSmall",
"compactPublicKeyBench256BitBig", "compactPublicKeyBench256BitBig",
"compactPublicKeyBench256BitSmall", "compactPublicKeyBench256BitSmall",
"compressedServerKeyBenchMessage1Carry1", "compressedServerKeyBenchMessage1Carry1",
"compressedServerKeyBenchMessage2Carry2", "compressedServerKeyBenchMessage2Carry2",
"compactPublicKeyZeroKnowledgeBench",
]; ];
function setupBtn(id) { function setupBtn(id) {

View File

@@ -1,4 +1,4 @@
const secs = 60; const secs = 1200; // 20 Minutes
const config = { const config = {
verbose: true, verbose: true,

View File

@@ -50,22 +50,26 @@ async function runActualTest(page, buttonId) {
} }
} }
const TWENTY_MINUTES = 1200 * 1000;
async function runTestAttachedToButton(buttonId) { async function runTestAttachedToButton(buttonId) {
let browser; let browser;
if (isRoot()) { if (isRoot()) {
browser = await puppeteer.launch({ browser = await puppeteer.launch({
headless: "new", headless: "new",
args: ["--no-sandbox"], args: ["--no-sandbox"],
protocolTimeout: TWENTY_MINUTES,
}); });
} else { } else {
browser = await puppeteer.launch({ browser = await puppeteer.launch({
headless: "new", headless: "new",
protocolTimeout: TWENTY_MINUTES,
}); });
} }
let page = await browser.newPage(); let page = await browser.newPage();
await page.setDefaultTimeout(300000); // Five minutes timeout await page.setDefaultTimeout(TWENTY_MINUTES);
await page.goto("http://localhost:3000"); await page.goto("http://localhost:3000");
page.on("console", (msg) => console.log("PAGE LOG:", msg.text())); page.on("console", (msg) => console.log("PAGE LOG:", msg.text()));

View File

@@ -23,3 +23,11 @@ it("Compressed Compact Public Key Test Small 256 Bit", async () => {
it("Compressed Compact Public Key Test Big 256 Bit", async () => { it("Compressed Compact Public Key Test Big 256 Bit", async () => {
await runTestAttachedToButton("compressedCompactPublicKeyTest256BitBig"); await runTestAttachedToButton("compressedCompactPublicKeyTest256BitBig");
}); });
it(
"Compact Public Key Test Big 64 Bit With Zero Knowledge",
async () => {
await runTestAttachedToButton("compactPublicKeyZeroKnowledge");
},
1200 * 1000,
); // 20 minutes timeout

View File

@@ -11,17 +11,15 @@ import init, {
TfheCompressedCompactPublicKey, TfheCompressedCompactPublicKey,
TfheCompactPublicKey, TfheCompactPublicKey,
TfheConfigBuilder, TfheConfigBuilder,
CompressedFheUint8,
FheUint8, FheUint8,
FheUint32,
CompactFheUint32,
CompactFheUint32List, CompactFheUint32List,
CompressedFheUint128,
FheUint128,
CompressedFheUint256,
FheUint256,
CompactFheUint256,
CompactFheUint256List, CompactFheUint256List,
ZkComputeLoad,
ProvenCompactFheUint64,
ProvenCompactFheUint64List,
CompactPkeCrs,
Shortint,
CompactFheUint64,
} from "./pkg/tfhe.js"; } from "./pkg/tfhe.js";
function assert(cond, text) { function assert(cond, text) {
@@ -74,7 +72,7 @@ async function compressedPublicKeyTest() {
} }
async function publicKeyTest() { async function publicKeyTest() {
let config = TfheConfigBuilder.default().use_small_encryption().build(); let config = TfheConfigBuilder.default_with_small_encryption().build();
console.time("ClientKey Gen"); console.time("ClientKey Gen");
let clientKey = TfheClientKey.generate(config); let clientKey = TfheClientKey.generate(config);
@@ -379,6 +377,106 @@ async function compressedCompactPublicKeyTest256BitOnConfig(config) {
} }
} }
function generateRandomBigInt(bitLen) {
let result = BigInt(0);
for (let i = 0; i < bitLen; i++) {
result << 1n;
result |= BigInt(Math.random() < 0.5);
}
return result;
}
async function compactPublicKeyZeroKnowledge() {
let block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_KS_PBS,
);
block_params.set_glwe_noise_distribution(Shortint.try_new_t_uniform(9));
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
console.log("Start CRS generation");
console.time("CRS generation");
let crs = CompactPkeCrs.from_config(config, 4 * 64);
console.timeEnd("CRS generation");
let public_params = crs.public_params();
{
let input = generateRandomBigInt(64);
let start = performance.now();
let encrypted = ProvenCompactFheUint64.encrypt_with_compact_public_key(
input,
public_params,
publicKey,
ZkComputeLoad.Proof,
);
let end = performance.now();
console.log(
"Time to encrypt + prove CompactFheUint64: ",
end - start,
" ms",
);
let bytes = encrypted.serialize();
console.log("ProvenCompactFheUint64 size:", bytes.length);
assert_eq(encrypted.verifies(public_params, publicKey), true);
start = performance.now();
let expanded = encrypted.verify_and_expand(public_params, publicKey);
end = performance.now();
console.log(
"Time to verify + expand CompactFheUint64: ",
end - start,
" ms",
);
let decrypted = expanded.decrypt(clientKey);
assert_eq(decrypted, input);
}
{
let inputs = [
generateRandomBigInt(64),
generateRandomBigInt(64),
generateRandomBigInt(64),
generateRandomBigInt(64),
];
let start = performance.now();
let encrypted = ProvenCompactFheUint64List.encrypt_with_compact_public_key(
inputs,
public_params,
publicKey,
ZkComputeLoad.Proof,
);
let end = performance.now();
console.log(
"Time to encrypt + prove CompactFheUint64List of 4: ",
end - start,
" ms",
);
assert_eq(encrypted.verifies(public_params, publicKey), true);
start = performance.now();
let expanded_list = encrypted.verify_and_expand(public_params, publicKey);
end = performance.now();
console.log(
"Time to verify + expand CompactFheUint64: ",
end - start,
" ms",
);
for (let i = 0; i < inputs.length; i++) {
let decrypted = expanded_list[i].decrypt(clientKey);
assert_eq(decrypted, inputs[i]);
}
}
}
async function compressedCompactPublicKeyTest256BitBig() { async function compressedCompactPublicKeyTest256BitBig() {
const block_params = new ShortintParameters( const block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS, ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS,
@@ -550,6 +648,60 @@ async function compressedServerKeyBenchMessage2Carry2() {
); );
} }
async function compactPublicKeyZeroKnowledgeBench() {
let block_params = new ShortintParameters(
ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS,
);
block_params.set_lwe_noise_distribution(Shortint.try_new_t_uniform(9));
let config = TfheConfigBuilder.default()
.use_custom_parameters(block_params)
.build();
let clientKey = TfheClientKey.generate(config);
let publicKey = TfheCompactPublicKey.new(clientKey);
console.log("Start CRS generation");
console.time("CRS generation");
let crs = CompactPkeCrs.from_config(config, 4 * 64);
console.timeEnd("CRS generation");
let public_params = crs.public_params();
const bench_loops = 4; // The computation is expensive
let bench_results = {};
let load_choices = [ZkComputeLoad.Proof, ZkComputeLoad.Verify];
const load_to_str = {
[ZkComputeLoad.Proof]: "compute_load_proof",
[ZkComputeLoad.Verify]: "compute_load_verify",
};
for (const loadChoice of load_choices) {
let timing = 0;
for (let i = 0; i < bench_loops; i++) {
let input = generateRandomBigInt(64);
const start = performance.now();
let _ = ProvenCompactFheUint64.encrypt_with_compact_public_key(
input,
public_params,
publicKey,
loadChoice,
);
const end = performance.now();
timing += end - start;
}
const mean = timing / bench_loops;
const bench_str =
"compact_fhe_uint64_proven_encryption_" +
load_to_str[loadChoice] +
"_mean";
console.log(bench_str, ": ", mean, " ms");
bench_results["compact_fhe_uint64_proven_encryption_"] = mean;
}
return bench_results;
}
async function main() { async function main() {
await init(); await init();
await initThreadPool(navigator.hardwareConcurrency); await initThreadPool(navigator.hardwareConcurrency);
@@ -564,12 +716,14 @@ async function main() {
compactPublicKeyTest256BitBig, compactPublicKeyTest256BitBig,
compressedCompactPublicKeyTest256BitSmall, compressedCompactPublicKeyTest256BitSmall,
compressedCompactPublicKeyTest256BitBig, compressedCompactPublicKeyTest256BitBig,
compactPublicKeyZeroKnowledge,
compactPublicKeyBench32BitBig, compactPublicKeyBench32BitBig,
compactPublicKeyBench32BitSmall, compactPublicKeyBench32BitSmall,
compactPublicKeyBench256BitBig, compactPublicKeyBench256BitBig,
compactPublicKeyBench256BitSmall, compactPublicKeyBench256BitSmall,
compressedServerKeyBenchMessage1Carry1, compressedServerKeyBenchMessage1Carry1,
compressedServerKeyBenchMessage2Carry2, compressedServerKeyBenchMessage2Carry2,
compactPublicKeyZeroKnowledgeBench,
}); });
} }