From fc7845024543ed8927b268d58aa14213e3a96735 Mon Sep 17 00:00:00 2001 From: Nicolas Sarlin Date: Mon, 17 Nov 2025 14:29:10 +0100 Subject: [PATCH] chore: test vector generation tool for core algorithms --- Cargo.toml | 1 + Makefile | 25 +- apps/test-vectors/Cargo.toml | 11 + apps/test-vectors/README.md | 11 + apps/test-vectors/checksums.sha256 | 28 ++ apps/test-vectors/data/README.md | 102 ++++++ apps/test-vectors/data/toy_params/bsk.cbor | 3 + .../data/toy_params/glwe_after_id_br.cbor | 3 + .../data/toy_params/glwe_after_spec_br.cbor | 3 + apps/test-vectors/data/toy_params/ksk.cbor | 3 + .../data/toy_params/large_lwe_secret_key.cbor | 3 + apps/test-vectors/data/toy_params/lwe_a.cbor | 3 + .../data/toy_params/lwe_after_id_pbs.cbor | 3 + .../data/toy_params/lwe_after_spec_pbs.cbor | 3 + apps/test-vectors/data/toy_params/lwe_b.cbor | 3 + apps/test-vectors/data/toy_params/lwe_ks.cbor | 3 + apps/test-vectors/data/toy_params/lwe_ms.cbor | 3 + .../data/toy_params/lwe_prod.cbor | 3 + .../test-vectors/data/toy_params/lwe_sum.cbor | 3 + .../data/toy_params/small_lwe_secret_key.cbor | 3 + .../data/valid_params_128/bsk.cbor | 3 + .../valid_params_128/glwe_after_id_br.cbor | 3 + .../valid_params_128/glwe_after_spec_br.cbor | 3 + .../data/valid_params_128/ksk.cbor | 3 + .../large_lwe_secret_key.cbor | 3 + .../data/valid_params_128/lwe_a.cbor | 3 + .../valid_params_128/lwe_after_id_pbs.cbor | 3 + .../valid_params_128/lwe_after_spec_pbs.cbor | 3 + .../data/valid_params_128/lwe_b.cbor | 3 + .../data/valid_params_128/lwe_ks.cbor | 3 + .../data/valid_params_128/lwe_ms.cbor | 3 + .../data/valid_params_128/lwe_prod.cbor | 3 + .../data/valid_params_128/lwe_sum.cbor | 3 + .../small_lwe_secret_key.cbor | 3 + apps/test-vectors/src/main.rs | 344 ++++++++++++++++++ scripts/pull_lfs_data.sh | 10 +- scripts/test_vectors.sh | 59 +++ tasks/src/check_tfhe_docs_are_tested.rs | 2 +- 38 files changed, 670 insertions(+), 7 deletions(-) create mode 100644 apps/test-vectors/Cargo.toml create mode 100644 apps/test-vectors/README.md create mode 100644 apps/test-vectors/checksums.sha256 create mode 100644 apps/test-vectors/data/README.md create mode 100644 apps/test-vectors/data/toy_params/bsk.cbor create mode 100644 apps/test-vectors/data/toy_params/glwe_after_id_br.cbor create mode 100644 apps/test-vectors/data/toy_params/glwe_after_spec_br.cbor create mode 100644 apps/test-vectors/data/toy_params/ksk.cbor create mode 100644 apps/test-vectors/data/toy_params/large_lwe_secret_key.cbor create mode 100644 apps/test-vectors/data/toy_params/lwe_a.cbor create mode 100644 apps/test-vectors/data/toy_params/lwe_after_id_pbs.cbor create mode 100644 apps/test-vectors/data/toy_params/lwe_after_spec_pbs.cbor create mode 100644 apps/test-vectors/data/toy_params/lwe_b.cbor create mode 100644 apps/test-vectors/data/toy_params/lwe_ks.cbor create mode 100644 apps/test-vectors/data/toy_params/lwe_ms.cbor create mode 100644 apps/test-vectors/data/toy_params/lwe_prod.cbor create mode 100644 apps/test-vectors/data/toy_params/lwe_sum.cbor create mode 100644 apps/test-vectors/data/toy_params/small_lwe_secret_key.cbor create mode 100644 apps/test-vectors/data/valid_params_128/bsk.cbor create mode 100644 apps/test-vectors/data/valid_params_128/glwe_after_id_br.cbor create mode 100644 apps/test-vectors/data/valid_params_128/glwe_after_spec_br.cbor create mode 100644 apps/test-vectors/data/valid_params_128/ksk.cbor create mode 100644 apps/test-vectors/data/valid_params_128/large_lwe_secret_key.cbor create mode 100644 apps/test-vectors/data/valid_params_128/lwe_a.cbor create mode 100644 apps/test-vectors/data/valid_params_128/lwe_after_id_pbs.cbor create mode 100644 apps/test-vectors/data/valid_params_128/lwe_after_spec_pbs.cbor create mode 100644 apps/test-vectors/data/valid_params_128/lwe_b.cbor create mode 100644 apps/test-vectors/data/valid_params_128/lwe_ks.cbor create mode 100644 apps/test-vectors/data/valid_params_128/lwe_ms.cbor create mode 100644 apps/test-vectors/data/valid_params_128/lwe_prod.cbor create mode 100644 apps/test-vectors/data/valid_params_128/lwe_sum.cbor create mode 100644 apps/test-vectors/data/valid_params_128/small_lwe_secret_key.cbor create mode 100644 apps/test-vectors/src/main.rs create mode 100755 scripts/test_vectors.sh diff --git a/Cargo.toml b/Cargo.toml index 35d48aeff..03d009849 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "utils/param_dedup", "tests", "mockups/tfhe-hpu-mockup", + "apps/test-vectors", ] exclude = [ diff --git a/Makefile b/Makefile index caf61e1ad..237c78f51 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ BENCH_CUSTOM_COMMAND:= NODE_VERSION=22.6 BACKWARD_COMPAT_DATA_DIR=utils/tfhe-backward-compat-data BACKWARD_COMPAT_DATA_GEN_VERSION:=$(TFHE_VERSION) +TEST_VECTORS_DIR=apps/test-vectors CURRENT_TFHE_VERSION:=$(shell grep '^version[[:space:]]*=' tfhe/Cargo.toml | cut -d '=' -f 2 | xargs) WASM_PACK_VERSION="0.13.1" WASM_BINDGEN_VERSION:=$(shell cargo tree --target wasm32-unknown-unknown -e all --prefix none | grep "wasm-bindgen v" | head -n 1 | cut -d 'v' -f2) @@ -525,11 +526,16 @@ clippy_backward_compat_data: install_rs_check_toolchain # the toolchain is selec echo "Cannot run clippy for backward compat crate on non x86 platform for now."; \ fi +.PHONY: clippy_test_vectors # Run clippy lints on the test vectors app +clippy_test_vectors: install_rs_check_toolchain + cd apps/test-vectors; RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \ + -p tfhe-test-vectors -- --no-deps -D warnings + .PHONY: clippy_all # Run all clippy targets clippy_all: clippy_rustdoc clippy clippy_boolean clippy_shortint clippy_integer clippy_all_targets \ clippy_c_api clippy_js_wasm_api clippy_tasks clippy_core clippy_tfhe_csprng clippy_zk_pok clippy_trivium \ clippy_versionable clippy_tfhe_lints clippy_ws_tests clippy_bench clippy_param_dedup \ -clippy_backward_compat_data +clippy_test_vectors clippy_backward_compat_data .PHONY: clippy_fast # Run main clippy targets clippy_fast: clippy_rustdoc clippy clippy_all_targets clippy_c_api clippy_js_wasm_api clippy_tasks \ @@ -1184,6 +1190,17 @@ test_backward_compatibility_ci: install_rs_build_toolchain .PHONY: test_backward_compatibility # Same as test_backward_compatibility_ci but tries to clone the data repo first if needed test_backward_compatibility: pull_backward_compat_data test_backward_compatibility_ci +# Generate the test vectors and update the hash file +.PHONY: gen_test_vectors +gen_test_vectors: + ./scripts/test_vectors.sh generate apps/test-vectors + +# Generate the test vectors and check that the content matches the hash file +# `comm` is used to compare the checksums, and will also notify of any added file +.PHONY: check_test_vectors +check_test_vectors: + ./scripts/test_vectors.sh check apps/test-vectors + .PHONY: doc # Build rust doc doc: install_rs_check_toolchain @# Even though we are not in docs.rs, this allows to "just" build the doc @@ -1723,6 +1740,10 @@ pull_backward_compat_data: pull_hpu_files: ./scripts/pull_lfs_data.sh backends/tfhe-hpu-backend/ +.PHONY: pull_test_vectors # Pull the data files needed for backward compatibility tests +pull_test_vectors: + ./scripts/pull_lfs_data.sh $(TEST_VECTORS_DIR) + # # Real use case examples # @@ -1774,6 +1795,8 @@ pcc_batch_2: $(call run_recipe_with_details,clippy) $(call run_recipe_with_details,clippy_all_targets) $(call run_recipe_with_details,check_fmt_js) + $(call run_recipe_with_details,clippy_test_vectors) + $(call run_recipe_with_details,check_test_vectors) .PHONY: pcc_batch_3 # duration: 6'50'' pcc_batch_3: diff --git a/apps/test-vectors/Cargo.toml b/apps/test-vectors/Cargo.toml new file mode 100644 index 000000000..9424f089c --- /dev/null +++ b/apps/test-vectors/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "tfhe-test-vectors" +version = "0.1.0" +edition = "2024" +rust-version.workspace = true + +[dependencies] +ciborium = "0.2" +serde = { version = "1.0", features = ["derive"] } +tfhe = { path = "../../tfhe", features = ["experimental-force_fft_algo_dif4"] } +tfhe-csprng = { path = "../../tfhe-csprng" } diff --git a/apps/test-vectors/README.md b/apps/test-vectors/README.md new file mode 100644 index 000000000..9afa98a40 --- /dev/null +++ b/apps/test-vectors/README.md @@ -0,0 +1,11 @@ +# Test vectors for TFHE +This folder contains test vectors for the core TFHE-rs algorithms. + +The test vectors are located in `data`, and are generated using the sample program in `src/main.rs`. + +To re-generate the test vectors, assuming you have [rustup](https://rust-lang.org/tools/install/) installed on your system, simply run the following command in the current folder: +``` +cargo run --release +``` + +See [the data folder](data/README.md) for more information about the generated values. diff --git a/apps/test-vectors/checksums.sha256 b/apps/test-vectors/checksums.sha256 new file mode 100644 index 000000000..9ee7f79fb --- /dev/null +++ b/apps/test-vectors/checksums.sha256 @@ -0,0 +1,28 @@ +2abec9dc5d399ece68ac227f67f7cafcb9a6acac36ed734fe3d5244021eb1cda data/toy_params/glwe_after_spec_br.cbor +2c70d1d78cc3760733850a353ace2b9c4705e840141b75841739e90e51247e18 data/valid_params_128/small_lwe_secret_key.cbor +36c9080b636475fcacca503ce041bbfeee800fd3e1890dee559ea18defff9fe8 data/toy_params/glwe_after_id_br.cbor +377761beeb4216cf5aa2624a8b64b8259f5a75c32d28e850be8bced3a0cdd6f5 data/toy_params/ksk.cbor +59dba26d457f96478eda130cab5301fce86f23c6a8807de42f2a1e78c4985ca7 data/valid_params_128/lwe_ks.cbor +656f0009c7834c5bcb61621e222047516054b9bc5d0593d474ab8f1c086b67a6 data/valid_params_128/lwe_after_id_pbs.cbor +699580ca92b9c2f9e1f57fb1e312c9e8cb29714f7acdef9d2ba05f798546751f data/toy_params/lwe_sum.cbor +6e54ab41056984595b077baff70236d934308cf5c0c33b4482fbfb129b3756c6 data/valid_params_128/glwe_after_id_br.cbor +70f5e5728822de05b49071efb5ec28551b0f5cc87aa709a455d8e7f04b9c96ee data/toy_params/lwe_after_id_pbs.cbor +7cc6803f5fbc3d5a1bf597f2b979ce17eecd3d6baca12183dea21022a7b65c52 data/toy_params/bsk.cbor +7f3c40a134623b44779a556212477fea26eaed22450f3b6faeb8721d63699972 data/valid_params_128/lwe_sum.cbor +837b3bd3245d4d0534ed255fdef896fb4fa6998a258a14543dfdadd0bfc9b6dd data/toy_params/lwe_prod.cbor +8ee68ed99dd9103fb62b1e2c7c8cf483706ae2071b792d4bd16f9f93f64871f9 data/toy_params/lwe_after_spec_pbs.cbor +99a19c5d6d5f4fd81d9164d0ff96719ef362eabda256bce6a55cba6cb69e42bf data/valid_params_128/glwe_after_spec_br.cbor +aa44aea29efd6d9e4d35a21a625d9cba155672e3f7ed3eddee1e211e62ad146b data/valid_params_128/lwe_ms.cbor +b7a037b9eaa88d6385167579b93e26a0cb6976d9b8967416fd1173e113bda199 data/valid_params_128/large_lwe_secret_key.cbor +c6df98676de04fe54b5ffc2eb30a82ebb706c9d7d5a4e0ed509700fec88761f7 data/toy_params/lwe_ms.cbor +c7d5a864d5616a7d8ad50bbf40416e41e6c9b60c546dc14d4aa8fc40a418baa7 data/toy_params/large_lwe_secret_key.cbor +c806533b325b1009db38be2f9bef5f3b2fad6b77b4c71f2855ccc9d3b4162e98 data/valid_params_128/lwe_b.cbor +c9eb75bd2993639348a679cf48c06e3c38d1a513f48e5b0ce0047cea8cff6bbc data/toy_params/lwe_a.cbor +d6da5baef0e787f6be56e218d8354e26904652602db964844156fdff08350ce6 data/toy_params/lwe_ks.cbor +e44ffa6e5a50a03d32721180a051c8ce62f1791d4853aeaebed0200c183a57cf data/valid_params_128/lwe_after_spec_pbs.cbor +e591ab9af1b6a0aede273f9a3abb65a4c387feb5fa06a6959e9314058ca0f7e5 data/valid_params_128/ksk.cbor +e69d2d2c064fc8c0460b39191ca65338146990349954f5ec5ebd01d93610e7eb data/valid_params_128/lwe_a.cbor +e76c24b2a0c9a842ad13dda35473c2514f9e7d20983b5ea0759c4521a91626d9 data/valid_params_128/lwe_prod.cbor +e9afe7019acba5cda926f13e06df9930571611729d2f2e8ce41956e1f5e1db6f data/valid_params_128/bsk.cbor +eadf2eff35133ffba075df11faecddd6e7af9ddc398011ec4568e5528812b3e2 data/toy_params/lwe_b.cbor +ee9fcf45f1379ca3a7d7bf2b0e7a1cc920ceb496c0217e8604b0b58d2831749e data/toy_params/small_lwe_secret_key.cbor diff --git a/apps/test-vectors/data/README.md b/apps/test-vectors/data/README.md new file mode 100644 index 000000000..ae0cd8613 --- /dev/null +++ b/apps/test-vectors/data/README.md @@ -0,0 +1,102 @@ +# Test vectors for TFHE +These test vectors are generated using [TFHE-rs](https://github.com/zama-ai/tfhe-rs), with the git tag `tfhe-test-vectors-0.1.0`. + +They are TFHE-rs objects serialized in the [cbor format](https://cbor.io/). You can deserialize them using any cbor library for the language of your choice. For example, using the [cbor2](https://pypi.org/project/cbor2/) program, run: `cbor2 --pretty toy_params/lwe_a.cbor`. + +You will find 2 folders with test vectors for different parameter sets: +- `valid_params_128`: valid classical PBS parameters using a gaussian noise distribution, providing 128bits of security in the IND-CPA model and a bootstrapping probability of failure of 2^{-64}. +- `toy_params`: insecure parameters that yield smaller values + +The values are generated for the keyswitch -> bootstrap (KS-PBS) atomic pattern. The cleartext inputs are 2 values, A and B defined below. + +All the random values are generated from a fixed seed, that can be found in the `RAND_SEED` constant below. The PRNG used is the one based on the AES block cipher in counter mode, from tfhe `tfhe-csprng` crate. + +The programmable bootstrap is applied twice, with 2 different lut, the identity lut and a specific one (currently a x2 operation) + +## Vectors +The following values are generated: + +### Keys +| name | description | TFHE-rs type | +|------------------------|---------------------------------------------------------------------------------------|-----------------------------| +| `large_lwe_secret_key` | Encryption secret key, before the KS and after the PBS | `LweSecretKey>` | +| `small_lwe_secret_key` | Secret key encrypting ciphertexts between the KS and the PBS | `LweSecretKey>` | +| `ksk` | The keyswitching key to convert a ct from the large key to the small one | `LweKeyswitchKey>` | +| `bsk` | the bootstrapping key to perform a programmable bootstrap on the keyswitched ciphertext | `LweBootstrapKey>` | + + +### Ciphertexts +| name | description | TFHE-rs type | Cleartext | +|----------------------|--------------------------------------------------------------------------------------------------------------|----------------------------|--------------| +| `lwe_a` | Lwe encryption of A | `LweCiphertext>` | `A` | +| `lwe_b` | Lwe encryption of B | `LweCiphertext>` | `B` | +| `lwe_sum` | Lwe encryption of A plus lwe encryption of B | `LweCiphertext>` | `A+B` | +| `lwe_prod` | Lwe encryption of A times cleartext B | `LweCiphertext>` | `A*B` | +| `lwe_ms` | The lwe ciphertext after the modswitch part of the PBS ([note](#non-native-encoding)) | `LweCiphertext>` | `A` | +| `lwe_ks` | The lwe ciphertext after the keyswitch | `LweCiphertext>` | `A` | +| `glwe_after_id_br` | The glwe returned by the application of the identity blind rotation on the mod switched ciphertexts. | `GlweCiphertext>` | rot id LUT | +| `lwe_after_id_pbs` | The lwe returned by the application of the sample extract operation on the output of the id blind rotation | `LweCiphertext>` | `A` | +| `glwe_after_spec_br` | The glwe returned by the application of the spec blind rotation on the mod switched ciphertexts. | `GlweCiphertext>` | rot spec LUT | +| `lwe_after_spec_pbs` | The lwe returned by the application of the sample extract operation on the output of the spec blind rotation | `LweCiphertext>` | `spec(A)` | + +### Encodings +#### Non native encoding +Warning: TFHE-rs uses a specific encoding for non native (ie: u32, u64) power of two ciphertext modulus. This encoding puts the encoded value in the high bits of the native integer. +For example, the value 37 with a modulus of 64 will be encoded as `0b1001010000000000000000000000000000000000000000000000000000000000`. This matters for the post modswitch lwe ciphertext. + +#### Ciphertext modulus +The ciphertext modulus encoding use a specific value for the native modulus: 0. For example, if values are stored on u64 integers, 0 means a ciphertext modulus of 2^64. + +## Operations + +| name | inputs | outputs | +|-------------------------|-------------------------------------------------------------------|------------------------| +| large secret key gen | PARAMS, RAND_SEED | `large_lwe_secret_key` | +| small secret key gen | PARAMS, RAND_SEED | `small_lwe_secret_key` | +| keyswitch key gen | PARAMS, RAND_SEED, `large_lwe_secret_key`, `small_lwe_secret_key` | `ksk` | +| bootstrap key gen | PARAMS, RAND_SEED, `small_lwe_secret_key`, `large_lwe_secret_key` | `bsk` | +| encryption A | A, `large_lwe_secret_key` | `lwe_a` | +| encryption B | B, `large_lwe_secret_key` | `lwe_b` | +| `E(A)+E(B)` | `lwe_a`, `lwe_b` | `lwe_sum` | +| `E(A)*B` | `lwe_a`, B | `lwe_prod` | +| keyswitch | `lwe_a`, `ksk` | `lwe_ks` | +| modulus switch | `lwe_ks` | `lwe_ms` | +| blind rotation id lut | ID_LUT, `lwe_ms`, `bsk` | `glwe_after_id_br` | +| sample extract id lut | `glwe_after_id_br` | `lwe_after_id_pbs` | +| blind rotation spec lut | SPEC_LUT, `lwe_ms`, `bsk` | `glwe_after_spec_br` | +| sample extract spec lut | `glwe_after_spec_br` | `lwe_after_spec_pbs` | + +## Parameters + +```rust +const RAND_SEED: u128 = 0x74666865; + +const MSG_A: u64 = 4; +const MSG_B: u64 = 3; + +const VALID_LWE_DIMENSION: LweDimension = LweDimension(833); +const VALID_GLWE_DIMENSION: GlweDimension = GlweDimension(1); +const VALID_POLYNOMIAL_SIZE: PolynomialSize = PolynomialSize(2048); +const VALID_GAUSSIAN_LWE_NOISE_STDDEV: f64 = 3.6158408373309336e-06; +const VALID_GAUSSIAN_GLWE_NOISE_STDDEV: f64 = 2.845267479601915e-15; +const VALID_PBS_DECOMPOSITION_BASE_LOG: DecompositionBaseLog = DecompositionBaseLog(23); +const VALID_PBS_DECOMPOSITION_LEVEL_COUNT: DecompositionLevelCount = DecompositionLevelCount(1); +const VALID_KS_DECOMPOSITION_BASE_LOG: DecompositionBaseLog = DecompositionBaseLog(3); +const VALID_KS_DECOMPOSITION_LEVEL_COUNT: DecompositionLevelCount = DecompositionLevelCount(5); + +const TOY_LWE_DIMENSION: LweDimension = LweDimension(10); +const TOY_GLWE_DIMENSION: GlweDimension = GlweDimension(1); +const TOY_POLYNOMIAL_SIZE: PolynomialSize = PolynomialSize(256); +const TOY_GAUSSIAN_LWE_NOISE_STDDEV: f64 = 0.; +const TOY_GAUSSIAN_GLWE_NOISE_STDDEV: f64 = 0.; +const TOY_PBS_DECOMPOSITION_BASE_LOG: DecompositionBaseLog = DecompositionBaseLog(24); +const TOY_PBS_DECOMPOSITION_LEVEL_COUNT: DecompositionLevelCount = DecompositionLevelCount(1); +const TOY_KS_DECOMPOSITION_BASE_LOG: DecompositionBaseLog = DecompositionBaseLog(37); +const TOY_KS_DECOMPOSITION_LEVEL_COUNT: DecompositionLevelCount = DecompositionLevelCount(1); + +const CIPHERTEXT_MODULUS: CiphertextModulus = CiphertextModulus::new_native(); +const MSG_BITS: usize = 4; + +const SPEC_LUT: fn(u64) -> u64 = |x| (x * 2) % (1u64 << MSG_BITS); +const ID_LUT: fn(u64) -> u64 = |x| x; +``` diff --git a/apps/test-vectors/data/toy_params/bsk.cbor b/apps/test-vectors/data/toy_params/bsk.cbor new file mode 100644 index 000000000..ed64555b1 --- /dev/null +++ b/apps/test-vectors/data/toy_params/bsk.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7cc6803f5fbc3d5a1bf597f2b979ce17eecd3d6baca12183dea21022a7b65c52 +size 92291 diff --git a/apps/test-vectors/data/toy_params/glwe_after_id_br.cbor b/apps/test-vectors/data/toy_params/glwe_after_id_br.cbor new file mode 100644 index 000000000..63faa8426 --- /dev/null +++ b/apps/test-vectors/data/toy_params/glwe_after_id_br.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36c9080b636475fcacca503ce041bbfeee800fd3e1890dee559ea18defff9fe8 +size 4679 diff --git a/apps/test-vectors/data/toy_params/glwe_after_spec_br.cbor b/apps/test-vectors/data/toy_params/glwe_after_spec_br.cbor new file mode 100644 index 000000000..529316301 --- /dev/null +++ b/apps/test-vectors/data/toy_params/glwe_after_spec_br.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2abec9dc5d399ece68ac227f67f7cafcb9a6acac36ed734fe3d5244021eb1cda +size 4679 diff --git a/apps/test-vectors/data/toy_params/ksk.cbor b/apps/test-vectors/data/toy_params/ksk.cbor new file mode 100644 index 000000000..26c85c2ea --- /dev/null +++ b/apps/test-vectors/data/toy_params/ksk.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:377761beeb4216cf5aa2624a8b64b8259f5a75c32d28e850be8bced3a0cdd6f5 +size 25451 diff --git a/apps/test-vectors/data/toy_params/large_lwe_secret_key.cbor b/apps/test-vectors/data/toy_params/large_lwe_secret_key.cbor new file mode 100644 index 000000000..c3c4f812b --- /dev/null +++ b/apps/test-vectors/data/toy_params/large_lwe_secret_key.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7d5a864d5616a7d8ad50bbf40416e41e6c9b60c546dc14d4aa8fc40a418baa7 +size 265 diff --git a/apps/test-vectors/data/toy_params/lwe_a.cbor b/apps/test-vectors/data/toy_params/lwe_a.cbor new file mode 100644 index 000000000..44591886a --- /dev/null +++ b/apps/test-vectors/data/toy_params/lwe_a.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9eb75bd2993639348a679cf48c06e3c38d1a513f48e5b0ce0047cea8cff6bbc +size 2365 diff --git a/apps/test-vectors/data/toy_params/lwe_after_id_pbs.cbor b/apps/test-vectors/data/toy_params/lwe_after_id_pbs.cbor new file mode 100644 index 000000000..2704016cd --- /dev/null +++ b/apps/test-vectors/data/toy_params/lwe_after_id_pbs.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70f5e5728822de05b49071efb5ec28551b0f5cc87aa709a455d8e7f04b9c96ee +size 2365 diff --git a/apps/test-vectors/data/toy_params/lwe_after_spec_pbs.cbor b/apps/test-vectors/data/toy_params/lwe_after_spec_pbs.cbor new file mode 100644 index 000000000..e2649c1d3 --- /dev/null +++ b/apps/test-vectors/data/toy_params/lwe_after_spec_pbs.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ee68ed99dd9103fb62b1e2c7c8cf483706ae2071b792d4bd16f9f93f64871f9 +size 2365 diff --git a/apps/test-vectors/data/toy_params/lwe_b.cbor b/apps/test-vectors/data/toy_params/lwe_b.cbor new file mode 100644 index 000000000..8e2948e76 --- /dev/null +++ b/apps/test-vectors/data/toy_params/lwe_b.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eadf2eff35133ffba075df11faecddd6e7af9ddc398011ec4568e5528812b3e2 +size 2365 diff --git a/apps/test-vectors/data/toy_params/lwe_ks.cbor b/apps/test-vectors/data/toy_params/lwe_ks.cbor new file mode 100644 index 000000000..2ca5f50b4 --- /dev/null +++ b/apps/test-vectors/data/toy_params/lwe_ks.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6da5baef0e787f6be56e218d8354e26904652602db964844156fdff08350ce6 +size 149 diff --git a/apps/test-vectors/data/toy_params/lwe_ms.cbor b/apps/test-vectors/data/toy_params/lwe_ms.cbor new file mode 100644 index 000000000..1a28ccff7 --- /dev/null +++ b/apps/test-vectors/data/toy_params/lwe_ms.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6df98676de04fe54b5ffc2eb30a82ebb706c9d7d5a4e0ed509700fec88761f7 +size 143 diff --git a/apps/test-vectors/data/toy_params/lwe_prod.cbor b/apps/test-vectors/data/toy_params/lwe_prod.cbor new file mode 100644 index 000000000..d013c65b2 --- /dev/null +++ b/apps/test-vectors/data/toy_params/lwe_prod.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:837b3bd3245d4d0534ed255fdef896fb4fa6998a258a14543dfdadd0bfc9b6dd +size 2365 diff --git a/apps/test-vectors/data/toy_params/lwe_sum.cbor b/apps/test-vectors/data/toy_params/lwe_sum.cbor new file mode 100644 index 000000000..a3d82c738 --- /dev/null +++ b/apps/test-vectors/data/toy_params/lwe_sum.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:699580ca92b9c2f9e1f57fb1e312c9e8cb29714f7acdef9d2ba05f798546751f +size 2365 diff --git a/apps/test-vectors/data/toy_params/small_lwe_secret_key.cbor b/apps/test-vectors/data/toy_params/small_lwe_secret_key.cbor new file mode 100644 index 000000000..73fd9a995 --- /dev/null +++ b/apps/test-vectors/data/toy_params/small_lwe_secret_key.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee9fcf45f1379ca3a7d7bf2b0e7a1cc920ceb496c0217e8604b0b58d2831749e +size 17 diff --git a/apps/test-vectors/data/valid_params_128/bsk.cbor b/apps/test-vectors/data/valid_params_128/bsk.cbor new file mode 100644 index 000000000..a52055a24 --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/bsk.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9afe7019acba5cda926f13e06df9930571611729d2f2e8ce41956e1f5e1db6f +size 61415556 diff --git a/apps/test-vectors/data/valid_params_128/glwe_after_id_br.cbor b/apps/test-vectors/data/valid_params_128/glwe_after_id_br.cbor new file mode 100644 index 000000000..7771618fb --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/glwe_after_id_br.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e54ab41056984595b077baff70236d934308cf5c0c33b4482fbfb129b3756c6 +size 36935 diff --git a/apps/test-vectors/data/valid_params_128/glwe_after_spec_br.cbor b/apps/test-vectors/data/valid_params_128/glwe_after_spec_br.cbor new file mode 100644 index 000000000..8b333abb0 --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/glwe_after_spec_br.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99a19c5d6d5f4fd81d9164d0ff96719ef362eabda256bce6a55cba6cb69e42bf +size 36935 diff --git a/apps/test-vectors/data/valid_params_128/ksk.cbor b/apps/test-vectors/data/valid_params_128/ksk.cbor new file mode 100644 index 000000000..e49074e9e --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/ksk.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e591ab9af1b6a0aede273f9a3abb65a4c387feb5fa06a6959e9314058ca0f7e5 +size 76861550 diff --git a/apps/test-vectors/data/valid_params_128/large_lwe_secret_key.cbor b/apps/test-vectors/data/valid_params_128/large_lwe_secret_key.cbor new file mode 100644 index 000000000..3c6ac887b --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/large_lwe_secret_key.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7a037b9eaa88d6385167579b93e26a0cb6976d9b8967416fd1173e113bda199 +size 2057 diff --git a/apps/test-vectors/data/valid_params_128/lwe_a.cbor b/apps/test-vectors/data/valid_params_128/lwe_a.cbor new file mode 100644 index 000000000..8b1b13c65 --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/lwe_a.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e69d2d2c064fc8c0460b39191ca65338146990349954f5ec5ebd01d93610e7eb +size 18493 diff --git a/apps/test-vectors/data/valid_params_128/lwe_after_id_pbs.cbor b/apps/test-vectors/data/valid_params_128/lwe_after_id_pbs.cbor new file mode 100644 index 000000000..98be067de --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/lwe_after_id_pbs.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:656f0009c7834c5bcb61621e222047516054b9bc5d0593d474ab8f1c086b67a6 +size 18493 diff --git a/apps/test-vectors/data/valid_params_128/lwe_after_spec_pbs.cbor b/apps/test-vectors/data/valid_params_128/lwe_after_spec_pbs.cbor new file mode 100644 index 000000000..9a2b54cf6 --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/lwe_after_spec_pbs.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e44ffa6e5a50a03d32721180a051c8ce62f1791d4853aeaebed0200c183a57cf +size 18493 diff --git a/apps/test-vectors/data/valid_params_128/lwe_b.cbor b/apps/test-vectors/data/valid_params_128/lwe_b.cbor new file mode 100644 index 000000000..3ad6f854a --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/lwe_b.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c806533b325b1009db38be2f9bef5f3b2fad6b77b4c71f2855ccc9d3b4162e98 +size 18493 diff --git a/apps/test-vectors/data/valid_params_128/lwe_ks.cbor b/apps/test-vectors/data/valid_params_128/lwe_ks.cbor new file mode 100644 index 000000000..ca6ac606e --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/lwe_ks.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59dba26d457f96478eda130cab5301fce86f23c6a8807de42f2a1e78c4985ca7 +size 7558 diff --git a/apps/test-vectors/data/valid_params_128/lwe_ms.cbor b/apps/test-vectors/data/valid_params_128/lwe_ms.cbor new file mode 100644 index 000000000..ee28ae5eb --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/lwe_ms.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa44aea29efd6d9e4d35a21a625d9cba155672e3f7ed3eddee1e211e62ad146b +size 7552 diff --git a/apps/test-vectors/data/valid_params_128/lwe_prod.cbor b/apps/test-vectors/data/valid_params_128/lwe_prod.cbor new file mode 100644 index 000000000..cca336840 --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/lwe_prod.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e76c24b2a0c9a842ad13dda35473c2514f9e7d20983b5ea0759c4521a91626d9 +size 18493 diff --git a/apps/test-vectors/data/valid_params_128/lwe_sum.cbor b/apps/test-vectors/data/valid_params_128/lwe_sum.cbor new file mode 100644 index 000000000..65e20208b --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/lwe_sum.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f3c40a134623b44779a556212477fea26eaed22450f3b6faeb8721d63699972 +size 18493 diff --git a/apps/test-vectors/data/valid_params_128/small_lwe_secret_key.cbor b/apps/test-vectors/data/valid_params_128/small_lwe_secret_key.cbor new file mode 100644 index 000000000..44c91b7b5 --- /dev/null +++ b/apps/test-vectors/data/valid_params_128/small_lwe_secret_key.cbor @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c70d1d78cc3760733850a353ace2b9c4705e840141b75841739e90e51247e18 +size 842 diff --git a/apps/test-vectors/src/main.rs b/apps/test-vectors/src/main.rs new file mode 100644 index 000000000..8968f9236 --- /dev/null +++ b/apps/test-vectors/src/main.rs @@ -0,0 +1,344 @@ +use serde::Serialize; +use std::fs::{File, create_dir_all, read_dir, remove_dir_all, remove_file}; +use std::path::{Path, PathBuf}; + +use tfhe::core_crypto::commons::generators::DeterministicSeeder; +use tfhe::core_crypto::commons::math::random::Seed; +use tfhe::core_crypto::prelude::*; +use tfhe_csprng::generators::SoftwareRandomGenerator; + +// If you modify the content of these parameters, don't forget to also update `data/README.md` +const RAND_SEED: u128 = 0x74666865; + +const MSG_A: u64 = 4; +const MSG_B: u64 = 3; + +const VALID_LWE_DIMENSION: LweDimension = LweDimension(833); +const VALID_GLWE_DIMENSION: GlweDimension = GlweDimension(1); +const VALID_POLYNOMIAL_SIZE: PolynomialSize = PolynomialSize(2048); +const VALID_GAUSSIAN_LWE_NOISE_STDDEV: f64 = 3.6158408373309336e-06; +const VALID_GAUSSIAN_GLWE_NOISE_STDDEV: f64 = 2.845267479601915e-15; +const VALID_PBS_DECOMPOSITION_BASE_LOG: DecompositionBaseLog = DecompositionBaseLog(23); +const VALID_PBS_DECOMPOSITION_LEVEL_COUNT: DecompositionLevelCount = DecompositionLevelCount(1); +const VALID_KS_DECOMPOSITION_BASE_LOG: DecompositionBaseLog = DecompositionBaseLog(3); +const VALID_KS_DECOMPOSITION_LEVEL_COUNT: DecompositionLevelCount = DecompositionLevelCount(5); + +const TOY_LWE_DIMENSION: LweDimension = LweDimension(10); +const TOY_GLWE_DIMENSION: GlweDimension = GlweDimension(1); +const TOY_POLYNOMIAL_SIZE: PolynomialSize = PolynomialSize(256); +const TOY_GAUSSIAN_LWE_NOISE_STDDEV: f64 = 0.; +const TOY_GAUSSIAN_GLWE_NOISE_STDDEV: f64 = 0.; +const TOY_PBS_DECOMPOSITION_BASE_LOG: DecompositionBaseLog = DecompositionBaseLog(24); +const TOY_PBS_DECOMPOSITION_LEVEL_COUNT: DecompositionLevelCount = DecompositionLevelCount(1); +const TOY_KS_DECOMPOSITION_BASE_LOG: DecompositionBaseLog = DecompositionBaseLog(37); +const TOY_KS_DECOMPOSITION_LEVEL_COUNT: DecompositionLevelCount = DecompositionLevelCount(1); + +const ENCODING: Encoding = Encoding { + ciphertext_modulus: CiphertextModulus::new_native(), + msg_bits: 4, +}; + +const SPEC_LUT: fn(u64) -> u64 = |x| (x * 2) & (1u64 << ENCODING.msg_bits); +const ID_LUT: fn(u64) -> u64 = |x| x; + +const DATA_DIR: &str = "./data"; + +struct Encoding { + ciphertext_modulus: CiphertextModulus, + msg_bits: usize, +} + +impl Encoding { + fn log_delta(&self) -> usize { + self.ciphertext_modulus.into_modulus_log().0 - self.msg_bits - 1 + } + + const fn msg_modulus(&self) -> usize { + 1 << self.msg_bits + } + + fn encode(&self, msg: u64) -> Plaintext { + Plaintext(msg << self.log_delta()) + } + + fn decode(&self, plaintext: Plaintext) -> u64 { + let decomposer = SignedDecomposer::new( + DecompositionBaseLog(self.msg_bits + 1), + DecompositionLevelCount(1), + ); + let decoded = decomposer.decode_plaintext(plaintext); + + decoded.0 + } + + fn encode_lut( + &self, + glwe_dimension: GlweDimension, + polynomial_size: PolynomialSize, + f: impl Fn(u64) -> u64, + ) -> GlweCiphertext> { + generate_programmable_bootstrap_glwe_lut( + polynomial_size, + glwe_dimension.to_glwe_size(), + self.msg_modulus(), + self.ciphertext_modulus, + 1 << self.log_delta(), + f, + ) + } +} + +fn modswitched_to_lwe( + modswitched: &LazyStandardModulusSwitchedLweCiphertext>, +) -> LweCiphertextOwned { + let cont = modswitched + .mask() + .chain(std::iter::once(modswitched.body())) + // The coefficients are converted to use the power of two encoding + .map(|coeff| coeff << (usize::BITS as usize - modswitched.log_modulus().0)) + .collect(); + + LweCiphertext::from_container( + cont, + CiphertextModulus::new(1 << modswitched.log_modulus().0), + ) +} + +fn store_data>(path: P, data: &Data, name: &str) { + let mut path = path.as_ref().to_path_buf(); + path.push(format!("{}.cbor", name)); + + let mut file = File::create(path).unwrap(); + ciborium::ser::into_writer(data, &mut file).unwrap(); +} + +#[allow(clippy::too_many_arguments)] +fn generate_test_vectors>( + path: P, + lwe_dimension: LweDimension, + glwe_dimension: GlweDimension, + polynomial_size: PolynomialSize, + lwe_noise_stddev: f64, + glwe_noise_stddev: f64, + pbs_decomp_base_log: DecompositionBaseLog, + pbs_decomp_level_count: DecompositionLevelCount, + ks_decomp_base_log: DecompositionBaseLog, + ks_decomp_level_count: DecompositionLevelCount, + encoding: Encoding, +) { + let path = path.as_ref(); + create_dir_all(path).unwrap(); + + let mut secret_generator = + SecretRandomGenerator::::new(Seed(RAND_SEED)); + let mut encryption_generator = EncryptionRandomGenerator::::new( + Seed(RAND_SEED), + &mut DeterministicSeeder::::new(Seed(RAND_SEED)), + ); + + let glwe_secret_key: GlweSecretKey> = + GlweSecretKey::generate_new_binary(glwe_dimension, polynomial_size, &mut secret_generator); + let large_lwe_secret_key = glwe_secret_key.as_lwe_secret_key(); + store_data(path, &large_lwe_secret_key, "large_lwe_secret_key"); + + let small_lwe_secret_key: LweSecretKey> = + LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_generator); + store_data(path, &small_lwe_secret_key, "small_lwe_secret_key"); + + let lwe_noise_distribution = Gaussian::from_standard_dev(StandardDev(lwe_noise_stddev), 0.); + let glwe_noise_distribution = Gaussian::from_standard_dev(StandardDev(glwe_noise_stddev), 0.); + + let plaintext_a = encoding.encode(MSG_A); + let lwe_a = allocate_and_encrypt_new_lwe_ciphertext( + &large_lwe_secret_key, + plaintext_a, + glwe_noise_distribution, + encoding.ciphertext_modulus, + &mut encryption_generator, + ); + store_data(path, &lwe_a, "lwe_a"); + + let plaintext_b = encoding.encode(MSG_B); + let lwe_b = allocate_and_encrypt_new_lwe_ciphertext( + &large_lwe_secret_key, + plaintext_b, + glwe_noise_distribution, + encoding.ciphertext_modulus, + &mut encryption_generator, + ); + store_data(path, &lwe_b, "lwe_b"); + + let mut lwe_sum = LweCiphertext::new( + 0u64, + glwe_dimension + .to_equivalent_lwe_dimension(polynomial_size) + .to_lwe_size(), + encoding.ciphertext_modulus, + ); + lwe_ciphertext_add(&mut lwe_sum, &lwe_a, &lwe_b); + let decrypted_sum = decrypt_lwe_ciphertext(&large_lwe_secret_key, &lwe_sum); + let res = encoding.decode(decrypted_sum); + + assert_eq!(res, MSG_A + MSG_B); + store_data(path, &lwe_sum, "lwe_sum"); + + let mut lwe_prod = LweCiphertext::new( + 0u64, + glwe_dimension + .to_equivalent_lwe_dimension(polynomial_size) + .to_lwe_size(), + encoding.ciphertext_modulus, + ); + lwe_ciphertext_cleartext_mul(&mut lwe_prod, &lwe_a, Cleartext(MSG_B)); + let decrypted_prod = decrypt_lwe_ciphertext(&large_lwe_secret_key, &lwe_prod); + let res = encoding.decode(decrypted_prod); + + assert_eq!(res, MSG_A * MSG_B); + store_data(path, &lwe_prod, "lwe_prod"); + + let ksk = allocate_and_generate_new_lwe_keyswitch_key( + &large_lwe_secret_key, + &small_lwe_secret_key, + ks_decomp_base_log, + ks_decomp_level_count, + lwe_noise_distribution, + encoding.ciphertext_modulus, + &mut encryption_generator, + ); + store_data(path, &ksk, "ksk"); + + let mut lwe_ks = LweCiphertext::new( + 0u64, + lwe_dimension.to_lwe_size(), + encoding.ciphertext_modulus, + ); + keyswitch_lwe_ciphertext(&ksk, &lwe_a, &mut lwe_ks); + + let decrypted_ks = decrypt_lwe_ciphertext(&small_lwe_secret_key, &lwe_ks); + let res = encoding.decode(decrypted_ks); + + assert_eq!(res, MSG_A); + store_data(path, &lwe_ks, "lwe_ks"); + + let bsk = par_allocate_and_generate_new_lwe_bootstrap_key( + &small_lwe_secret_key, + &glwe_secret_key, + pbs_decomp_base_log, + pbs_decomp_level_count, + glwe_noise_distribution, + encoding.ciphertext_modulus, + &mut encryption_generator, + ); + store_data(path, &bsk, "bsk"); + + let mut fourier_bsk = FourierLweBootstrapKey::new( + bsk.input_lwe_dimension(), + bsk.glwe_size(), + bsk.polynomial_size(), + bsk.decomposition_base_log(), + bsk.decomposition_level_count(), + ); + par_convert_standard_lwe_bootstrap_key_to_fourier(&bsk, &mut fourier_bsk); + + let lwe_in_ms = + LweCiphertext::from_container(lwe_ks.as_ref().to_vec(), lwe_ks.ciphertext_modulus()); + let log_modulus = polynomial_size.to_blind_rotation_input_modulus_log(); + + let modswitched = lwe_ciphertext_modulus_switch(lwe_in_ms, log_modulus); + let lwe_ms = modswitched_to_lwe(&modswitched); + store_data(path, &lwe_ms, "lwe_ms"); + + let mut id_lut = encoding.encode_lut(glwe_dimension, polynomial_size, ID_LUT); + + blind_rotate_assign(&modswitched, &mut id_lut, &fourier_bsk); + store_data(path, &id_lut, "glwe_after_id_br"); + + let mut lwe_pbs_id = LweCiphertext::new( + 0u64, + glwe_dimension + .to_equivalent_lwe_dimension(polynomial_size) + .to_lwe_size(), + encoding.ciphertext_modulus, + ); + + extract_lwe_sample_from_glwe_ciphertext(&id_lut, &mut lwe_pbs_id, MonomialDegree(0)); + + let decrypted_pbs_id = decrypt_lwe_ciphertext(&large_lwe_secret_key, &lwe_pbs_id); + let res = encoding.decode(decrypted_pbs_id); + + assert_eq!(res, MSG_A); + store_data(path, &lwe_pbs_id, "lwe_after_id_pbs"); + + let mut spec_lut = encoding.encode_lut(glwe_dimension, polynomial_size, SPEC_LUT); + + blind_rotate_assign(&modswitched, &mut spec_lut, &fourier_bsk); + store_data(path, &spec_lut, "glwe_after_spec_br"); + + let mut lwe_pbs_spec = LweCiphertext::new( + 0u64, + glwe_dimension + .to_equivalent_lwe_dimension(polynomial_size) + .to_lwe_size(), + encoding.ciphertext_modulus, + ); + + extract_lwe_sample_from_glwe_ciphertext(&spec_lut, &mut lwe_pbs_spec, MonomialDegree(0)); + + let decrypted_pbs_spec = decrypt_lwe_ciphertext(&large_lwe_secret_key, &lwe_pbs_spec); + let res = encoding.decode(decrypted_pbs_spec); + + assert_eq!(res, SPEC_LUT(MSG_A)); + store_data(path, &lwe_pbs_spec, "lwe_after_spec_pbs"); +} + +fn rm_dir_except_readme>(dir: P) { + let dir = dir.as_ref(); + + for entry_result in read_dir(dir).unwrap() { + let entry = entry_result.unwrap(); + let path = entry.path(); + + // Skip the README.md file at the root. + if entry.file_name() == "README.md" { + continue; + } + + if path.is_dir() { + remove_dir_all(&path).unwrap(); + } else { + remove_file(&path).unwrap(); + } + } +} + +fn main() { + rm_dir_except_readme(DATA_DIR); + + generate_test_vectors( + PathBuf::from(DATA_DIR).join("valid_params_128"), + VALID_LWE_DIMENSION, + VALID_GLWE_DIMENSION, + VALID_POLYNOMIAL_SIZE, + VALID_GAUSSIAN_LWE_NOISE_STDDEV, + VALID_GAUSSIAN_GLWE_NOISE_STDDEV, + VALID_PBS_DECOMPOSITION_BASE_LOG, + VALID_PBS_DECOMPOSITION_LEVEL_COUNT, + VALID_KS_DECOMPOSITION_BASE_LOG, + VALID_KS_DECOMPOSITION_LEVEL_COUNT, + ENCODING, + ); + + generate_test_vectors( + PathBuf::from(DATA_DIR).join("toy_params"), + TOY_LWE_DIMENSION, + TOY_GLWE_DIMENSION, + TOY_POLYNOMIAL_SIZE, + TOY_GAUSSIAN_LWE_NOISE_STDDEV, + TOY_GAUSSIAN_GLWE_NOISE_STDDEV, + TOY_PBS_DECOMPOSITION_BASE_LOG, + TOY_PBS_DECOMPOSITION_LEVEL_COUNT, + TOY_KS_DECOMPOSITION_BASE_LOG, + TOY_KS_DECOMPOSITION_LEVEL_COUNT, + ENCODING, + ); +} diff --git a/scripts/pull_lfs_data.sh b/scripts/pull_lfs_data.sh index 3c60c6a59..fe7135581 100755 --- a/scripts/pull_lfs_data.sh +++ b/scripts/pull_lfs_data.sh @@ -3,14 +3,14 @@ set -e if [ $# -lt 1 ]; then - echo "invalid arguments, usage:\n" - echo "$0 " - exit 1 + echo "invalid arguments, usage:\n" + echo "$0 " + exit 1 fi if ! git lfs env 2>/dev/null >/dev/null; then - echo "git lfs is not installed, please install it and try again" - exit 1 + echo "git lfs is not installed, please install it and try again" + exit 1 fi git lfs pull --include="$1/*" --exclude="" diff --git a/scripts/test_vectors.sh b/scripts/test_vectors.sh new file mode 100755 index 000000000..0cbead551 --- /dev/null +++ b/scripts/test_vectors.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# This script generates or checks the SHA256 for the test vectors in `apps/test-vectors` + +set -e + +MODE="$1" +TARGET_DIR="$2" +CHECKSUM_FILE="checksums.sha256" + +if [ -z "$MODE" ] || [ -z "$TARGET_DIR" ]; then + echo "Usage: $0 " + echo "Modes:" + echo " generate Generate the vectors and update the checksum file" + echo " check Generate the vectors and compare their content with the checksum file" + exit 1 +fi + +if [ ! -d "$TARGET_DIR" ]; then + echo "Error: Directory '$TARGET_DIR' not found." + exit 1 +fi + +checksum () { + find data -name '*.cbor' -type f -exec sha256sum {} + | sort +} + +# Generate the test vectors +echo "Generating test-vectors in $TARGET_DIR..." +cd $TARGET_DIR +cargo run --release + +if [ "$MODE" == "generate" ]; then + echo "Generating hashes in $TARGET_DIR/$CHECKSUM_FILE..." + checksum > "$CHECKSUM_FILE" + echo "Done." + +elif [ "$MODE" == "check" ]; then + echo "Checking vectors integrity against $TARGET_DIR/$CHECKSUM_FILE..." + + if [ ! -f "$CHECKSUM_FILE" ]; then + echo "Error: Checksum file $CHECKSUM_FILE not found." + echo "Run 'generate' mode first." + exit 1 + fi + + diffs=$(comm -3 checksums.sha256 <(checksum)) + if [ -n "$diffs" ]; then + echo "Error: Checksum file and generated vectors do not match." + echo $diffs + exit 1 + fi + echo "Done." + +else + echo "Error: Invalid mode '$MODE'." + echo "Mode must be 'generate' or 'check'." + exit 1 +fi diff --git a/tasks/src/check_tfhe_docs_are_tested.rs b/tasks/src/check_tfhe_docs_are_tested.rs index 65a1ae283..63c4538bf 100644 --- a/tasks/src/check_tfhe_docs_are_tested.rs +++ b/tasks/src/check_tfhe_docs_are_tested.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use std::io::{Error, ErrorKind}; // TODO use .gitignore or git to resolve ignored files -const DIR_TO_IGNORE: [&str; 2] = [".git", "target"]; +const DIR_TO_IGNORE: [&str; 3] = [".git", "target", "apps/test-vectors"]; const FILES_TO_IGNORE: [&str; 9] = [ // This contains fragments of code that are unrelated to TFHE-rs