├── .cargo └── config.toml ├── .gitattributes ├── .github └── workflows │ ├── docs.yml │ ├── playground-preprod.yml │ ├── playground-prod.yml │ ├── rust.yml │ └── website.yml ├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── Tables_of_things.md ├── benchmarks ├── bfv_zkp │ ├── Cargo.toml │ └── src │ │ ├── bfv.rs │ │ ├── main.rs │ │ └── poly_ring.rs └── cannonical_norm_noise_model │ ├── Cargo.toml │ ├── Results.csv │ └── src │ ├── main.rs │ └── ops.rs ├── emsdk ├── Cargo.toml ├── crates-io.md └── src │ └── lib.rs ├── examples ├── allowlist_zkp │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── bin │ │ ├── prover.rs │ │ └── verifier.rs │ │ └── lib.rs ├── amm │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── bigint │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── calculator_fractional │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── calculator_rational │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── chi_sq │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── dot_prod │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── mean_variance │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── ordering_zkp │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── pir │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── polynomial_zkp │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── private_tx_linkedproof │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── simple_multiply │ ├── Cargo.toml │ └── src │ │ └── main.rs └── sudoku_zkp │ ├── Cargo.toml │ └── src │ └── main.rs ├── logproof ├── Cargo.toml ├── benches │ └── linear_relation.rs ├── sdlp-changes.tex ├── src │ ├── assertions.rs │ ├── bfv_statement.rs │ ├── crypto.rs │ ├── error.rs │ ├── generators.rs │ ├── inner_product.rs │ ├── lib.rs │ ├── linear_algebra.rs │ ├── linear_relation.rs │ ├── math.rs │ ├── rings.rs │ ├── test.rs │ └── transcript.rs ├── tests │ └── seal.rs └── utils │ └── compute_params.py ├── purge-github-caches.sh ├── seal_fhe ├── Cargo.toml ├── bindgen_wrapper.h ├── build.rs ├── crates-io.md ├── src │ ├── bfv_evaluator.rs │ ├── context.rs │ ├── data_structures.rs │ ├── encoder.rs │ ├── encryption_parameters.rs │ ├── encryptor_decryptor.rs │ ├── error.rs │ ├── evaluator.rs │ ├── evaluator_base.rs │ ├── key_generator.rs │ ├── lib.rs │ ├── modulus.rs │ └── plaintext_ciphertext.rs └── tests │ ├── assumptions.rs │ ├── data │ ├── public_key.bin │ └── secret_key.bin │ └── test_common.rs ├── sunscreen ├── Cargo.toml ├── benches │ ├── fractional_range_proof.rs │ └── smart_fhe.rs ├── crates-io.md ├── docs │ └── assets │ │ └── katex-header.html ├── src │ ├── compiler.rs │ ├── error.rs │ ├── fhe │ │ └── mod.rs │ ├── lib.rs │ ├── linked.rs │ ├── params.rs │ ├── types │ │ ├── bfv │ │ │ ├── batched.rs │ │ │ ├── fractional.rs │ │ │ ├── mod.rs │ │ │ ├── rational.rs │ │ │ ├── signed.rs │ │ │ └── unsigned.rs │ │ ├── intern │ │ │ ├── fhe_literal.rs │ │ │ ├── fhe_program_node.rs │ │ │ ├── input.rs │ │ │ ├── mod.rs │ │ │ ├── output.rs │ │ │ └── u64_literal.rs │ │ ├── mod.rs │ │ ├── ops │ │ │ ├── add.rs │ │ │ ├── div.rs │ │ │ ├── insert.rs │ │ │ ├── mod.rs │ │ │ ├── mul.rs │ │ │ ├── neg.rs │ │ │ ├── rotate.rs │ │ │ └── sub.rs │ │ └── zkp │ │ │ ├── bfv_plaintext.rs │ │ │ ├── field.rs │ │ │ ├── gadgets │ │ │ ├── arithmetic.rs │ │ │ ├── binary.rs │ │ │ └── mod.rs │ │ │ ├── mod.rs │ │ │ ├── program_node.rs │ │ │ └── rns_polynomial.rs │ └── zkp │ │ └── mod.rs └── tests │ ├── array.rs │ ├── chaining.rs │ ├── edge_cases.rs │ ├── encrypt_decrypt.rs │ ├── features.rs │ ├── fhe_program_tests.rs │ ├── fractional.rs │ ├── linked.rs │ ├── multi_program.rs │ ├── rational.rs │ ├── sdlp.rs │ ├── serialization.rs │ ├── signed.rs │ ├── simd.rs │ ├── typename_tests.rs │ ├── unsigned.rs │ └── zkp_program_tests.rs ├── sunscreen_backend ├── Cargo.toml ├── crates-io.md └── src │ ├── error.rs │ ├── lib.rs │ ├── noise_model │ ├── canonical_embedding_norm.rs │ ├── measured_model.rs │ └── mod.rs │ └── transforms │ ├── insert_relinearizations.rs │ └── mod.rs ├── sunscreen_compiler_common ├── Cargo.toml ├── crates-io.md └── src │ ├── context.rs │ ├── graph.rs │ ├── lib.rs │ ├── macros │ ├── mod.rs │ └── type_name.rs │ └── transforms │ ├── common_subexpression_elimination.rs │ ├── graph_transforms.rs │ └── mod.rs ├── sunscreen_compiler_macros ├── Cargo.toml ├── crates-io.md └── src │ ├── attr_parsing.rs │ ├── error.rs │ ├── fhe_program.rs │ ├── fhe_program_transforms.rs │ ├── internals │ ├── attr.rs │ ├── case.rs │ └── mod.rs │ ├── lib.rs │ ├── type_name.rs │ └── zkp_program.rs ├── sunscreen_docs ├── README.md ├── book.toml ├── deploy.sh ├── running_tests.md └── src │ ├── SUMMARY.md │ ├── fhe │ ├── advanced │ │ ├── advanced.md │ │ ├── batching │ │ │ ├── batched.md │ │ │ ├── batching.md │ │ │ └── dot_product.md │ │ ├── bfv.md │ │ ├── carryless_arithmetic.md │ │ ├── faq.md │ │ ├── features.md │ │ ├── good_fhe_programs.md │ │ ├── noise_margin.md │ │ ├── performance.md │ │ ├── plain_modulus │ │ │ └── plain_modulus.md │ │ ├── pruning_keys.md │ │ └── wasm.md │ ├── compiler │ │ ├── compiler.md │ │ ├── features.md │ │ ├── performance.md │ │ └── who.md │ ├── fhe_programs │ │ ├── apps.md │ │ ├── chaining.md │ │ ├── chaining1-chubby.svg │ │ ├── chaining2-chubby.svg │ │ ├── example.md │ │ ├── factoring_fhe_programs.md │ │ ├── fhe_programs.md │ │ ├── parameter_sharing.md │ │ ├── pir_intro.md │ │ ├── pir_matrix.md │ │ ├── pir_simple.md │ │ ├── private_tests.md │ │ ├── programs_to_apps.md │ │ ├── runtime │ │ │ ├── decryption.md │ │ │ ├── encryption.md │ │ │ ├── key_generation.md │ │ │ ├── running_fhe_programs.md │ │ │ ├── runtime.md │ │ │ └── serialization.md │ │ ├── troubleshooting.md │ │ ├── types │ │ │ ├── fractional.md │ │ │ ├── rational.md │ │ │ ├── signed.md │ │ │ └── types.md │ │ └── writing_an_fhe_program │ │ │ ├── fhe_programs.md │ │ │ ├── limitations.md │ │ │ └── writing_an_fhe_program.md │ ├── getting_started │ │ ├── example.md │ │ ├── getting_started.md │ │ └── installation.md │ └── intro │ │ ├── intro.md │ │ ├── prelim.md │ │ └── why.md │ └── zkp │ ├── advanced │ ├── advanced.md │ ├── constant_inputs.md │ ├── gadgets.md │ ├── optimized.md │ ├── wasm.md │ └── zkp_type.md │ ├── applications │ ├── allowlist.md │ ├── applications.md │ └── sudoku.md │ ├── compiler │ └── compiler.md │ ├── compiling │ └── compiling.md │ ├── faq │ └── faq.md │ ├── getting_started │ ├── example.md │ ├── getting_started.md │ └── installation.md │ ├── intro │ ├── intro.md │ ├── prelim.md │ ├── prereq.md │ └── zkp.svg │ ├── runtime │ ├── prove.md │ ├── runtime.md │ ├── serialization.md │ └── verify.md │ └── zkp_programs │ ├── attributes.md │ ├── constraints.md │ ├── limitations.md │ ├── troubleshooting.md │ ├── types.md │ └── zkp_programs.md ├── sunscreen_fhe_program ├── Cargo.toml ├── crates-io.md └── src │ ├── error.rs │ ├── lib.rs │ ├── literal.rs │ ├── operation.rs │ └── validation.rs ├── sunscreen_math ├── Cargo.toml ├── benches │ ├── cpu.rs │ └── gpu.rs ├── build.rs ├── crates-io.md └── src │ ├── combination.rs │ ├── cpu.rs │ ├── cuda_impl │ ├── mod.rs │ ├── ristrettovec.rs │ ├── scalarvec.rs │ └── shaders │ │ └── sunscreen_math.cu │ ├── error.rs │ ├── field │ └── mod.rs │ ├── geometry.rs │ ├── lib.rs │ ├── metal_impl │ ├── mod.rs │ ├── ristrettovec.rs │ ├── scalarvec.rs │ └── shaders │ │ ├── field.metal │ │ ├── include │ │ ├── constants.hpp.metal │ │ ├── field.hpp.metal │ │ ├── inttypes.hpp.metal │ │ ├── lookuptable.hpp.metal │ │ ├── ristretto.hpp.metal │ │ └── scalar.hpp.metal │ │ ├── lookuptable.metal │ │ ├── ristretto.metal │ │ └── scalar.metal │ ├── misc_traits.rs │ ├── opencl_impl │ ├── mod.rs │ ├── multiexp.md │ ├── multiexp.rs │ ├── radix_sort.rs │ ├── ristrettovec.rs │ ├── rle.rs │ ├── scalarvec.rs │ └── shaders │ │ ├── constants.cl │ │ ├── field2625.cl │ │ ├── include │ │ ├── constants.h.cl │ │ ├── field2625.h.cl │ │ ├── inttypes.h.cl │ │ ├── ristrettopoint.h.cl │ │ └── scalar29.h.cl │ │ ├── inttypes.cl │ │ ├── main.cl │ │ ├── multiexp.cl │ │ ├── radix_sort.cl │ │ ├── ristrettopoint.cl │ │ ├── ristrettopoint_prefixsum.cl │ │ ├── rle.cl │ │ └── scalar29.cl │ ├── pina.rs │ ├── poly │ └── mod.rs │ ├── ring │ ├── barrett.rs │ └── mod.rs │ ├── security.rs │ ├── stats.rs │ ├── test_impl.rs │ └── webgpu_impl │ ├── mod.rs │ ├── ristrettovec.rs │ ├── scalarvec.rs │ ├── scalarvectest.rs │ └── shaders │ ├── basic.test.wgsl │ ├── bindings.wgsl │ ├── constants.wgsl │ ├── scalar.test.wgsl │ ├── scalar.wgsl │ ├── u64.test.wgsl │ └── u64.wgsl ├── sunscreen_math_macros ├── Cargo.toml ├── crates-io.md └── src │ └── lib.rs ├── sunscreen_runtime ├── Cargo.toml ├── crates-io.md └── src │ ├── array.rs │ ├── builder.rs │ ├── error.rs │ ├── keys.rs │ ├── lib.rs │ ├── linked.rs │ ├── metadata.rs │ ├── run.rs │ ├── runtime.rs │ └── serialization.rs ├── sunscreen_tfhe ├── .vscode │ ├── launch.json │ └── settings.json ├── Cargo.toml ├── barrett.py ├── benches │ ├── fft.rs │ ├── ops.rs │ └── tfhe_proof.rs ├── images │ ├── circuit_bootstrapping.graffle │ └── circuit_bootstrapping.png ├── mont.py ├── notes │ ├── glwe_scheme_switching.md │ └── leveled_computation.md └── src │ ├── dst.rs │ ├── entities │ ├── bivariate_lookup_table.rs │ ├── blind_rotation_shift.rs │ ├── bootstrap_key.rs │ ├── circuit_bootstrapping_private_keyswitch_keys.rs │ ├── ggsw_ciphertext.rs │ ├── ggsw_ciphertext_fft.rs │ ├── glev_ciphertext.rs │ ├── glev_ciphertext_fft.rs │ ├── glwe_ciphertext.rs │ ├── glwe_ciphertext_fft.rs │ ├── glwe_keyswitch_key.rs │ ├── glwe_secret_key.rs │ ├── lev_ciphertext.rs │ ├── lwe_ciphertext.rs │ ├── lwe_ciphertext_list.rs │ ├── lwe_keyswitch_key.rs │ ├── lwe_public_key.rs │ ├── lwe_secret_key.rs │ ├── mod.rs │ ├── polynomial.rs │ ├── polynomial_fft.rs │ ├── polynomial_list.rs │ ├── private_functional_keyswitch_key.rs │ ├── public_functional_keyswitch_key.rs │ ├── rlwe_public_key.rs │ ├── scheme_switch_key.rs │ ├── scheme_switch_key_fft.rs │ └── univariate_lookup_table.rs │ ├── error.rs │ ├── high_level.rs │ ├── iteration │ ├── mod.rs │ └── triangular_pairs.rs │ ├── lib.rs │ ├── macros.rs │ ├── math │ ├── basic.rs │ ├── fft │ │ ├── cyclic │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── negacyclic │ │ │ └── mod.rs │ ├── goldilocks_field.rs │ ├── mod.rs │ ├── polynomial.rs │ ├── radix.rs │ ├── simd │ │ ├── avx2.rs │ │ ├── mod.rs │ │ └── scalar.rs │ └── torus.rs │ ├── ops │ ├── bootstrapping │ │ ├── blind_rotation.rs │ │ ├── circuit_bootstrapping.rs │ │ ├── mod.rs │ │ ├── programmable_bootstrapping.rs │ │ └── scheme_switch.rs │ ├── ciphertext │ │ ├── glev_ciphertext_ops.rs │ │ ├── glwe_ciphertext_ops.rs │ │ ├── lev_ciphertext_ops.rs │ │ ├── lwe_ciphertext_ops.rs │ │ └── mod.rs │ ├── encryption │ │ ├── ggsw_encryption.rs │ │ ├── glev_encryption.rs │ │ ├── glwe_encryption.rs │ │ ├── lwe_encryption.rs │ │ ├── mod.rs │ │ └── rlwe_encryption.rs │ ├── fft_ops.rs │ ├── homomorphisms │ │ ├── lwe.rs │ │ └── mod.rs │ ├── keyswitch │ │ ├── glwe_keyswitch.rs │ │ ├── glwe_keyswitch_key.rs │ │ ├── lwe_keyswitch.rs │ │ ├── lwe_keyswitch_key.rs │ │ ├── mod.rs │ │ ├── private_functional_keyswitch.rs │ │ └── public_functional_keyswitch.rs │ ├── mod.rs │ └── polynomial │ │ └── mod.rs │ ├── params.rs │ ├── rand.rs │ ├── scratch.rs │ └── zkp.rs ├── sunscreen_zkp_backend ├── Cargo.toml ├── crates-io.md └── src │ ├── bulletproofs.rs │ ├── error.rs │ ├── exec.rs │ ├── jit.rs │ └── lib.rs └── website ├── deploy.bash └── src └── index.html /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [registries] 2 | sunscreen = { index = "https://crates.sunscreen.tech/git/index" } 3 | 4 | [env] 5 | EMCC_CFLAGS = "-sERROR_ON_UNDEFINED_SYMBOLS=0 -sDISABLE_EXCEPTION_CATCHING=0 -sALLOW_MEMORY_GROWTH" 6 | 7 | [build] 8 | #rustflags = ['--cfg=curve25519_dalek_bits="32"'] 9 | 10 | [target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'] 11 | rustflags = ["-Ctarget-feature=+avx2"] -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rs text eol=LF 2 | *.toml text eol=LF 3 | *.md text eol=LF 4 | *.json text eol=LF 5 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs-deployment 2 | 3 | on: 4 | push: 5 | branches: [ docs ] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | concurrency: docs-production 11 | 12 | jobs: 13 | build-docs: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: 'recursive' 20 | - uses: actions/cache@v3 21 | with: 22 | path: | 23 | ~/.cargo/bin/ 24 | ~/.cargo/registry/index/ 25 | ~/.cargo/registry/cache/ 26 | ~/.cargo/git/db/ 27 | mdBook/target 28 | key: ${{ runner.os }}-cargo-mdBook-${{ hashFiles('mdBook/Cargo.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-cargo-mdBook- 31 | ${{ runner.os }}-cargo- 32 | - name: Build mdBook 33 | run: cargo build --release 34 | working-directory: ./mdBook 35 | - name: Build docs 36 | run: ../mdBook/target/release/mdbook build 37 | working-directory: ./sunscreen_docs 38 | -------------------------------------------------------------------------------- /.github/workflows/playground-prod.yml: -------------------------------------------------------------------------------- 1 | name: "Playground production deployment" 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | deployment_id: 7 | description: 'The ID of a preproduction playground deployment version to deploy to production' 8 | required: true 9 | 10 | concurrency: playground-production 11 | 12 | jobs: 13 | deploy: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Deploy 18 | run: aws elasticbeanstalk update-environment --application-name Playground --environment-id $ENVIRONMENT_ID --description "Deployment for ID ${{ github.event.inputs.deployment_id}}" --version-label ${{ github.event.inputs.deployment_id}} 19 | env: 20 | AWS_ACCESS_KEY_ID: ${{ secrets.DEPLOYMENT_AWS_ACCESS_KEY }} 21 | AWS_DEFAULT_REGION: us-west-2 22 | AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOYMENT_AWS_ACCESS_KEY_SECRET }} 23 | ENVIRONMENT_ID: e-ehgmnmqadu -------------------------------------------------------------------------------- /.github/workflows/website.yml: -------------------------------------------------------------------------------- 1 | name: website-deployment 2 | 3 | on: 4 | push: 5 | branches: [ website ] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | concurrency: website-production 11 | 12 | jobs: 13 | deploy-website: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: 'recursive' 20 | - uses: actions/cache@v3 21 | with: 22 | path: | 23 | ~/.cargo/bin/ 24 | ~/.cargo/registry/index/ 25 | ~/.cargo/registry/cache/ 26 | ~/.cargo/git/db/ 27 | mdBook/target 28 | key: ${{ runner.os }}-cargo-mdBook-${{ hashFiles('mdBook/Cargo.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-cargo-mdBook- 31 | ${{ runner.os }}-cargo- 32 | - name: Copy site to S3 33 | run: aws s3 cp src/* s3://sunscreen-site 34 | working-directory: ./website 35 | env: 36 | AWS_ACCESS_KEY_ID: ${{ secrets.DOCS_ACCESS_KEY_ID }} 37 | AWS_SECRET_ACCESS_KEY: ${{ secrets.DOCS_SECRET_ACCESS_KEY }} 38 | AWS_DEFAULT_REGION: us-west-2 39 | - name: Invalidate Cloudfront 40 | run: aws cloudfront create-invalidation --distribution-id E10NWJGCYHNFM4 --paths "/*" 41 | env: 42 | AWS_ACCESS_KEY_ID: ${{ secrets.DOCS_ACCESS_KEY_ID }} 43 | AWS_SECRET_ACCESS_KEY: ${{ secrets.DOCS_SECRET_ACCESS_KEY }} 44 | AWS_DEFAULT_REGION: us-west-2 45 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "seal_fhe/SEAL"] 2 | path = seal_fhe/SEAL 3 | url = https://github.com/Sunscreen-tech/SEAL.git 4 | branch = sunscreen 5 | fetchRecurseSubmodules = true 6 | [submodule "emsdk/emsdk"] 7 | path = emsdk/emsdk 8 | url = https://github.com/emscripten-core/emsdk.git 9 | [submodule "mdBook"] 10 | path = mdBook 11 | url = https://github.com/Sunscreen-tech/mdBook.git 12 | [submodule "rust-playground"] 13 | path = rust-playground 14 | url = https://github.com/Sunscreen-tech/rust-playground.git 15 | branch = sunscreen 16 | [submodule "sunscreen_bulletproofs"] 17 | path = sunscreen_bulletproofs 18 | url = https://github.com/Sunscreen-tech/bulletproofs_zkcrypto.git 19 | [submodule "sunscreen_curve25519"] 20 | path = sunscreen_curve25519 21 | url = https://github.com/Sunscreen-tech/curve25519-dalek-ng.git 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug.allowBreakpointsEverywhere": true, 3 | "cSpell.words": [ 4 | "Bincode", 5 | "ciphertext", 6 | "ciphertexts", 7 | "Iliashenko", 8 | "petgraph", 9 | "println", 10 | "relinearization", 11 | "relinearizations", 12 | "relinearize", 13 | "struct", 14 | "usize", 15 | "Walkthrough", 16 | "Xeon" 17 | ], 18 | "OpenCL.server.buildOptions": [ 19 | "-I", "${workspaceFolder}/sunscreen_math/src/opencl_impl/shaders/include" 20 | ], 21 | "OpenCL.server.deviceID": 124252029, 22 | "rust-analyzer.showUnlinkedFileNotification": false 23 | } 24 | -------------------------------------------------------------------------------- /Tables_of_things.md: -------------------------------------------------------------------------------- 1 | # Dungeon Master's tables of FHE things 2 | 3 | ## BFV 4 | 5 | When using a plain modulus large enough for batching, generating relin keys fails at `N=1024,2048`. 6 | 7 | ### Noise budget impact at minimum plain modulus to support batching of a single operation 8 | 9 | | n | Add | Mul+relin | 10 | |-------|------|-----------| 11 | | 1024 | N/A | N/A | 12 | | 2048 | N/A | N/A | 13 | | 4096 | ~0 | ~26 | 14 | | 8192 | ~0 | ~28 | 15 | | 16384 | ~0 | ~29 | 16 | | 32768 | ~0 | ~30 | 17 | 18 | ### Noise budget at minimum plain modulus to support batching 19 | 20 | | n | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 21 | |------|------|------|------|------|-------|-------| 22 | | bits | N/A | N/A | 49 | 149 | 365 | 800 | 23 | 24 | ### Key sizes 25 | 26 | * Precise sizes may vary with RNG. 27 | * Some keys fail to generate for some poly degrees (marked as N/A). 28 | * Coefficient modulus is default for 128-bit security. 29 | * Plain modulus = 1,000,000. (Should have no effect) 30 | 31 | | Poly degree | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 32 | |---------------------|----------|-----------|------------|------------|--------------|------------| 33 | | secret key | 4.17 kiB | 15.15 kiB | 68.60 kiB | 264.46 kiB | 1,004.55 kiB | 3.77 MiB | 34 | | public key | 8.41 kiB | 30.19 kiB | 130.92 kiB | 529.19 kiB | 1.96 MiB | 7.53 MiB | 35 | | compact public key | 4.27 kiB | 15.22 kiB | 68.69 kiB | 264.50 kiB | 1,004.58 kiB | 3.77 MiB | 36 | | relin keys | N/A | N/A | 270.42 kiB | 2.07 MiB | 15.69 MiB | 113.02 MiB | 37 | | compact relin keys | N/A | N/A | 131.21 kiB | 1.03 MiB | 7.85 MiB | 56.51 MiB | 38 | | Galois keys | N/A | N/A | 5.79 MiB | 49.65 MiB | 408.23 MiB | 3.09 GiB | 39 | | compact Galois keys | N/A | N/A | N/A | N/A | N/A | N/A | 40 | 41 | ### Ciphertext sizes 42 | * Precise sizes may vary with RNG. 43 | * Coefficient modulus is default for 128-bit security. 44 | * Plain modulus = 1,000,000. (Should have no effect) 45 | 46 | | Poly degree | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 47 | |-------------|----------|-----------|-----------|------------|----------|----------| 48 | | secret key | 8.42 kiB | 30.19 kiB | 86.45 kiB | 422.24 kiB | 1.74 MiB | 7.06 MiB | -------------------------------------------------------------------------------- /benchmarks/bfv_zkp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bfv_zkp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ark-ff = { workspace = true } 10 | ark-poly = { workspace = true } 11 | crypto-bigint = { workspace = true } 12 | rand = { workspace = true } 13 | rand_distr = { workspace = true } 14 | sunscreen = { workspace = true } 15 | sunscreen_zkp_backend = { workspace = true } 16 | 17 | [dev-dependencies] 18 | env_logger = { workspace = true } -------------------------------------------------------------------------------- /benchmarks/bfv_zkp/src/main.rs: -------------------------------------------------------------------------------- 1 | mod bfv; 2 | mod poly_ring; 3 | 4 | /// STOP. DO NOT USE THIS CODE FOR PRODUCTION. 5 | /// Security is a non-goal for this library. In fact, this library is known 6 | /// to be insecure. 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /benchmarks/cannonical_norm_noise_model/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cannonical_norm_noise_model" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen_backend = { workspace = true } 10 | seal_fhe = { workspace = true } 11 | sunscreen_runtime = { workspace = true } 12 | sunscreen_fhe_program = { workspace = true } 13 | rayon = { workspace = true } 14 | env_logger = { workspace = true } 15 | log = { workspace = true } 16 | -------------------------------------------------------------------------------- /emsdk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "emsdk" 3 | version = "0.1.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "A crate for building C++ projects with emcake. This facilitates building wasm binaries with both Rust and C++ code." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["emsdk", "emcc", "emcmake"] 14 | categories = ["development-tools"] 15 | readme = "crates-io.md" 16 | 17 | [dependencies] 18 | 19 | [build-dependencies] 20 | fs_extra = { workspace = true } 21 | reqwest = { workspace = true } 22 | -------------------------------------------------------------------------------- /emsdk/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). 2 | -------------------------------------------------------------------------------- /emsdk/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::io::Write; 3 | use std::path::{Path, PathBuf}; 4 | use std::process::{Command, Stdio}; 5 | 6 | pub struct Config { 7 | defines: HashMap, 8 | emcc_args: Vec, 9 | path: PathBuf, 10 | } 11 | 12 | impl Config { 13 | pub fn new>(path: P) -> Self { 14 | Self { 15 | path: path.as_ref().canonicalize().unwrap(), 16 | defines: HashMap::new(), 17 | emcc_args: vec![], 18 | } 19 | } 20 | 21 | pub fn define(mut self, k: &str, v: &str) -> Self { 22 | self.defines.insert(k.to_owned(), v.to_owned()); 23 | self 24 | } 25 | 26 | pub fn emcc_arg(mut self, arg: &str) -> Self { 27 | self.emcc_args.push(arg.to_owned()); 28 | self 29 | } 30 | 31 | pub fn build(self) -> PathBuf { 32 | let output_directory = PathBuf::from(std::env::var("OUT_DIR").unwrap()); 33 | 34 | let script_path = output_directory.join("build.sh"); 35 | let mut script = std::fs::File::create(&script_path).unwrap(); 36 | 37 | let build_dir = output_directory.join("build"); 38 | 39 | writeln!(script, "set -e").unwrap(); 40 | writeln!(script, "{}", self.get_cmake_command(&build_dir)).unwrap(); 41 | writeln!(script, "emmake make -C {:#?} -j", build_dir).unwrap(); 42 | 43 | let status = Command::new("bash") 44 | .arg(script_path) 45 | .stdout(Stdio::inherit()) 46 | .stderr(Stdio::inherit()) 47 | .spawn() 48 | .unwrap() 49 | .wait() 50 | .unwrap(); 51 | 52 | if !status.success() { 53 | panic!("Compilation failed"); 54 | } 55 | 56 | build_dir.to_owned() 57 | } 58 | 59 | fn get_cmake_command(&self, build_dir: &Path) -> String { 60 | let mut args = vec![ 61 | "emcmake".to_owned(), 62 | "cmake".to_owned(), 63 | "-S".to_owned(), 64 | self.path.to_str().unwrap().to_owned(), 65 | "-B".to_owned(), 66 | build_dir.to_str().unwrap().to_owned(), 67 | ]; 68 | 69 | for (k, v) in &self.defines { 70 | args.push(format!("-D{}=\"{}\"", k, v.to_owned())); 71 | } 72 | 73 | args.join(" ") 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/allowlist_zkp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /examples/allowlist_zkp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "allowlist_zkp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.79" 8 | bincode = { workspace = true } 9 | sunscreen = { workspace = true } 10 | -------------------------------------------------------------------------------- /examples/allowlist_zkp/README.md: -------------------------------------------------------------------------------- 1 | # allowlist 2 | 3 | This example shows a possible project layout of a [cargo package](pkg-docs) with a `lib` 4 | crate containing the ZKP program definition, and two binary crates `prover` and 5 | `verifier`. 6 | 7 | When the prover and verifier live on different machines, they'll probably 8 | communicate by sending the serialized proof over a network call. However, we can 9 | demonstrate a similar scenario by having them communicate as separate processes, 10 | piping a serialized proof from `prover` stdout to `verifier` stdin. 11 | 12 | In this allowlist zkp, the verifier verifies that the prover has an entry on its 13 | public allowlist, without revealing which entry. The allowlist is hardcoded to 14 | the numbers 100 to 199 inclusive. 15 | 16 | ```shell 17 | cargo run --bin prover -- 101 | cargo run --bin verifier 18 | ``` 19 | 20 | [pkg-docs]: https://rustwiki.org/en/book/ch07-01-packages-and-crates.html 21 | -------------------------------------------------------------------------------- /examples/allowlist_zkp/src/bin/prover.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use anyhow::Result; 4 | use sunscreen::{ 5 | bulletproofs::BulletproofsBackend, types::zkp::BulletproofsField, ZkpProgramFnExt, 6 | }; 7 | 8 | use allowlist_zkp::{allowlist, default_list}; 9 | 10 | fn main() -> Result<()> { 11 | let prog = allowlist.compile::()?; 12 | let runtime = allowlist.runtime::()?; 13 | 14 | let entry: BulletproofsField = get_first_arg()?.unwrap_or(101).into(); 15 | let list: [BulletproofsField; 100] = default_list(); 16 | 17 | let proof = runtime 18 | .proof_builder(&prog) 19 | .private_input(entry) 20 | .public_input(list) 21 | .prove()?; 22 | 23 | bincode::serialize_into(io::stdout(), &proof)?; 24 | Ok(()) 25 | } 26 | 27 | fn get_first_arg() -> Result> { 28 | let arg = std::env::args().nth(1).map(|s| s.parse()).transpose()?; 29 | Ok(arg) 30 | } 31 | -------------------------------------------------------------------------------- /examples/allowlist_zkp/src/bin/verifier.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use anyhow::Result; 4 | use sunscreen::{ 5 | bulletproofs::BulletproofsBackend, types::zkp::BulletproofsField, Proof, ZkpProgramFnExt, 6 | }; 7 | 8 | use allowlist_zkp::{allowlist, default_list}; 9 | 10 | fn main() -> Result<()> { 11 | let prog = allowlist.compile::()?; 12 | let runtime = allowlist.runtime::()?; 13 | 14 | let proof: Proof = bincode::deserialize_from(io::stdin())?; 15 | 16 | let list: [BulletproofsField; 100] = default_list(); 17 | 18 | runtime 19 | .verification_builder(&prog) 20 | .proof(&proof) 21 | .public_input(list) 22 | .verify()?; 23 | 24 | println!("Verified proof successfully!"); 25 | Ok(()) 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn main_works() -> Result<()> { 34 | let prog = allowlist.compile::()?; 35 | let runtime = allowlist.runtime::()?; 36 | 37 | let entry: BulletproofsField = 101.into(); 38 | let list: [BulletproofsField; 100] = default_list(); 39 | 40 | let proof = runtime 41 | .proof_builder(&prog) 42 | .private_input(entry) 43 | .public_input(list) 44 | .prove()?; 45 | 46 | runtime 47 | .verification_builder(&prog) 48 | .proof(&proof) 49 | .public_input(list) 50 | .verify()?; 51 | 52 | Ok(()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/allowlist_zkp/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::array; 2 | 3 | use sunscreen::{ 4 | types::zkp::{Field, FieldSpec}, 5 | zkp_program, zkp_var, 6 | }; 7 | 8 | /// A ZKP proving a private entry is equal to one of the values in a list. 9 | #[zkp_program] 10 | pub fn allowlist(entry: Field, #[public] list: [Field; 100]) { 11 | let zero = zkp_var!(0); 12 | let one = zkp_var!(1); 13 | let mut poly = one; 14 | for x in list { 15 | poly = poly * (x - entry); 16 | } 17 | poly.constrain_eq(zero); 18 | } 19 | 20 | /// A default list for the prover and verifier to use: [100, 199] 21 | pub fn default_list() -> [Field; 100] { 22 | array::from_fn(|i| Field::from(100 + i as u32)) 23 | } 24 | -------------------------------------------------------------------------------- /examples/amm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "amm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } 10 | -------------------------------------------------------------------------------- /examples/bigint/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bigint" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } 10 | crypto-bigint = { workspace = true } 11 | -------------------------------------------------------------------------------- /examples/calculator_fractional/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calculator_fractional" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } 10 | -------------------------------------------------------------------------------- /examples/calculator_rational/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calculator_rational" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } 10 | -------------------------------------------------------------------------------- /examples/chi_sq/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chi_sq" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } 10 | env_logger = { workspace = true } -------------------------------------------------------------------------------- /examples/dot_prod/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dot_prod" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } -------------------------------------------------------------------------------- /examples/mean_variance/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mean_variance" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } 10 | bincode = { workspace = true } -------------------------------------------------------------------------------- /examples/ordering_zkp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ordering_zkp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | sunscreen = { workspace = true } 8 | -------------------------------------------------------------------------------- /examples/ordering_zkp/src/main.rs: -------------------------------------------------------------------------------- 1 | use sunscreen::{ 2 | bulletproofs::BulletproofsBackend, 3 | types::zkp::{BulletproofsField, ConstrainCmp, Field, FieldSpec}, 4 | zkp_program, Compiler, Error, ZkpRuntime, 5 | }; 6 | 7 | #[zkp_program] 8 | fn greater_than(a: Field, #[public] b: Field) { 9 | a.constrain_gt_bounded(b, 32) 10 | } 11 | 12 | fn main() -> Result<(), Error> { 13 | let app = Compiler::new() 14 | .zkp_backend::() 15 | .zkp_program(greater_than) 16 | .compile()?; 17 | 18 | let greater_than_zkp = app.get_zkp_program(greater_than).unwrap(); 19 | 20 | let runtime = ZkpRuntime::new(BulletproofsBackend::new())?; 21 | 22 | let amount = BulletproofsField::from(232); 23 | let threshold = BulletproofsField::from(64); 24 | 25 | // Prove that amount > threshold 26 | 27 | let proof = runtime.prove(greater_than_zkp, vec![amount], vec![threshold], vec![])?; 28 | 29 | runtime.verify(greater_than_zkp, &proof, vec![threshold], vec![])?; 30 | 31 | Ok(()) 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use super::*; 37 | 38 | #[test] 39 | fn main_works() -> Result<(), Error> { 40 | main() 41 | } 42 | 43 | fn run_test(amount: BulletproofsField, threshold: BulletproofsField, should_succeed: bool) { 44 | let app = Compiler::new() 45 | .zkp_backend::() 46 | .zkp_program(greater_than) 47 | .compile() 48 | .unwrap(); 49 | let gt_zkp = app.get_zkp_program(greater_than).unwrap(); 50 | let runtime = ZkpRuntime::new(BulletproofsBackend::new()).unwrap(); 51 | let proof = runtime.prove(gt_zkp, vec![amount], vec![threshold], vec![]); 52 | if !should_succeed { 53 | assert!(proof.is_err()); 54 | } else { 55 | assert!(runtime 56 | .verify(gt_zkp, &proof.unwrap(), vec![threshold], vec![]) 57 | .is_ok()) 58 | } 59 | } 60 | 61 | #[test] 62 | fn test_gt() { 63 | run_test(1.into(), 0.into(), true); 64 | run_test(100.into(), 0.into(), true); 65 | run_test(100.into(), 99.into(), true); 66 | run_test(u32::MAX.into(), 0.into(), true); 67 | } 68 | 69 | #[test] 70 | fn test_le() { 71 | run_test(0.into(), 1.into(), false); 72 | } 73 | 74 | #[test] 75 | fn test_eq() { 76 | run_test(1.into(), 1.into(), false); 77 | } 78 | 79 | #[test] 80 | fn test_bounded_failure() { 81 | run_test(u64::MAX.into(), 0.into(), false); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/pir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pir" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } 10 | -------------------------------------------------------------------------------- /examples/polynomial_zkp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "polynomial_zkp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | sunscreen = { workspace = true } 8 | -------------------------------------------------------------------------------- /examples/private_tx_linkedproof/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "private_tx_linkedproof" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | sunscreen = { path = "../../sunscreen", features = ["linkedproofs"] } 8 | env_logger = { workspace = true } 9 | -------------------------------------------------------------------------------- /examples/simple_multiply/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_multiply" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sunscreen = { workspace = true } 10 | -------------------------------------------------------------------------------- /examples/sudoku_zkp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sudoku_zkp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | sunscreen = { workspace = true } 8 | -------------------------------------------------------------------------------- /logproof/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "logproof" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | bitvec = { workspace = true } 10 | crypto-bigint = { workspace = true } 11 | curve25519-dalek = { workspace = true } 12 | log = { workspace = true } 13 | merlin = { workspace = true } 14 | sha3 = { workspace = true } 15 | digest = { workspace = true } 16 | rand = { workspace = true } 17 | rayon = { workspace = true } 18 | serde = { workspace = true } 19 | sunscreen_math = { workspace = true } 20 | seal_fhe = { workspace = true } 21 | thiserror = { workspace = true } 22 | 23 | [dev-dependencies] 24 | bincode = { workspace = true } 25 | criterion = { workspace = true } 26 | once_cell = { workspace = true } 27 | seal_fhe = { workspace = true, features = ["insecure-params"] } 28 | 29 | [features] 30 | default = [] 31 | cuda = ["sunscreen_math/cuda"] 32 | opencl = ["sunscreen_math/opencl"] 33 | metal = ["sunscreen_math/metal"] 34 | pina = ["sunscreen_math/pina"] 35 | 36 | [[bench]] 37 | name = "linear_relation" 38 | harness = false 39 | -------------------------------------------------------------------------------- /logproof/src/crypto.rs: -------------------------------------------------------------------------------- 1 | use digest::Digest; 2 | use sha3::Sha3_256; 3 | use sunscreen_math::{ 4 | poly::Polynomial, 5 | ring::{ArithmeticBackend, Ring, WrappingSemantics, ZInt, Zq}, 6 | ToBytes, 7 | }; 8 | 9 | /** 10 | * A trait that allows you to get a collision-resistant hash of an object. 11 | */ 12 | pub trait CryptoHash { 13 | /** 14 | * Compute a SHA-3 hash of this object. 15 | */ 16 | fn crypto_hash(&self, hasher: &mut Sha3_256); 17 | } 18 | 19 | impl CryptoHash for Zq 20 | where 21 | B: ArithmeticBackend, 22 | { 23 | fn crypto_hash(&self, hasher: &mut Sha3_256) { 24 | // We can just leave the value in Montgomery form. 25 | for i in self.val.as_words() { 26 | hasher.update(i.to_be_bytes()); 27 | } 28 | } 29 | } 30 | 31 | impl CryptoHash for ZInt 32 | where 33 | T: WrappingSemantics, 34 | { 35 | fn crypto_hash(&self, hasher: &mut Sha3_256) { 36 | hasher.update(self.to_be_bytes()); 37 | } 38 | } 39 | 40 | impl CryptoHash for Polynomial 41 | where 42 | R: Ring + CryptoHash, 43 | { 44 | fn crypto_hash(&self, hasher: &mut Sha3_256) { 45 | for c in &self.coeffs { 46 | c.crypto_hash(hasher); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /logproof/src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] 2 | /** 3 | * An error that occurred when verifying a proof. 4 | */ 5 | pub enum ProofError { 6 | /** 7 | * Failed to verify a proof. 8 | */ 9 | #[error("Failed to verify proof")] 10 | VerificationError, 11 | 12 | /** 13 | * The proof is malformed. 14 | */ 15 | #[error("The proof is malformed")] 16 | MalformedProof, 17 | } 18 | -------------------------------------------------------------------------------- /logproof/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | 3 | //! This crate contains proofs for demonstrating facts about lattice 4 | //! relations in zero knowledge. It contains 2 proofs: 5 | //! * [InnerProductProof], which is basically the Bulletproofs inner product 6 | //! proof modified to be zero-knowledge. 7 | //! * [LogProof], which demonstrates knowledge of a solution to a lattice 8 | //! relation `As=t`, where `A`, `s`, `t` are in `Z_q[X] / f(X)`. 9 | //! 10 | //! # Remarks 11 | //! These proofs come from "Short Discreet Log Proofs for FHE and Ring-LWE 12 | //! Ciphertexts" by Pino, Lyubashevsky, and Seiler. 13 | //! 14 | 15 | mod assertions; 16 | 17 | /** 18 | * Contains traits relating to cryptographic operations. 19 | */ 20 | pub mod crypto; 21 | mod error; 22 | pub use error::ProofError; 23 | 24 | mod generators; 25 | pub use generators::*; 26 | 27 | /** 28 | * Contains a zero-knowledge inner-product proof 29 | */ 30 | mod inner_product; 31 | pub use inner_product::{ 32 | InnerProductProof, ProverKnowledge as InnerProductProverKnowledge, 33 | VerifierKnowledge as InnerProductVerifierKnowledge, 34 | }; 35 | 36 | /** 37 | * Contains linear algebra related types and traits (e.g. [`linear_algebra::Matrix`]). 38 | */ 39 | pub mod linear_algebra; 40 | 41 | /** 42 | * Contains a zero-knowlege proof of a linear relation `As=t` where `A` and `t` are 43 | * public and `s` is known only to the prover. 44 | * 45 | * # Remarks 46 | * This proof is an implementation of "Short Discrete Log Proofs for FHE" 47 | * by Pino, Lyubashevsky, and Seiler. 48 | */ 49 | mod linear_relation; 50 | pub use linear_relation::{ 51 | Bounds, LogProof, ProverKnowledge as LogProofProverKnowledge, 52 | VerifierKnowledge as LogProofVerifierKnowledge, 53 | }; 54 | 55 | pub mod bfv_statement; 56 | 57 | /** 58 | * A collection of fields Z_q you can use in our log proofs. 59 | */ 60 | pub mod rings; 61 | 62 | /** 63 | * A collection of operations on algebraic structures. 64 | */ 65 | pub mod math; 66 | mod transcript; 67 | 68 | pub use merlin::Transcript; 69 | pub use transcript::LogProofTranscript; 70 | 71 | /** 72 | * Components that are helpful for testing but should not be used in production. 73 | */ 74 | #[doc(hidden)] 75 | pub mod test; 76 | pub use test::LatticeProblem; 77 | -------------------------------------------------------------------------------- /logproof/tests/seal.rs: -------------------------------------------------------------------------------- 1 | use merlin::Transcript; 2 | 3 | use logproof::{ 4 | rings::{SealQ128_1024, SealQ128_2048, SealQ128_4096, SealQ128_8192}, 5 | test::seal_bfv_encryption_linear_relation, 6 | InnerProductVerifierKnowledge, LogProof, LogProofGenerators, LogProofTranscript, 7 | }; 8 | use sunscreen_math::ring::BarrettConfig; 9 | 10 | fn zero_knowledge_proof(message: u64, degree: u64, plain_modulus: u64) 11 | where 12 | B: BarrettConfig, 13 | { 14 | let pk = seal_bfv_encryption_linear_relation::(message, degree, plain_modulus); 15 | 16 | let mut transcript = Transcript::new(b"test"); 17 | let mut verify_transcript = transcript.clone(); 18 | 19 | let gens = LogProofGenerators::new(pk.vk.l() as usize); 20 | let u = InnerProductVerifierKnowledge::get_u(); 21 | 22 | let proof = LogProof::create(&mut transcript, &pk, &gens.g, &gens.h, &u); 23 | 24 | proof 25 | .verify(&mut verify_transcript, &pk.vk, &gens.g, &gens.h, &u) 26 | .unwrap(); 27 | 28 | let l = transcript.challenge_scalar(b"verify"); 29 | let r = verify_transcript.challenge_scalar(b"verify"); 30 | 31 | assert_eq!(l, r); 32 | } 33 | 34 | // This will run the full knowledge proof (which is a trivial amount of time 35 | // in comparison to the zero knowledge proof) before running the zero 36 | // knowledge proof. 37 | #[test] 38 | fn zero_knowledge_bfv_proof_1024() { 39 | zero_knowledge_proof::(12, 1024, 12289); 40 | } 41 | 42 | #[test] 43 | fn full_knowledge_bfv_proof_2048() { 44 | seal_bfv_encryption_linear_relation::(12, 2048, 1032193); 45 | } 46 | 47 | #[test] 48 | fn full_knowledge_bfv_proof_4096() { 49 | seal_bfv_encryption_linear_relation::(12, 4096, 1032193); 50 | } 51 | 52 | #[test] 53 | fn full_knowledge_bfv_proof_8192() { 54 | seal_bfv_encryption_linear_relation::(12, 8192, 1032193); 55 | } 56 | -------------------------------------------------------------------------------- /logproof/utils/compute_params.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def compute_params(n, m, B, k, d, q): 4 | b = int(math.ceil(math.log2(B))) + 1 5 | b_1 = int(math.ceil(math.log2(m * d * B + d))) 6 | b_2 = int(math.ceil(math.log2(q))) 7 | 8 | print(f"b={b} b_1={b_1} b_2={b_2}") 9 | 10 | l = m * k * d * b + n * k * (2 * d - 1) * b_1 + n * k * (d - 1) * b_2 11 | 12 | return l 13 | 14 | # Given a SDLP problem instance, compute l 15 | n = 2 16 | m = 4 17 | B = 2 ** 18 18 | k = 1 19 | 20 | # SEAL q when d = 8192 21 | d = 8192 22 | q = 421249101157150430150591791601812858371395928330411389778873040897 23 | 24 | print(f"SEAL params d={d} q={q} l={compute_params(n, m, B, k, d, q)}") 25 | 26 | # SEAL when using d = 4096, but same q as 8192. This configuration 27 | # is wildly insecure, but convenient for testing SDLP. 28 | d = 4096 29 | q = 421249101157150430150591791601812858371395928330411389778873040897 30 | 31 | print(f"SEAL params d={d} q={q} l={compute_params(n, m, B, k, d, q)}") 32 | 33 | # SEAL q when d = 4096 34 | d = 4096 35 | q = 649033470896967801447398927572993 36 | 37 | print(f"SEAL params d={d} q={q} l={compute_params(n, m, B, k, d, q)}") 38 | 39 | # SEAL q when d = 4096 40 | d = 2048 41 | q = 18014398492704769 42 | 43 | print(f"SEAL params d={d} q={q} l={compute_params(n, m, B, k, d, q)}") 44 | 45 | # SEAL q when d = 4096 46 | d = 1024 47 | q = 132120577 48 | 49 | print(f"SEAL params d={d} q={q} l={compute_params(n, m, B, k, d, q)}") -------------------------------------------------------------------------------- /purge-github-caches.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | gh auth status 4 | 5 | if [ $? -ne 0 ]; then 6 | gh auth login 7 | fi 8 | 9 | actions=$(gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/sunscreen-tech/sunscreen/actions/caches | jq '.actions_caches | .[] | .id') 10 | 11 | for i in $actions; do 12 | echo "Deleting cache id " $i 13 | gh api --method DELETE -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/sunscreen-tech/sunscreen/actions/caches/$i 14 | done 15 | 16 | -------------------------------------------------------------------------------- /seal_fhe/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "seal_fhe" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen LLC"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate contains Rust bindings for Microsoft's SEAL Fully Homomorphic Encryption (FHE) library." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | 18 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 19 | 20 | [dependencies] 21 | serde = { workspace = true } 22 | thiserror = { workspace = true } 23 | static_assertions = { workspace = true } 24 | 25 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 26 | link-cplusplus = { workspace = true } 27 | 28 | [build-dependencies] 29 | cmake = { workspace = true } 30 | bindgen = { workspace = true } 31 | emsdk = { workspace = true } 32 | 33 | [dev-dependencies] 34 | serde_json = { workspace = true } 35 | 36 | [features] 37 | hexl = [] 38 | transparent-ciphertexts = [] 39 | deterministic = [] 40 | insecure-params = [] 41 | -------------------------------------------------------------------------------- /seal_fhe/bindgen_wrapper.h: -------------------------------------------------------------------------------- 1 | #include "seal/c/batchencoder.h" 2 | #include "seal/c/ciphertext.h" 3 | #include "seal/c/ckksencoder.h" 4 | #include "seal/c/contextdata.h" 5 | #include "seal/c/decryptor.h" 6 | #include "seal/c/defines.h" 7 | #include "seal/c/encryptionparameterqualifiers.h" 8 | #include "seal/c/encryptionparameters.h" 9 | #include "seal/c/encryptor.h" 10 | #include "seal/c/evaluator.h" 11 | #include "seal/c/galoiskeys.h" 12 | #include "seal/c/keygenerator.h" 13 | #include "seal/c/kswitchkeys.h" 14 | #include "seal/c/memorymanager.h" 15 | #include "seal/c/memorypoolhandle.h" 16 | #include "seal/c/modulus.h" 17 | #include "seal/c/plaintext.h" 18 | #include "seal/c/polyarray.h" 19 | #include "seal/c/publickey.h" 20 | #include "seal/c/relinkeys.h" 21 | #include "seal/c/sealcontext.h" 22 | #include "seal/c/secretkey.h" 23 | #include "seal/c/serialization.h" 24 | #include "seal/c/stdafx.h" 25 | //#include "seal/c/utilities.h" 26 | #include "seal/c/valcheck.h" 27 | #include "seal/c/rns.h" 28 | -------------------------------------------------------------------------------- /seal_fhe/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate provides Rust bindings for Microsoft SEAL. -------------------------------------------------------------------------------- /seal_fhe/tests/data/public_key.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sunscreen-tech/Sunscreen/d7f58b39c145d530ca9b5abdf6172bd292523696/seal_fhe/tests/data/public_key.bin -------------------------------------------------------------------------------- /seal_fhe/tests/data/secret_key.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sunscreen-tech/Sunscreen/d7f58b39c145d530ca9b5abdf6172bd292523696/seal_fhe/tests/data/secret_key.bin -------------------------------------------------------------------------------- /seal_fhe/tests/test_common.rs: -------------------------------------------------------------------------------- 1 | use seal_fhe::*; 2 | 3 | pub fn run_bfv_test(lane_bits: u32, degree: u64, test: F) 4 | where 5 | F: FnOnce(Decryptor, BFVEncoder, Encryptor, BFVEvaluator, KeyGenerator), 6 | { 7 | let params = BfvEncryptionParametersBuilder::new() 8 | .set_poly_modulus_degree(degree) 9 | .set_coefficient_modulus( 10 | CoefficientModulus::bfv_default(degree, SecurityLevel::TC128).unwrap(), 11 | ) 12 | .set_plain_modulus(PlainModulus::batching(degree, lane_bits).unwrap()) 13 | .build() 14 | .unwrap(); 15 | 16 | let ctx = Context::new(¶ms, false, SecurityLevel::TC128).unwrap(); 17 | let gen = KeyGenerator::new(&ctx).unwrap(); 18 | 19 | let encoder = BFVEncoder::new(&ctx).unwrap(); 20 | 21 | let public_key = gen.create_public_key(); 22 | let private_key = gen.secret_key(); 23 | 24 | let encryptor = Encryptor::with_public_and_secret_key(&ctx, &public_key, &private_key).unwrap(); 25 | let decryptor = Decryptor::new(&ctx, &private_key).unwrap(); 26 | let evaluator = BFVEvaluator::new(&ctx).unwrap(); 27 | 28 | test(decryptor, encoder, encryptor, evaluator, gen); 29 | } 30 | -------------------------------------------------------------------------------- /sunscreen/crates-io.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | Sunscreen is an ecosystem for building privacy-preserving applications using fully homomorphic encryption. 4 | 5 | This project is licensed under the terms of the GNU AGPLv3 license. If you require a different license for your application, please reach out to us. 6 | 7 | *WARNING!* This library is meant for experiments only. It has not been externally audited and is *not* intended for use in production. 8 | 9 | # Example 10 | Below, we look at how to multiply two encrypted integers together. 11 | 12 | ```rust 13 | use sunscreen::{ 14 | fhe_program, 15 | types::{bfv::Signed, Cipher}, 16 | Compiler, Error, FheRuntime, 17 | }; 18 | 19 | #[fhe_program(scheme = "bfv")] 20 | fn simple_multiply(a: Cipher, b: Cipher) -> Cipher { 21 | a * b 22 | } 23 | 24 | fn main() -> Result<(), Error> { 25 | let app = Compiler::new() 26 | .fhe_program(simple_multiply) 27 | .compile()?; 28 | 29 | let runtime = FheRuntime::new(app.params())?; 30 | 31 | let (public_key, private_key) = runtime.generate_keys()?; 32 | 33 | let a = runtime.encrypt(Signed::from(15), &public_key)?; 34 | let b = runtime.encrypt(Signed::from(5), &public_key)?; 35 | 36 | let results = runtime.run(app.get_fhe_program(simple_multiply).unwrap(), vec![a, b], &public_key)?; 37 | 38 | let c: Signed = runtime.decrypt(&results[0], &private_key)?; 39 | 40 | assert_eq!(c, 75.into()); 41 | 42 | Ok(()) 43 | } 44 | ``` 45 | 46 | # Docs 47 | * [User guide](https://bfv-docs.sunscreen.tech/) 48 | * [API docs](https://docs.rs/sunscreen) 49 | 50 | # Getting help 51 | For questions about Sunscreen, join our [Discord](https://discord.gg/WHCs6jNNDS)! 52 | -------------------------------------------------------------------------------- /sunscreen/docs/assets/katex-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | -------------------------------------------------------------------------------- /sunscreen/src/linked.rs: -------------------------------------------------------------------------------- 1 | //! A linked proof consists of a short discrete log proof (SDLP) and an R1CS bulletproof (BP). It 2 | //! allows you to simultaneously prove an encryption is valid (SDLP) and that the encrypted message 3 | //! has some property (BP). 4 | //! 5 | //! The SDLP proves a linear relation while keeping part of that relation secret. Specifically, the 6 | //! SDLP allows one to prove a matrix relation of the form \\(A \cdot S = T\\), where \\(S\\) is a 7 | //! matrix of secrets (sometimes also called a witness) and \\(T\\) is the result of computing 8 | //! \\(A\\) on that secret. An example relation is the equation for encryption in BFV, which can 9 | //! be used to show that a ciphertext is a valid encryption of some known underlying message. 10 | //! 11 | //! The BP enables proving arbitrary arithmetic circuits, which can be used to prove that a secret 12 | //! satisfies some property. For example, one can prove that a private transaction can occur 13 | //! because the sender has enough funds to cover the transaction, without revealing what the 14 | //! transaction is. 15 | //! 16 | //! Combining these two proofs is powerful because it allows one to prove both that a ciphertext is 17 | //! a valid encryption of some message and that the message satisfies some property. In the prior 18 | //! example of a private transaction, with a linked proof we can now prove that the sender knows 19 | //! the value in an encrypted transaction and that the sender has enough funds to cover the 20 | //! transaction, without decrypting the transaction. 21 | //! 22 | //! How does this work in practice? If you use our [builder](`LinkedProofBuilder`), you 23 | //! can encrypt messages in a very similar way to our typical [runtime 24 | //! encryption](crate::FheRuntime::encrypt), while also opting to _share_ a message with a linked 25 | //! ZKP program. Under the hood, we'll handle the complicated bits of generating a linear relation 26 | //! for SDLP and sharing the secrets with the [`zkp_program`](crate::zkp_program). 27 | pub use logproof::Bounds; 28 | pub use sunscreen_runtime::{ 29 | ExistingMessage, LinkWithZkp, LinkedMessage, LinkedProof, LinkedProofBuilder, 30 | LinkedProofVerificationBuilder, Message, MessageRef, Sdlp, SdlpBuilder, 31 | SdlpVerificationBuilder, 32 | }; 33 | -------------------------------------------------------------------------------- /sunscreen/src/types/bfv/mod.rs: -------------------------------------------------------------------------------- 1 | mod batched; 2 | mod fractional; 3 | mod rational; 4 | mod signed; 5 | mod unsigned; 6 | 7 | pub use batched::*; 8 | pub use fractional::*; 9 | pub use rational::*; 10 | pub use signed::*; 11 | pub use unsigned::*; 12 | -------------------------------------------------------------------------------- /sunscreen/src/types/intern/fhe_literal.rs: -------------------------------------------------------------------------------- 1 | use crypto_bigint::Uint; 2 | 3 | /** 4 | * Tags types (e.g. u64, f64, etc) so they can be used as literals 5 | * in FHE programs with the GraphCipherConst* traits. 6 | */ 7 | pub trait FheLiteral {} 8 | impl FheLiteral for f64 {} 9 | impl FheLiteral for u64 {} 10 | impl FheLiteral for i64 {} 11 | impl FheLiteral for Uint {} // is this true? 12 | -------------------------------------------------------------------------------- /sunscreen/src/types/intern/mod.rs: -------------------------------------------------------------------------------- 1 | mod fhe_literal; 2 | mod fhe_program_node; 3 | mod input; 4 | mod output; 5 | mod u64_literal; 6 | 7 | pub use fhe_literal::*; 8 | pub use fhe_program_node::*; 9 | pub use input::*; 10 | pub use output::*; 11 | pub use u64_literal::*; 12 | -------------------------------------------------------------------------------- /sunscreen/src/types/intern/output.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fhe::{with_fhe_ctx, FheContextOps}, 3 | types::{intern::FheProgramNode, NumCiphertexts}, 4 | }; 5 | 6 | /** 7 | * Captures an output for an FHE program. 8 | */ 9 | pub trait Output { 10 | /** 11 | * The captured object return type. 12 | */ 13 | type Output; 14 | 15 | /** 16 | * Denote this object is an output by appending an appropriate output FHE program nodes 17 | * 18 | * You should not call this, but rather allow the [`fhe_program`](crate::fhe_program) macro to do this on your behalf. 19 | * 20 | * # Undefined behavior 21 | * This type references memory in a backing 22 | * [`FheContext`](crate::fhe::FheContext) and without carefully 23 | * ensuring FheProgramNodes never outlive the backing context, 24 | * use-after-free can occur. 25 | */ 26 | fn output(&self) -> Self::Output; 27 | } 28 | 29 | impl Output for FheProgramNode 30 | where 31 | T: NumCiphertexts, 32 | { 33 | type Output = FheProgramNode; 34 | 35 | fn output(&self) -> Self::Output { 36 | let mut ids = Vec::with_capacity(self.ids.len()); 37 | 38 | for i in 0..self.ids.len() { 39 | ids.push(with_fhe_ctx(|ctx| ctx.add_output(self.ids[i]))); 40 | } 41 | 42 | FheProgramNode::new(&ids) 43 | } 44 | } 45 | 46 | impl Output for [T; N] 47 | where 48 | T: Output + NumCiphertexts + Copy, 49 | { 50 | type Output = [T::Output; N]; 51 | 52 | fn output(&self) -> Self::Output { 53 | self.map(|i| i.output()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sunscreen/src/types/intern/u64_literal.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fhe::{with_fhe_ctx, FheContextOps}, 3 | Literal, 4 | }; 5 | use petgraph::stable_graph::NodeIndex; 6 | 7 | #[derive(Clone, Copy)] 8 | /** 9 | * A reference to a u64 literal in an Fhe Program graph. 10 | */ 11 | pub struct U64LiteralRef {} 12 | 13 | impl U64LiteralRef { 14 | /** 15 | * Creates a reference to the given literal. If the given literal already exists in the current 16 | * graph, a reference to the existing literal is returned. 17 | */ 18 | pub fn node(val: u64) -> NodeIndex { 19 | with_fhe_ctx(|ctx| ctx.add_literal(Literal::U64(val))) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sunscreen/src/types/ops/add.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{ 2 | intern::{FheLiteral, FheProgramNode}, 3 | Cipher, FheType, 4 | }; 5 | 6 | /** 7 | * Called when an Fhe Program encounters a + operation on two encrypted 8 | * types. 9 | * 10 | * This trait is an implementation detail of FHE program compilation; 11 | * you should not directly call methods on this trait. 12 | */ 13 | pub trait GraphCipherAdd { 14 | /** 15 | * The type of the left operand 16 | */ 17 | type Left: FheType; 18 | 19 | /** 20 | * The type of the right operand 21 | */ 22 | type Right: FheType; 23 | 24 | /** 25 | * Process the + operation 26 | */ 27 | fn graph_cipher_add( 28 | a: FheProgramNode>, 29 | b: FheProgramNode>, 30 | ) -> FheProgramNode>; 31 | } 32 | 33 | /** 34 | * Called when an Fhe Program encounters a + operation on one encrypted 35 | * and one unencrypted type. 36 | * 37 | * This trait is an implementation detail of FHE program compilation; 38 | * you should not directly call methods on this trait. 39 | */ 40 | pub trait GraphCipherPlainAdd { 41 | /** 42 | * The type of the left operand 43 | */ 44 | type Left: FheType; 45 | 46 | /** 47 | * The type of the right operand 48 | */ 49 | type Right: FheType; 50 | 51 | /** 52 | * Process the + operation 53 | */ 54 | fn graph_cipher_plain_add( 55 | a: FheProgramNode>, 56 | b: FheProgramNode, 57 | ) -> FheProgramNode>; 58 | } 59 | 60 | /** 61 | * Called when an Fhe Program encounters a + operation on one encrypted 62 | * and a literal. 63 | * 64 | * This trait is an implementation detail of FHE program compilation; 65 | * you should not directly call methods on this trait. 66 | */ 67 | pub trait GraphCipherConstAdd { 68 | /** 69 | * The type of the left operand 70 | */ 71 | type Left: FheType + TryFrom; 72 | 73 | /** 74 | * The type of the right operand 75 | */ 76 | type Right: FheLiteral; 77 | 78 | /** 79 | * Process the + operation 80 | */ 81 | fn graph_cipher_const_add( 82 | a: FheProgramNode>, 83 | b: Self::Right, 84 | ) -> FheProgramNode>; 85 | } 86 | -------------------------------------------------------------------------------- /sunscreen/src/types/ops/insert.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{ 2 | intern::{FheLiteral, FheProgramNode}, 3 | FheType, 4 | }; 5 | 6 | /** 7 | * Called when an Fhe Program encounters a literal type and inserts it as plaintext node. 8 | * 9 | * This trait is an implementation detail of FHE program compilation; 10 | * you should not directly call methods on this trait. 11 | */ 12 | pub trait GraphCipherInsert { 13 | /** 14 | * The type of the literal 15 | */ 16 | type Lit: FheLiteral; 17 | 18 | /** 19 | * The type of the plaintext encoding 20 | */ 21 | type Val: FheType; 22 | 23 | /** 24 | * Process the insertion 25 | */ 26 | fn graph_cipher_insert(lit: Self::Lit) -> FheProgramNode; 27 | } 28 | -------------------------------------------------------------------------------- /sunscreen/src/types/ops/mod.rs: -------------------------------------------------------------------------------- 1 | mod add; 2 | mod div; 3 | mod insert; 4 | mod mul; 5 | mod neg; 6 | mod rotate; 7 | mod sub; 8 | 9 | pub use add::*; 10 | pub use div::*; 11 | pub use insert::*; 12 | pub use mul::*; 13 | pub use neg::*; 14 | pub use rotate::*; 15 | pub use sub::*; 16 | -------------------------------------------------------------------------------- /sunscreen/src/types/ops/mul.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{ 2 | intern::{FheLiteral, FheProgramNode}, 3 | Cipher, FheType, 4 | }; 5 | 6 | /** 7 | * Called when an Fhe Program encounters a * operation on two encrypted types. 8 | * 9 | * This trait is an implementation detail of FHE program compilation; 10 | * you should not directly call methods on this trait. 11 | */ 12 | pub trait GraphCipherMul { 13 | /** 14 | * The type of the left operand 15 | */ 16 | type Left: FheType; 17 | 18 | /** 19 | * The type of the right operand 20 | */ 21 | type Right: FheType; 22 | 23 | /** 24 | * Process the * operation 25 | */ 26 | fn graph_cipher_mul( 27 | a: FheProgramNode>, 28 | b: FheProgramNode>, 29 | ) -> FheProgramNode>; 30 | } 31 | 32 | /** 33 | * Called when an Fhe Program encounters a * operation on an encrypted 34 | * and plaintext data type. 35 | * 36 | * This trait is an implementation detail of FHE program compilation; 37 | * you should not directly call methods on this trait. 38 | */ 39 | pub trait GraphCipherPlainMul { 40 | /** 41 | * The type of the left operand 42 | */ 43 | type Left: FheType; 44 | 45 | /** 46 | * The type of the right operand 47 | */ 48 | type Right: FheType; 49 | 50 | /** 51 | * Process the * operation 52 | */ 53 | fn graph_cipher_plain_mul( 54 | a: FheProgramNode>, 55 | b: FheProgramNode, 56 | ) -> FheProgramNode>; 57 | } 58 | 59 | /** 60 | * Called when an Fhe Program encounters a + operation on one encrypted 61 | * and a literal. 62 | * 63 | * This trait is an implementation detail of FHE program compilation; 64 | * you should not directly call methods on this trait. 65 | */ 66 | pub trait GraphCipherConstMul { 67 | /** 68 | * The type of the left operand 69 | */ 70 | type Left: FheType + TryFrom; 71 | 72 | /** 73 | * The type of the right operand 74 | */ 75 | type Right: FheLiteral; 76 | 77 | /** 78 | * Process the + operation 79 | */ 80 | fn graph_cipher_const_mul( 81 | a: FheProgramNode>, 82 | b: Self::Right, 83 | ) -> FheProgramNode>; 84 | } 85 | -------------------------------------------------------------------------------- /sunscreen/src/types/ops/neg.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{ 2 | intern::{FheProgramNode, FheType}, 3 | Cipher, 4 | }; 5 | 6 | /** 7 | * Called when the user performs unary negation (-) on a ciphertext. 8 | * 9 | * This trait is an implementation detail of FHE program compilation; 10 | * you should not directly call methods on this trait. 11 | */ 12 | pub trait GraphCipherNeg { 13 | /** 14 | * The unary type. 15 | */ 16 | type Val: FheType; 17 | 18 | /** 19 | * Negates the given ciphertext (e.g. -x). 20 | */ 21 | fn graph_cipher_neg(a: FheProgramNode>) -> FheProgramNode>; 22 | } 23 | -------------------------------------------------------------------------------- /sunscreen/src/types/ops/rotate.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{intern::FheProgramNode, Cipher, FheType}; 2 | 3 | /** 4 | * Swaps the rows of the given ciphertext. 5 | * 6 | * This trait is an implementation detail of FHE program compilation; 7 | * you should not directly call methods on this trait. 8 | */ 9 | pub trait GraphCipherSwapRows 10 | where 11 | Self: FheType, 12 | { 13 | /** 14 | * Swap the rows in the given ciphertext. 15 | */ 16 | fn graph_cipher_swap_rows(x: FheProgramNode>) -> FheProgramNode>; 17 | } 18 | 19 | pub trait GraphCipherRotateLeft 20 | where 21 | Self: FheType, 22 | { 23 | fn graph_cipher_rotate_left( 24 | x: FheProgramNode>, 25 | amount: u64, 26 | ) -> FheProgramNode>; 27 | } 28 | 29 | pub trait GraphCipherRotateRight 30 | where 31 | Self: FheType, 32 | { 33 | fn graph_cipher_rotate_right( 34 | x: FheProgramNode>, 35 | amount: u64, 36 | ) -> FheProgramNode>; 37 | } 38 | -------------------------------------------------------------------------------- /sunscreen/src/types/zkp/gadgets/mod.rs: -------------------------------------------------------------------------------- 1 | mod arithmetic; 2 | mod binary; 3 | 4 | pub use arithmetic::*; 5 | pub use binary::*; 6 | -------------------------------------------------------------------------------- /sunscreen/tests/chaining.rs: -------------------------------------------------------------------------------- 1 | use sunscreen::{ 2 | types::{bfv::Signed, Cipher}, 3 | *, 4 | }; 5 | 6 | #[test] 7 | fn chain_count_defaults_to_1() { 8 | #[fhe_program(scheme = "bfv")] 9 | fn my_program() {} 10 | 11 | assert_eq!(my_program.chain_count, 1); 12 | } 13 | 14 | #[test] 15 | fn chain_count_is_overridable() { 16 | #[fhe_program(scheme = "bfv", chain_count = 42)] 17 | fn my_program() {} 18 | 19 | assert_eq!(my_program.chain_count, 42); 20 | } 21 | 22 | #[test] 23 | fn cant_chain_multiple_program() { 24 | #[fhe_program(scheme = "bfv", chain_count = 42)] 25 | fn program_1() {} 26 | 27 | #[fhe_program(scheme = "bfv")] 28 | fn program_2() {} 29 | 30 | let result = Compiler::new() 31 | .fhe_program(program_1) 32 | .fhe_program(program_2) 33 | .compile(); 34 | 35 | match result { 36 | Err(Error::Unsupported(_)) => {} 37 | _ => panic!("Expected compilation to fail with UnsupportedOperation."), 38 | }; 39 | } 40 | 41 | #[test] 42 | fn chaining_increases_parameters() { 43 | #[fhe_program(scheme = "bfv")] 44 | fn mul_1(a: Cipher, b: Cipher) -> Cipher { 45 | a * b 46 | } 47 | 48 | let app = Compiler::new() 49 | .fhe_program(mul_1) 50 | .plain_modulus_constraint(PlainModulusConstraint::Raw(256)) 51 | .compile() 52 | .unwrap(); 53 | 54 | assert_eq!(app.params().lattice_dimension, 4096); 55 | 56 | #[fhe_program(scheme = "bfv", chain_count = 3)] 57 | fn mul_2(a: Cipher, b: Cipher) -> Cipher { 58 | a * b 59 | } 60 | 61 | let app = Compiler::new() 62 | .fhe_program(mul_2) 63 | .plain_modulus_constraint(PlainModulusConstraint::Raw(256)) 64 | .compile() 65 | .unwrap(); 66 | 67 | assert_eq!(app.params().lattice_dimension, 8192); 68 | } 69 | -------------------------------------------------------------------------------- /sunscreen/tests/encrypt_decrypt.rs: -------------------------------------------------------------------------------- 1 | use sunscreen::{types::bfv::Signed, types::Cipher, *}; 2 | 3 | #[test] 4 | fn can_encrypt_decrypt() { 5 | #[fhe_program(scheme = "bfv")] 6 | fn foo(a: Cipher, b: Cipher) -> Cipher { 7 | a + b 8 | } 9 | 10 | let app = Compiler::new() 11 | .fhe_program(foo) 12 | .additional_noise_budget(5) 13 | .plain_modulus_constraint(PlainModulusConstraint::Raw(500)) 14 | .compile() 15 | .unwrap(); 16 | 17 | let runtime = Runtime::new_fhe(app.params()).unwrap(); 18 | 19 | let (public_key, private_key) = runtime.generate_keys().unwrap(); 20 | 21 | let a = runtime.encrypt(Signed::from(15), &public_key).unwrap(); 22 | let b = runtime.encrypt(Signed::from(5), &public_key).unwrap(); 23 | 24 | let result = runtime 25 | .run(app.get_fhe_program(foo).unwrap(), vec![a, b], &public_key) 26 | .unwrap(); 27 | 28 | let c: Signed = runtime.decrypt(&result[0], &private_key).unwrap(); 29 | 30 | assert_eq!(c, 20.into()); 31 | } 32 | -------------------------------------------------------------------------------- /sunscreen/tests/features.rs: -------------------------------------------------------------------------------- 1 | use sunscreen::{ 2 | types::{bfv::Signed, Cipher}, 3 | Compiler, 4 | }; 5 | use sunscreen_compiler_macros::fhe_program; 6 | use sunscreen_runtime::{FheProgramInput, Runtime}; 7 | 8 | #[cfg(not(feature = "transparent-ciphertexts"))] 9 | mod transparent_ciphertexts { 10 | use super::*; 11 | 12 | #[test] 13 | #[should_panic] 14 | fn panics_on_transparent_ciphertext() { 15 | #[fhe_program(scheme = "bfv")] 16 | fn add(a: Cipher, b: Signed) -> Cipher { 17 | a * b 18 | } 19 | 20 | let app = Compiler::new().fhe_program(add).compile().unwrap(); 21 | 22 | let runtime = Runtime::new_fhe(app.params()).unwrap(); 23 | 24 | let (public, _) = runtime.generate_keys().unwrap(); 25 | 26 | let a = runtime.encrypt(Signed::from(42), &public).unwrap(); 27 | 28 | let args: Vec = vec![a.into(), Signed::from(0).into()]; 29 | 30 | runtime 31 | .run(app.get_fhe_program(add).unwrap(), args, &public) 32 | .unwrap(); 33 | } 34 | } 35 | 36 | #[cfg(feature = "transparent-ciphertexts")] 37 | mod transparent_ciphertexts { 38 | use super::*; 39 | 40 | #[test] 41 | fn no_panic_on_transparent_ciphertext() { 42 | #[fhe_program(scheme = "bfv")] 43 | fn add(a: Cipher, b: Signed) -> Cipher { 44 | a * b 45 | } 46 | 47 | let app = Compiler::new().fhe_program(add).compile().unwrap(); 48 | 49 | let runtime = Runtime::new_fhe(app.params()).unwrap(); 50 | 51 | let (public, _) = runtime.generate_keys().unwrap(); 52 | 53 | let a = runtime.encrypt(Signed::from(42), &public).unwrap(); 54 | 55 | let args: Vec = vec![a.into(), Signed::from(0).into()]; 56 | 57 | runtime 58 | .run(app.get_fhe_program(add).unwrap(), args, &public) 59 | .unwrap(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sunscreen/tests/serialization.rs: -------------------------------------------------------------------------------- 1 | use seal_fhe::{CoefficientModulus, SecurityLevel}; 2 | use sunscreen::types::bfv::Signed; 3 | use sunscreen_fhe_program::SchemeType; 4 | use sunscreen_runtime::{Ciphertext, Params, Runtime}; 5 | 6 | #[test] 7 | fn can_roundtrip_ciphertexts_bincode() { 8 | let runtime = Runtime::new_fhe(&Params { 9 | lattice_dimension: 8192, 10 | plain_modulus: 1024, 11 | coeff_modulus: CoefficientModulus::bfv_default(8192, SecurityLevel::TC128) 12 | .unwrap() 13 | .iter() 14 | .map(|c| c.value()) 15 | .collect(), 16 | security_level: SecurityLevel::TC128, 17 | scheme_type: SchemeType::Bfv, 18 | }) 19 | .unwrap(); 20 | 21 | let (public_key, private_key) = runtime.generate_keys().unwrap(); 22 | 23 | let expected: i64 = 42; 24 | 25 | let c = runtime 26 | .encrypt(Signed::from(expected), &public_key) 27 | .unwrap(); 28 | let c: Ciphertext = bincode::deserialize(&bincode::serialize(&c).unwrap()).unwrap(); 29 | 30 | let v: Signed = runtime.decrypt(&c, &private_key).unwrap(); 31 | 32 | let actual: i64 = v.into(); 33 | assert_eq!(actual, expected); 34 | } 35 | 36 | #[test] 37 | fn can_roundtrip_ciphertexts_json() { 38 | let runtime = Runtime::new_fhe(&Params { 39 | lattice_dimension: 8192, 40 | plain_modulus: 1024, 41 | coeff_modulus: CoefficientModulus::bfv_default(8192, SecurityLevel::TC128) 42 | .unwrap() 43 | .iter() 44 | .map(|c| c.value()) 45 | .collect(), 46 | security_level: SecurityLevel::TC128, 47 | scheme_type: SchemeType::Bfv, 48 | }) 49 | .unwrap(); 50 | 51 | let (public_key, private_key) = runtime.generate_keys().unwrap(); 52 | 53 | let expected: i64 = 42; 54 | 55 | let c = runtime 56 | .encrypt(Signed::from(expected), &public_key) 57 | .unwrap(); 58 | let c: Ciphertext = serde_json::from_str(&serde_json::to_string(&c).unwrap()).unwrap(); 59 | 60 | let v: Signed = runtime.decrypt(&c, &private_key).unwrap(); 61 | 62 | let actual: i64 = v.into(); 63 | assert_eq!(actual, expected); 64 | } 65 | -------------------------------------------------------------------------------- /sunscreen/tests/typename_tests.rs: -------------------------------------------------------------------------------- 1 | use sunscreen::{ 2 | types::{Type, TypeName, TypeNameInstance, Version}, 3 | TypeName as DeriveTypeName, 4 | }; 5 | 6 | #[test] 7 | fn derive_typename_example() { 8 | #[derive(DeriveTypeName)] 9 | struct Foo { 10 | _cow: String, 11 | } 12 | 13 | let test_data = Foo { 14 | _cow: "moo".to_string(), 15 | }; 16 | 17 | let name = format!("{}::{}", module_path!(), "Foo"); 18 | let version = env!("CARGO_PKG_VERSION"); 19 | 20 | let version = Version::parse(version).unwrap(); 21 | 22 | let expected = Type { 23 | name, 24 | version, 25 | is_encrypted: false, 26 | }; 27 | 28 | assert_eq!(Foo::type_name(), expected); 29 | assert_eq!(test_data.type_name_instance(), expected); 30 | } 31 | -------------------------------------------------------------------------------- /sunscreen_backend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_backend" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "The backend for the Sunscreen compiler." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [dependencies] 20 | crossbeam = { workspace = true } 21 | seal_fhe = { workspace = true } 22 | sunscreen_compiler_common = { workspace = true } 23 | sunscreen_fhe_program = { workspace = true } 24 | sunscreen_runtime = { workspace = true } 25 | petgraph = { workspace = true } 26 | log = { workspace = true } 27 | num = { workspace = true } 28 | -------------------------------------------------------------------------------- /sunscreen_backend/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). -------------------------------------------------------------------------------- /sunscreen_backend/src/error.rs: -------------------------------------------------------------------------------- 1 | use seal_fhe::Error as SealError; 2 | use sunscreen_runtime::FheProgramRunFailure as RuntimeError; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq)] 5 | /** 6 | * Represents an error that can occur in this crate. 7 | */ 8 | pub enum Error { 9 | /** 10 | * An [``]() is erroneous. 11 | */ 12 | IRError(sunscreen_fhe_program::Error), 13 | 14 | /** 15 | * The given parameters are invalid. 16 | */ 17 | InvalidParams, 18 | 19 | /** 20 | * An error occurred in SEAL. 21 | */ 22 | SealError(SealError), 23 | 24 | /** 25 | * Failed to generate expected Relinearization or Galois keys. 26 | */ 27 | KeygenFailure, 28 | 29 | /** 30 | * An error occurred when running an FHE program. 31 | */ 32 | RuntimeError(RuntimeError), 33 | 34 | /** 35 | * The given target noise budget is unachievable even with a fresh ciphertext. 36 | */ 37 | ImpossibleNoiseFloor, 38 | 39 | /** 40 | * Attempted to create a ciphertext with 41 | * [`TargetNoiseLevel::NotApplicable`](crate::noise_model::TargetNoiseLevel::NotApplicable). 42 | */ 43 | NotApplicable, 44 | } 45 | 46 | impl From for Error { 47 | fn from(err: sunscreen_fhe_program::Error) -> Self { 48 | Self::IRError(err) 49 | } 50 | } 51 | 52 | impl From for Error { 53 | fn from(err: SealError) -> Self { 54 | Self::SealError(err) 55 | } 56 | } 57 | 58 | impl From for Error { 59 | fn from(err: RuntimeError) -> Self { 60 | Self::RuntimeError(err) 61 | } 62 | } 63 | 64 | /** 65 | * A convenience wrapper around [`std::result::Result`]. 66 | */ 67 | pub type Result = std::result::Result; 68 | -------------------------------------------------------------------------------- /sunscreen_backend/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | #![deny(rustdoc::broken_intra_doc_links)] 3 | 4 | //! This crate contains the backend compiler for sunscreen FHE programs. It includes the 5 | //! following useful operations: 6 | //! * [`compile`] takes either an FHE program from the compiler frontend and applies a set 7 | //! of transformations. 8 | 9 | mod error; 10 | /** 11 | * A module for performing noise estimation on FHE programs. 12 | */ 13 | pub mod noise_model; 14 | mod transforms; 15 | 16 | pub use error::*; 17 | 18 | use sunscreen_fhe_program::FheProgram; 19 | 20 | use transforms::transform_intermediate_representation; 21 | 22 | /** 23 | * Clones the given [`FheProgram`] and compiles it. 24 | */ 25 | pub fn compile(ir: &FheProgram) -> FheProgram { 26 | let mut clone = ir.clone(); 27 | 28 | transform_intermediate_representation(&mut clone); 29 | 30 | clone 31 | } 32 | 33 | /** 34 | * Consumes the given [`FheProgram`] and compiles it. 35 | */ 36 | pub fn compile_inplace(mut ir: FheProgram) -> FheProgram { 37 | transform_intermediate_representation(&mut ir); 38 | 39 | ir 40 | } 41 | -------------------------------------------------------------------------------- /sunscreen_backend/src/transforms/mod.rs: -------------------------------------------------------------------------------- 1 | mod insert_relinearizations; 2 | 3 | use petgraph::stable_graph::NodeIndex; 4 | use sunscreen_fhe_program::{FheProgram, FheProgramTrait}; 5 | 6 | use insert_relinearizations::apply_insert_relinearizations; 7 | 8 | pub fn transform_intermediate_representation(ir: &mut FheProgram) { 9 | apply_insert_relinearizations(ir); 10 | 11 | // Dead code elimination. 12 | *ir = ir.prune(&ir.get_outputs().collect::>()); 13 | } 14 | -------------------------------------------------------------------------------- /sunscreen_compiler_common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_compiler_common" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate contains common functionality for Sunscreen's FHE and ZKP compilers." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | [dependencies] 18 | petgraph = { workspace = true } 19 | proc-macro2 = { workspace = true } 20 | quote = { workspace = true } 21 | semver = { workspace = true } 22 | serde = { workspace = true } 23 | syn = { workspace = true } 24 | thiserror = { workspace = true } 25 | static_assertions = { workspace = true } 26 | -------------------------------------------------------------------------------- /sunscreen_compiler_common/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). -------------------------------------------------------------------------------- /sunscreen_compiler_common/src/macros/type_name.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use quote::quote; 3 | use syn::{DeriveInput, Ident, LitStr}; 4 | 5 | /** 6 | * The implementation for #[derive(TypeName)] 7 | */ 8 | pub fn derive_typename_impl(parse_stream: DeriveInput) -> TokenStream { 9 | let name = &parse_stream.ident; 10 | let name_contents = LitStr::new(&format!("{{}}::{name}"), name.span()); 11 | let crate_name = std::env::var("CARGO_CRATE_NAME").unwrap(); 12 | 13 | // If the sunscreen crate itself tries to derive types, then it needs to refer 14 | // to itself in the first-person as "crate", not in the third-person as "sunscreen" 15 | let sunscreen_path = if crate_name == "sunscreen" { 16 | Ident::new("crate", Span::call_site()) 17 | } else { 18 | Ident::new("sunscreen", Span::call_site()) 19 | }; 20 | 21 | quote! { 22 | impl #sunscreen_path ::types::TypeName for #name { 23 | fn type_name() -> #sunscreen_path ::types::Type { 24 | let version = env!("CARGO_PKG_VERSION"); 25 | 26 | #sunscreen_path ::types::Type { 27 | name: format!(#name_contents, module_path!()), 28 | version: #sunscreen_path ::types::Version ::parse(version).expect("Crate version is not a valid semver"), 29 | is_encrypted: false 30 | } 31 | } 32 | } 33 | 34 | impl #sunscreen_path ::types::TypeNameInstance for #name { 35 | fn type_name_instance(&self) -> #sunscreen_path ::types::Type { 36 | let version = env!("CARGO_PKG_VERSION"); 37 | 38 | #sunscreen_path ::types::Type { 39 | name: format!(#name_contents, module_path!()), 40 | version: #sunscreen_path ::types::Version ::parse(version).expect("Crate version is not a valid semver"), 41 | is_encrypted: false, 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sunscreen_compiler_common/src/transforms/mod.rs: -------------------------------------------------------------------------------- 1 | mod common_subexpression_elimination; 2 | mod graph_transforms; 3 | 4 | pub use common_subexpression_elimination::*; 5 | pub use graph_transforms::*; 6 | -------------------------------------------------------------------------------- /sunscreen_compiler_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_compiler_macros" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate contains macros that support the Sunscreen compiler." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | [lib] 18 | proc-macro = true 19 | 20 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 21 | 22 | [dependencies] 23 | proc-macro2 = { workspace = true } 24 | quote = { workspace = true } 25 | syn = { workspace = true } 26 | sunscreen_compiler_common = { workspace = true } 27 | thiserror = { workspace = true } 28 | 29 | [dev-dependencies] 30 | serde_json = { workspace = true } 31 | -------------------------------------------------------------------------------- /sunscreen_compiler_macros/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). -------------------------------------------------------------------------------- /sunscreen_compiler_macros/src/error.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | 3 | #[derive(Debug, Clone, thiserror::Error)] 4 | pub enum Error { 5 | #[error("{1}")] 6 | CompileError(Span, String), 7 | } 8 | 9 | impl Error { 10 | pub fn compile_error(span: Span, msg: &str) -> Self { 11 | Self::CompileError(span, msg.to_owned()) 12 | } 13 | } 14 | 15 | pub type Result = std::result::Result; 16 | -------------------------------------------------------------------------------- /sunscreen_compiler_macros/src/internals/case.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sunscreen_compiler_macros/src/internals/mod.rs: -------------------------------------------------------------------------------- 1 | // Following the pattern in serde (https://github.com/serde-rs) 2 | pub mod attr; 3 | pub mod case; 4 | -------------------------------------------------------------------------------- /sunscreen_compiler_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | #![deny(rustdoc::broken_intra_doc_links)] 3 | #![recursion_limit = "128"] 4 | 5 | //! This crate contains macros to support the sunscreen compiler. 6 | 7 | extern crate proc_macro; 8 | 9 | mod error; 10 | mod fhe_program; 11 | mod fhe_program_transforms; 12 | mod internals; 13 | mod type_name; 14 | mod zkp_program; 15 | 16 | #[proc_macro_derive(TypeName)] 17 | /** 18 | * Allows you to `#[derive(Typename)]`. 19 | */ 20 | pub fn derive_typename(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 21 | type_name::derive_typename(input) 22 | } 23 | 24 | #[proc_macro_attribute] 25 | /** 26 | * Specifies a function to be an [`fhe_program`](macro@fhe_program). An [`fhe_program`](macro@fhe_program) has any number of inputs that impl the 27 | * `FheType` trait and returns either a single type implementing `FheType` or a tuple of 28 | * types implementing `FheType`. 29 | * 30 | * This function gets run by the compiler to build up the [`fhe_program`](macro@fhe_program) you specify and does not 31 | * directly or eagerly perform homomorphic operations. 32 | * 33 | * # Parameters 34 | * * `scheme` (required): Designates the scheme this [`fhe_program`](macro@fhe_program) uses. Today, this must be `"bfv"`. 35 | * 36 | * # Examples 37 | * ```rust,ignore 38 | * # use sunscreen::{fhe_program, types::{bfv::Signed, Cipher}, Params, Context}; 39 | * 40 | * #[fhe_program(scheme = "bfv")] 41 | * fn multiply_add( 42 | * a: Cipher, 43 | * b: Cipher, 44 | * c: Cipher 45 | * ) -> Cipher { 46 | * a * b + c 47 | * } 48 | * ``` 49 | * 50 | * ```rust,ignore 51 | * # use sunscreen::{fhe_program, types::{bfv::Signed, Cipher}, Params, Context}; 52 | * 53 | * #[fhe_program(scheme = "bfv")] 54 | * fn multi_out( 55 | * a: Cipher, 56 | * b: Cipher, 57 | * c: Cipher 58 | * ) -> (Cipher, Cipher) { 59 | * (a + b, b + c) 60 | * } 61 | * ``` 62 | */ 63 | pub fn fhe_program( 64 | metadata: proc_macro::TokenStream, 65 | input: proc_macro::TokenStream, 66 | ) -> proc_macro::TokenStream { 67 | fhe_program::fhe_program_impl(metadata, input) 68 | } 69 | 70 | #[proc_macro_attribute] 71 | /** 72 | * Specifies a function to be a ZKP program. TODO: docs. 73 | */ 74 | pub fn zkp_program( 75 | metadata: proc_macro::TokenStream, 76 | input: proc_macro::TokenStream, 77 | ) -> proc_macro::TokenStream { 78 | zkp_program::zkp_program_impl(metadata, input) 79 | } 80 | -------------------------------------------------------------------------------- /sunscreen_docs/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Sunscreen Team"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Sunscreen BFV Documentation" 7 | 8 | [output.html] 9 | mathjax-support = true 10 | 11 | [output.html.fold] 12 | enable = true 13 | 14 | [rust] 15 | edition = "2021" 16 | -------------------------------------------------------------------------------- /sunscreen_docs/deploy.sh: -------------------------------------------------------------------------------- 1 | aws s3 rm s3://sunscreen-docs --recursive 2 | aws s3 sync book s3://sunscreen-docs 3 | aws cloudfront create-invalidation --distribution-id E250PLIECML3N3 --paths "/*" 4 | -------------------------------------------------------------------------------- /sunscreen_docs/running_tests.md: -------------------------------------------------------------------------------- 1 | Running our book's tests is a dark art that requires a custom build of mdbook (until our changes get accepted upstream). 2 | 3 | # Build custom mdbook 4 | Clone and build mdbook in the parent directory of this project. 5 | 6 | ``` 7 | cd .. 8 | git clone git@github.com:rickwebiii/mdBook.git 9 | cd mdBook 10 | git checkout rweber/extern 11 | cargo build --release 12 | ``` 13 | 14 | # Build sunscreen 15 | Build sunscreen (do this from your Sunscreen repo) 16 | 17 | ``` 18 | cargo build --release --package sunscreen --package bincode 19 | ``` 20 | 21 | # Run the tests 22 | ``` 23 | ../mdBook/target/release/mdbook test -L dependency=/Users/rickweber/Projects/Sunscreen/target/release/deps --extern sunscreen=/Users/rickweber/Projects/Sunscreen/target/release/libsunscreen.rlib --extern bincode=/Users/rickweber/Projects/Sunscreen/target/release/libbincode.rlib 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/advanced.md: -------------------------------------------------------------------------------- 1 | # Advanced 2 | Now that you've gotten the basics down, let's dive into some more complex topics. 3 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/batching/batching.md: -------------------------------------------------------------------------------- 1 | # Batching 2 | This section describes an advanced topic known as batching. Batching is a technique that allows you pack many values into a single ciphertext and operate on the values in parallel. -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/bfv.md: -------------------------------------------------------------------------------- 1 | # BFV scheme 2 | 3 | There are many different FHE schemes out there. Our compiler uses the [BFV scheme](http://homomorphicencryption.org/wp-content/uploads/2018/11/HomomorphicEncryptionStandardv1.1.pdf). 4 | 5 | ### Why did we choose the BFV scheme? 6 | 7 | The BFV scheme provides the following nice features: 8 | - Exact integer arithmetic (it may surprise you but some FHE schemes can't support exact integer arithmetic) 9 | - "Fast" integer arithmetic[^1] 10 | - "Small" public key sizes[^1] 11 | - Potential for "batching" (aka "SIMD"-like) operation for improved performance 12 | - Compatibility with fairly efficient [zero-knowledge proof](https://www.wired.com/story/zero-knowledge-proofs/) systems 13 | 14 | [^1]: in comparison to other FHE schemes (e.g. TFHE) 15 | 16 | ### What are some of the drawbacks to the BFV scheme? 17 | 18 | No FHE scheme is perfect. Some drawbacks to working with BFV include: 19 | - Certain operations are difficult (i.e. more expensive) to perform. This notably includes comparisons of two private values. 20 | - You can't perform private computations *indefinitely* on data. What that means for you is that you'll need to choose an upper bound (ahead of time) for how many private computations you'd like to perform. There is an advanced technique called bootstrapping to get around this issue but we do not currently support it. 21 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ### I've worked with FHE libraries before. What does your compiler actually *do*? 4 | A challenge in working with the BFV scheme is having to set polynomial modulus degree, coefficient modulus, as well as the plaintext modulus for your specific application to ensure good performance. Additionally, bootstrapping is not supported so you need to be careful in choosing the correct parameters for your application so that you don't run out of noise budget before you finish your computation. Our compiler chooses the best polynomial modulus degree and coefficient modulus for your particular program, ensuring you’ll have enough noise budget to perform the entire computation. We do not yet choose the best plaintext modulus for your specific program (this will likely be implemented in the next release). Right now, we simply hard code a value for the plaintext modulus that works for almost any computation you'd likely do. Advanced users can go in and [change this value](./plain_modulus/plain_modulus.md) manually if they'd like though. 5 | 6 | You’ll also notice there’s no encoding process to do before encryption (we’ve abstracted that away). You also don’t have to worry about inserting relinearizations in manually (done for ciphertext maintenance purposes). 7 | 8 | ### Can I perform computations on private data belonging to *different* parties? 9 | Our compiler currently only supports a single-key FHE scheme, meaning that all data needs to be encrypted with respect to the same key if you want to perform computations on it. There *are* certainly [ways](https://eprint.iacr.org/2021/133) to get around the single key limitation to enable computation on private data belonging to different parties. However, it's highly application dependent and often requires the use of additional tools (e.g. zero-knowledge proofs). 10 | 11 | There are also variants of FHE—multi-party FHE and multi-key FHE— that support computation on private data belonging to different parties out of the box. 12 | 13 | ### What are your future plans? 14 | In terms of plans for our compiler specifically, we'd like to add support for: 15 | - batching 16 | - choosing scheme parameters based on multiple FHE programs as inputs (multi-program parameter sharing) 17 | - using the outputs of one FHE program as the inputs to another FHE program (chaining) 18 | - providing rigorous analysis of noise growth 19 | 20 | In terms of broader plans for Sunscreen, some of our next milestones include: 21 | - helping users manage large FHE ciphertexts 22 | - providing a complementary zero-knowledge proof library for our FHE compiler 23 | 24 | 25 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/features.md: -------------------------------------------------------------------------------- 1 | # What features does our compiler offer? 2 | 3 | This list isn't comprehensive. These are just the main features we'd like to call attention to: 4 | - Type support for fractions, rationals, and signed integers (even 64-bit integers!) 5 | - Ability to perform computations on combinations of plaintexts and ciphertexts (e.g. you can multiply a ciphertext and plaintext together) 6 | - Can run computations without FHE (useful for testing purposes) 7 | - Private computation with literals 8 | - Automated parameter and key selection (including parameter selection based on a user-defined set of programs) 9 | - Ciphertext maintenance operations inserted automatically (these operations need to be done for optimal performance) 10 | - Compiler generates FHE programs for you (no need to work with circuits) 11 | - Compiler automatically parallelizes program (i.e. circuit) execution for you 12 | - Support for serialization 13 | - Can compile natively to Apple's M1 14 | 15 | ## Limitations 16 | 17 | We don't take advantage of all possible compiler transforms so performance isn't as good as it could be (yet!). 18 | 19 | Additionally, we do not currently allow users to author their own types. 20 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/good_fhe_programs.md: -------------------------------------------------------------------------------- 1 | # Writing even better FHE programs 2 | In this section, we're going to cover a few techniques to get the most out of your FHE applications. 3 | 4 | Specifically, we'll look at: 5 | * Manually changing the plaintext modulus to better suit your data and computation 6 | * Manually changing the noise margin to allow for more efficient computations 7 | * Manually pruning unused public keys for space savings 8 | 9 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/noise_margin.md: -------------------------------------------------------------------------------- 1 | # Noise 2 | 3 | Every FHE operation introduces *noise* into ciphertexts.[^1] If too much noise accrues, then the message becomes garbled and cannot be decrypted. The total amount of allowed noise before this problem occurs is referred to as the *noise budget*. 4 | 5 | Fortunately, our compiler handles this noise issue for you. It uses a value known as the `noise_margin` that influences how conservative it needs to be in parameter selection. So long as `noise_budget >= noise_margin`, we're good to go. Sunscreen's default noise margin prevents garbled ciphertexts coming from a *single* FHE program. 6 | 7 | [^1]: Multiplying ciphertexts is one of the largest contributors to noise growth. 8 | 9 | ## Why you might want to change the default noise margin 10 | There are a few reasons you may wish to change the noise margin: 11 | * If you wish to use an output of one FHE program as an input to another FHE program, you need to allow for additional noise growth in subsequent programs. See our [calculator](https://github.com/Sunscreen-tech/Sunscreen/blob/main/examples/calculator_rational/src/main.rs#L221) for a complete example of this scenario. 12 | * Decreasing the noise margin can result in improved performance if your application can tolerate a higher rate of faults. 13 | 14 | ## How to change the noise margin 15 | To change the noise margin, simply call `.additional_noise_budget()` when compiling your program. For example: 16 | 17 | ```rust 18 | # use sunscreen::{ 19 | # fhe_program, 20 | # types::{bfv::Signed, Cipher}, 21 | # Compiler, Runtime, PublicKey 22 | # }; 23 | # 24 | # #[fhe_program(scheme = "bfv")] 25 | # fn my_program() { 26 | # } 27 | # 28 | # fn main() { 29 | let app = Compiler::new() 30 | .fhe_program(my_program) 31 | .additional_noise_budget(60) 32 | .compile() 33 | .unwrap(); 34 | # } 35 | ``` 36 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/performance.md: -------------------------------------------------------------------------------- 1 | # Performance 2 | 3 | We've benchmarked Sunscreen's compiler against existing FHE compilers (that support exact computation). We run a chi-squared test according to the criteria set out in [this](https://arxiv.org/pdf/2101.07078.pdf) SoK paper on FHE compilers. 4 | 5 | Time includes key generation + encryption + (homomorphic) computation + decryption. 6 | 7 | Experiments were performed on an Intel Xeon @ 3.00 GHz with 8 cores and 16 GB RAM. 8 | 9 | | Compiler | Time (seconds) | 10 | | ------------- | ------------- | 11 | | Sunscreen | 0.072 | 12 | | Microsoft EVA | 0.328 | 13 | | Cingulata-BFV | 492.109 | 14 | | Cingulata-TFHE | 62.118 | 15 | | E3-BFV | 11.319 | 16 | | E3-TFHE | 1934.663 | 17 | | Zama Concrete Numpy | N/A[^1] | 18 | 19 | [^1]: Zama's compiler could not support the program (only computation on values <256 is supported). 20 | 21 | Our compiler is built on SEAL's implementation of the BFV scheme. For reference, if coded directly in SEAL and optimized manually by an expert, the chi-squared test can run in 0.053 s. 22 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/plain_modulus/plain_modulus.md: -------------------------------------------------------------------------------- 1 | # Plaintext modulus 2 | 3 | FHE uses some [funky math](../../intro/why.md). At the lowest level, plaintexts are polynomials where the coefficient of each term is an integer modulo the *plaintext modulus*. The plaintext modulus parameter impacts [correctness](../carryless_arithmetic.md#overflow) and performance — overflow occurs when the plaintext modulus is too small, but increasing it can negatively impact performance. Sunscreen balances these two considerations by setting a default plaintext modulus that prevents overflow in most applications while maintaining good performance.[^1] However, you may at times wish to change it. 4 | 5 | [^1]: The default is `64^3 = 262,144`, which allows multiplying any 4 canonical (i.e. all 1s and 0s) 64-bit input values without overflow. 6 | 7 | ## Why you might want to change the default plaintext modulus 8 | A few reasons you would change the plain modulus include: 9 | * If the default is too conservative, decreasing the plaintext modulus can improve performance. 10 | * Very very rarely, the default can cause overflow in some FHE programs. Increasing the plaintext modulus solves this issue at the expense of performance. 11 | * You wish to use batching, which requires very specific values. 12 | 13 | When setting the plaintext modulus, you call `compiler.plain_modulus_constraint()` and pass a `PlainModulusConstraint`, which comes in two forms: 14 | * `Raw(x)` sets the plaintext modulus to `x`. 15 | * `BatchingMinimum(x)` chooses a value suitable for use with batching with at least `x` bits of precision. As noted in the name, this modulus should be used with batching. 16 | 17 | ## How to change the plaintext modulus 18 | You can manually set the `PlainModulusConstraint` when compiling your program like so: 19 | ```rust 20 | # use sunscreen::{ 21 | # fhe_program, 22 | # types::{bfv::Signed, Cipher}, 23 | # Compiler, Runtime, PublicKey, PlainModulusConstraint 24 | # }; 25 | # 26 | # #[fhe_program(scheme = "bfv")] 27 | # fn my_program() { 28 | # } 29 | # 30 | # fn main() { 31 | let app = Compiler::new() 32 | .fhe_program(my_program) 33 | .plain_modulus_constraint(PlainModulusConstraint::Raw(1_000_000)) 34 | .compile() 35 | .unwrap(); 36 | # } 37 | ``` 38 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/advanced/pruning_keys.md: -------------------------------------------------------------------------------- 1 | # Pruning public keys 2 | For convenience, the `generate_keys` function creates several keys in the returned `PublicKey` object. 3 | 4 | ## Why you might want to prune `PublicKey` 5 | Some of these keys can be fairly large, the size of which is determined by scheme parameters. However, they may or may not be needed in your application: 6 | 7 | * Galois keys (`galois_key`) are needed to run FHE programs that perform batching rotations or row swapping. 8 | * Relinearization keys (`relin_key`) are needed to run FHE programs that multiply ciphertexts. 9 | 10 | 11 | ## How to prune `PublicKey` 12 | 13 | Each compiled FHE program contains a list the keys it needs at runtime in `fhe_program.metadata.required_keys`. You can then remove unneeded keys. For example: 14 | ```rust 15 | # use sunscreen::{ 16 | # fhe_program, 17 | # types::{bfv::Signed, Cipher}, 18 | # Compiler, FheRuntime, PublicKey 19 | # }; 20 | # 21 | # #[fhe_program(scheme = "bfv")] 22 | # fn noop() { 23 | # } 24 | # 25 | # fn main() { 26 | # let app = Compiler::new() 27 | # .fhe_program(noop) 28 | # .compile() 29 | # .unwrap(); 30 | # 31 | # let runtime = FheRuntime::new(app.params()).unwrap(); 32 | let (public_key, private_key) = runtime.generate_keys().unwrap(); 33 | 34 | // Shadow and overwrite the public_key, removing the galois_key and relin_key 35 | let public_key = PublicKey { 36 | galois_key: None, // only do this if not using batching 37 | relin_key: None, // only do this if your FHE program never multiplies ciphertexts 38 | ..public_key 39 | }; 40 | # } 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/compiler/compiler.md: -------------------------------------------------------------------------------- 1 | # Sunscreen's compiler 2 | 3 | Our goal is to make it easy for *any engineer* to write an FHE program. To accomplish this, we've been working to get the API just right (we're always excited to hear feedback from users!). A large part of this was choosing the right language for our compiler— we chose [Rust](https://www.rust-lang.org/). In addition to having a powerful and expressive type system, Rust is very well suited to cryptography. It's highly performant (like C/C++) *and* safe by design (unlike C/C++). 4 | 5 | Our compiler relies on Microsoft's [SEAL](https://github.com/microsoft/SEAL) library. There are many different types of FHE schemes out there; we've chosen to use the BFV fully homomorphic encryption scheme—named for the authors (Brakerski-Fan-Vercauteren) who came up with the scheme. 6 | 7 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/compiler/features.md: -------------------------------------------------------------------------------- 1 | # What features does our compiler offer? 2 | 3 | This list isn't comprehensive. These are just the main features we'd like to call attention to: 4 | - Type support for fractions, rationals, and signed integers (even 64-bit integers!) 5 | - Ability to perform computations on combinations of plaintexts and ciphertexts (e.g. you can multiply a ciphertext and plaintext together) 6 | - Can run computations without FHE (useful for testing purposes) 7 | - Private computation with literals 8 | - Automated parameter and key selection 9 | - Ciphertext maintenance operations inserted automatically (these operations need to be done for optimal performance) 10 | - Compiler generates FHE programs for you (no need to work with circuits) 11 | - Compiler automatically parallelizes program (i.e. circuit) execution for you 12 | - Support for WASM 13 | - Support for serialization 14 | - Can compile natively to Apple's M1 15 | 16 | *Note:* Although we have performed a number of optimizations, we don't take advantage of all possible compiler transforms (yet!). Additionally, we do not currently allow users to author their own types. 17 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/compiler/performance.md: -------------------------------------------------------------------------------- 1 | # Compiler performance 2 | 3 | We've benchmarked Sunscreen's compiler against existing FHE compilers (that support exact computation). We run a chi-squared test according to the criteria set out in [this](https://arxiv.org/pdf/2101.07078.pdf) SoK paper on FHE compilers. 4 | 5 | Time includes key generation + encryption + (homomorphic) computation + decryption. 6 | 7 | Experiments were performed on an Intel Xeon @ 3.00 GHz with 8 cores and 16 GB RAM. 8 | 9 | | Compiler | Time (seconds) | 10 | | ------------- | ------------- | 11 | | Sunscreen | 0.072 | 12 | | Microsoft EVA | 0.328 | 13 | | Cingulata-BFV | 492.109 | 14 | | Cingulata-TFHE | 62.118 | 15 | | E3-BFV | 11.319 | 16 | | E3-TFHE | 1934.663 | 17 | | Concrete Numpy | N/A[^1] | 18 | 19 | [^1]: This compiler could not support the program. Concrete Numpy only allows for 256 unique values (e.g. can only represent integer values in the range [0, 255]). 20 | 21 | Our compiler is built on SEAL's implementation of the BFV scheme. For reference, if coded directly in SEAL and optimized manually by an expert, the chi-squared test can run in 0.053 s. 22 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/compiler/who.md: -------------------------------------------------------------------------------- 1 | # Who should use our compiler? 2 | You're building an application that operates on user data *but* you want to ensure all user data remains private. 3 | 4 | ## You're a web3 engineer. 5 | Our compiler was primarily designed with your web3 needs in mind! 6 | 7 | You likely need all of the following features: 8 | - Exact computation (since you're working with account balances and currency transfer) 9 | - Compatibility with efficient ZKP schemes for trustless decentralized applications (we plan to provide the appropriate libraries for this) 10 | - Program chaining (helpful for smart contract composability) 11 | - Support for fractions/rationals/big integers 12 | - Fast arithmetic 13 | - Exceptional performance overall 14 | 15 | You may notice that FHE ciphertexts can sometimes be quite large. In the future, we'll help you manage this issue. 16 | 17 | ## You're a web2 engineer. 18 | Performance is very important to you; more importantly, the code needs to be easy to understand and write since you don't have the time to learn the intricacies of FHE or (god forbid) an entirely new language. You may need to perform 32 or even 64 bit computation. 19 | 20 | Our compiler is great for many web2 applications (e.g. data analysis on private data). Comparisons on encrypted data are not currently supported; please keep this in mind when deciding if our compiler is best suited to your application. We will likely expand support to other FHE schemes in the future. The CKKS scheme, for example, is often better suited to privacy-preserving machine learning applications than the BFV scheme. 21 | 22 | ## You're a researcher. 23 | You want to quickly prototype FHE applications without fussing over the optimal parameter choice. However, performance is very important and you don't want to introduce a significant slowdown by working with an FHE compiler (over the FHE scheme directly). 24 | 25 | We also provide advanced features that allow you to fine tune the [plaintext modulus](./../advanced/plain_modulus/plain_modulus.md) choice and [noise margin](./../advanced/noise_margin.md) if desired. 26 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/apps.md: -------------------------------------------------------------------------------- 1 | # Applications 2 | In this section, we'll look at some (hopefully) interesting applications of FHE to web2 and web3. 3 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/factoring_fhe_programs.md: -------------------------------------------------------------------------------- 1 | # Factoring FHE programs 2 | 3 | In this section we'll show you how to factor your programs in a specific way that allows for 4 | * Reusing algorithms with different data types. 5 | * Running your algorithm without FHE. This allows you to debug the algorithm without encryption getting in your way and measure FHE's performance overhead. 6 | 7 | Let's begin by rewriting our `simple_multiply` example with a common implementation (`simple_multiply_impl`): 8 | 9 | ```rust 10 | use sunscreen::{ 11 | fhe_program, 12 | types::{bfv::{Signed, Fractional}, Cipher}, 13 | }; 14 | use std::ops::Mul; 15 | 16 | fn simple_multiply_impl(a: T, b: U) -> T 17 | where T: Mul + Copy 18 | { 19 | a * b 20 | } 21 | 22 | #[fhe_program(scheme = "bfv")] 23 | fn simple_multiply_signed(a: Cipher, b: Cipher) -> Cipher { 24 | simple_multiply_impl(a, b) 25 | } 26 | 27 | 28 | #[fhe_program(scheme = "bfv")] 29 | fn simple_multiply_fractional(a: Cipher>, b: Fractional<64>) -> Cipher> { 30 | simple_multiply_impl(a, b) 31 | } 32 | ``` 33 | 34 | The first FHE program multiplies encrypted `Signed` values. In the second, `a` is an encrypted `Fractional` value while `b` is an unencrypted `Fractional` value. We can run both of these programs using `runtime.run()` as normal. 35 | 36 | ## Running your implementation without FHE 37 | If we inspect the [trait bounds](https://doc.rust-lang.org/rust-by-example/generics/where.html) on `simple_multiply_impl`, we'll notice there is no mention of anything Sunscreen related. This means we can directly run our implementation with Rust `i64` values by simply calling: 38 | 39 | ```rust 40 | # use std::ops::Mul; 41 | # 42 | # fn simple_multiply_impl(a: T, b: U) -> T 43 | # where T: Mul + Copy 44 | # { 45 | # a * b 46 | # } 47 | # 48 | # fn main() { 49 | simple_multiply_impl(7, 5); 50 | # } 51 | ``` 52 | 53 | It's worth explicitly pointing out that `T` and `U` may be of the same or different types; the trait bounds merely require that you can multiply `T` and `U` values. 54 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/fhe_programs.md: -------------------------------------------------------------------------------- 1 | # What's in an FHE program? 2 | This section describes the anatomy of an FHE program, what you can and can't do, and the different data types FHE programs support. 3 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/pir_intro.md: -------------------------------------------------------------------------------- 1 | # Private information retrieval 2 | With private information retrieval (PIR), a user can retrieve an item from a database without *revealing* to the server which item she's interested in. PIR is useful for both web2 and web3 applications. In web2, for example, PIR can be used to [help detect harmful images](https://www.usenix.org/system/files/sec21summer_kulshrestha.pdf) in end-to-end encrypted messaging. For private cryptocurrencies, PIR can help light clients [retrieve relevant transactions](https://eprint.iacr.org/2021/1256.pdf). 3 | 4 | To make this section easy to understand, we'll implement two simple PIR algorithms with our compiler. 5 | 6 | The [first algorithm](./pir_simple.md) does not require the full power of FHE since we only perform ciphertext-plaintext multiplication via a vector dot product; thus, an additively homomorphic encryption scheme would actually suffice. 7 | 8 | The [second algorithm](./pir_matrix.md) *does* require the full power of FHE as we'll perform both ciphertext-ciphertext multiplication and ciphertext-plaintext multiplication via a matrix-vector product and vector dot product. 9 | 10 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/programs_to_apps.md: -------------------------------------------------------------------------------- 1 | # From toy programs to real life 2 | 3 | So far, we’ve seen how to write a single FHE program that can be run once on an encrypted data set. However, such a situation is often not representative of real life applications of FHE. What if we’d like to run different programs on the same encrypted data set? What if we want to feed the outputs of one FHE program as the inputs to another FHE program? 4 | 5 | In this chapter, we’ll look at how to use *unified parameters* and *chaining* to address the above scenarios. 6 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/runtime/decryption.md: -------------------------------------------------------------------------------- 1 | # Decryption 2 | To decrypt, simply call `decrypt()` using your private key and the data you want to decrypt 3 | 4 | ```rust 5 | # use sunscreen::{ 6 | # fhe_program, 7 | # types::{bfv::Signed, Cipher}, 8 | # Compiler, FheRuntime, PublicKey, PlainModulusConstraint 9 | # }; 10 | # 11 | # #[fhe_program(scheme = "bfv")] 12 | # fn noop() { 13 | # } 14 | # 15 | # fn main() { 16 | # let app = Compiler::new() 17 | # .fhe_program(noop) 18 | # .plain_modulus_constraint(PlainModulusConstraint::Raw(5)) 19 | # .compile() 20 | # .unwrap(); 21 | # 22 | # let runtime = FheRuntime::new(app.params()).unwrap(); 23 | # let (public_key, private_key) = runtime.generate_keys().unwrap(); 24 | # 25 | # let val = Signed::from(15); 26 | # let val_enc = runtime.encrypt(val, &public_key).unwrap(); 27 | # 28 | // val_enc is an encrypted `Signed` value coming from an FHE program 29 | // or just directly encrypted. 30 | let a: Signed = runtime.decrypt(&val_enc, &private_key).unwrap(); 31 | # } 32 | ``` 33 | 34 | ## Validation 35 | Unlike with `encrypt`, you must specify the unencrypted data type (either on the left-hand side as above or using [turbofish](https://techblog.tonsser.com/posts/what-is-rusts-turbofish)). The encrypted value's type must match the assigned type; if the specified data type doesn't match that on the [ciphertext](./encryption.html#type-annotation), `decrypt` will return an error. In the above example, decrypt will fail if the encrypted value is not a `Signed` type. 36 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/runtime/encryption.md: -------------------------------------------------------------------------------- 1 | # Encryption 2 | To encrypt data, simply call `encrypt()` on `FheRuntime`: 3 | ```rust 4 | # use sunscreen::{ 5 | # fhe_program, 6 | # types::{bfv::Signed, Cipher}, 7 | # Compiler, FheRuntime, PublicKey 8 | # }; 9 | # 10 | # #[fhe_program(scheme = "bfv")] 11 | # fn noop() { 12 | # } 13 | # 14 | # fn main() { 15 | # let app = Compiler::new() 16 | # .fhe_program(noop) 17 | # .compile() 18 | # .unwrap(); 19 | # 20 | # let runtime = FheRuntime::new(app.params()).unwrap(); 21 | # let (public_key, private_key) = runtime.generate_keys().unwrap(); 22 | # 23 | let val = Signed::from(15); 24 | let val_enc = runtime.encrypt(val, &public_key).unwrap(); 25 | # } 26 | ``` 27 | 28 | This produces a `Ciphertext` value suitable for use in `run()`. Be careful not to confuse the `Ciphertext` type, which represents an actual encrypted value, with [`Cipher`](../types/types.md#cipher), which is a marker type to indicate a value in an FHE program is encrypted! Sunscreen can encrypt any of its provided [types](../types/types.md) or fixed-length arrays[^1] of them. Note that arrays encrypt as multiple values in a single large `Ciphertext`. 29 | 30 | [^1]: Fixed-length arrays have the type `[T; N]` where `N` is a the number `T`s. Don't confuse these with `Vec`, which does not encode the length in its type! Sunscreen does not support `Vecs`. 31 | 32 | ## Cleartext metadata 33 | Sunscreen attaches scheme parameters and the underlying datatype metadata to each `Ciphertext`. The former aids in serialization, while the latter [prevents honest mistakes](../runtime/running_fhe_programs.md#validation) and improves the developer experience. When you serialize `Ciphertext` values to send over the network, this metadata appears in the clear. For most applications, this will be public information and part of the protocol. If, for some reason, you need the data type or scheme parameters to also be encrypted, you should encrypt the serialized ciphertext (e.g. use TLS for communication). 34 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/runtime/key_generation.md: -------------------------------------------------------------------------------- 1 | # Key Generation 2 | Once you've created a runtime, generating keys is simple: 3 | 4 | ```rust 5 | # use sunscreen::{ 6 | # fhe_program, 7 | # types::{bfv::Signed, Cipher}, 8 | # Compiler, FheRuntime, PublicKey 9 | # }; 10 | # 11 | # #[fhe_program(scheme = "bfv")] 12 | # fn noop() { 13 | # } 14 | # 15 | # fn main() { 16 | # let app = Compiler::new() 17 | # .fhe_program(noop) 18 | # .compile() 19 | # .unwrap(); 20 | # 21 | # let runtime = FheRuntime::new(app.params()).unwrap(); 22 | # 23 | let (public_key, private_key) = runtime.generate_keys().unwrap(); 24 | # } 25 | ``` 26 | 27 | This produces a public key (which allows you to encrypt data and run FHE programs) and a private key (which allows you to decrypt). 28 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/runtime/runtime.md: -------------------------------------------------------------------------------- 1 | # Runtime 2 | To create a runtime, you simply call `FheRuntime::new`, passing a `Params` object. You get a params object from compiling an FHE program as we did in our example. 3 | 4 | ```rust 5 | # use sunscreen::{ 6 | # fhe_program, 7 | # types::{bfv::Signed, Cipher}, 8 | # Compiler, FheRuntime, PublicKey 9 | # }; 10 | # 11 | # #[fhe_program(scheme = "bfv")] 12 | # fn noop() { 13 | # } 14 | # 15 | # fn main() { 16 | # let app = Compiler::new() 17 | # .fhe_program(noop) 18 | # .compile() 19 | # .unwrap(); 20 | # 21 | let runtime = FheRuntime::new(app.params()).unwrap(); 22 | # } 23 | ``` 24 | 25 | Once you're created a runtime, you can: 26 | * [generate public/private keys](./key_generation.md) 27 | * [encrypt plaintexts](./encryption.md) 28 | * [run FHE programs](./running_fhe_programs.md) 29 | * [decrypt ciphertexts](./decryption.md) 30 | 31 | ## Parameter compatibility 32 | Note that to use artifacts produced by a runtime (e.g. ciphertexts, keys), they must have been produced under a runtime using *exactly the same parameters*. This situation may have ramifications if you're attempting to re-use ciphertexts across multiple FHE programs; those programs must be compiled with the *same* set of parameters. 33 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/runtime/serialization.md: -------------------------------------------------------------------------------- 1 | # Serialization 2 | While most examples compute everything in one place, in practice, data will be split amongst multiple machines. You can serialize many things in Sunscreen: 3 | * Ciphertexts 4 | * Keys 5 | * Scheme parameters 6 | * FHE programs 7 | 8 | Sunscreen uses [`serde`](https://serde.rs/) for serialization and can serialize data in a number of formats including JSON and bincode. Since most data in Sunscreen is high entropy byte arrays, we recommend using [bincode](https://docs.rs/bincode/1.3.3/bincode/) since it reduces storage and network requirements by efficiently packing byte arrays. 9 | 10 | The process to serialize and deserialize any type is the same, but this example shows how to do it with a ciphertext: 11 | ```rust 12 | # use sunscreen::{ 13 | # fhe_program, 14 | # types::{bfv::Signed, Cipher}, 15 | # Compiler, FheRuntime, PublicKey, Ciphertext 16 | # }; 17 | # 18 | # #[fhe_program(scheme = "bfv")] 19 | # fn noop() { 20 | # } 21 | # 22 | # fn main() { 23 | # let app = Compiler::new() 24 | # .fhe_program(noop) 25 | # .compile() 26 | # .unwrap(); 27 | # 28 | # let runtime = FheRuntime::new(app.params()).unwrap(); 29 | # let (public_key, _) = runtime.generate_keys().unwrap(); 30 | let c = runtime 31 | .encrypt(Signed::from(20), &public_key) 32 | .unwrap(); 33 | 34 | // ser is now a Vec that can be written to disk, socket, etc. 35 | let ser = bincode::serialize(&c).unwrap(); 36 | 37 | // Upon receiving a serialized byte array, deserialize it 38 | // back into a Ciphertext. You may now use it normally. 39 | let c: Ciphertext = bincode::deserialize(&ser).unwrap(); 40 | # } 41 | ``` 42 | 43 | As with any dependency, you'll need to add `bincode` as a dependency in your `Cargo.toml`. 44 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/types/fractional.md: -------------------------------------------------------------------------------- 1 | # Fractional 2 | `Fractional` values allow you to perform decimal arithmetic. You can think of `Fractional` as being similar to a fixed-point representation. 3 | 4 | Sunscreen represents `Fractional` values as an integer and fractional part. The `INT_BITS` type argument specifies how many binary digits store the integer part. Setting `INT_BITS` to `64` should be more than sufficient for most applications since `Fractional<64>` values can exactly represent every `i64` with thousands (!!) of binary digits for the decimal portion. 5 | 6 | You can perform operations on `Fractional` values as follows (recall at least one operand must be a ciphertext): 7 | 8 | operation | operand 9 | ----------|--------------------------------------- 10 | add | ciphertext, plaintext, literal 11 | sub | ciphertext, plaintext, literal 12 | mul | ciphertext, plaintext, literal 13 | div | ciphertext numerator + literal divisor 14 | 15 | Additionally, you perform unary negation on `Fractional` ciphertexts. 16 | 17 | While division by only literals may seem limiting, this is one of the more common use cases. For example, you can average 3 values: 18 | 19 | ```rust 20 | # use sunscreen::{ 21 | # fhe_program, 22 | # types::{bfv::Fractional, Cipher}, 23 | # Compiler, Runtime, PublicKey 24 | # }; 25 | 26 | #[fhe_program(scheme = "bfv")] 27 | fn avg( 28 | a: Cipher>, 29 | b: Cipher>, 30 | c: Cipher> 31 | ) -> Cipher> { 32 | (a + b + c) / 3.0 33 | } 34 | ``` 35 | 36 | Additionally, division by literals is sufficient to compute many transcendental functions (e.g. `sin`, `cos`, `exp`) via a power series. 37 | 38 | ## Representation 39 | A scheme parameter `lattice_dimension` (chosen by the Sunscreen compiler) determines the number of decimal places such that `INT_BITS + DECIMAL_BITS = lattice_dimension`. This scheme parameter is always at least `1024`. You can find the compiler's chosen value with `my_compiled_program.metadata.params.lattice_dimension`. 40 | 41 | `Fractional` values use exact arithmetic and don't suffer from roundoff errors as floating point values do. In fact, if `INT_BITS=1024` and `lattice_dimension >= 2048` they can *exactly* store every double precision value with a fixed decimal point! 42 | 43 | ## Efficiency 44 | Unlike the [`Rational`](./rational.md) type, storing and computing `Fractional` values is as efficient as [`Signed`](./signed.md) values. 45 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/types/rational.md: -------------------------------------------------------------------------------- 1 | # Rational 2 | The `Rational` type allows you to perform all decimal arithmetic: 3 | 4 | operation | operand 5 | ----------|--------------------------------------- 6 | add | ciphertext, plaintext, literal 7 | sub | ciphertext, plaintext, literal 8 | mul | ciphertext, plaintext, literal 9 | div | ciphertext, plaintext, literal 10 | 11 | Additionally, you can perform unary negation on `Rational` ciphertexts (i.e., given `a`, compute `-a`). 12 | 13 | ## Representation 14 | `Rational` encodes a numerator and denominator as two independent `Signed` values. This results in ciphertexts **twice** as large as when using the [`Fractional`](./fractional.md) type. 15 | 16 | ## Efficiency 17 | In addition to the increased size, each `Rational` operation (except negation) requires multiple FHE operations. Thus, even addition can quickly increase FHE [program complexity](../../advanced/noise_margin.html#what-is-noise). Using `Rational` ciphertexts in prolonged computation may require larger scheme parameters (hence resulting in slower computations). 18 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/types/signed.md: -------------------------------------------------------------------------------- 1 | # Signed 2 | `Signed` values allow you to perform integer arithmetic as follows (recall that at least one operand must be a ciphertext): 3 | 4 | operation | operand 5 | ----------|--------------------------------------- 6 | add | ciphertext, plaintext, `i64` literal 7 | sub | ciphertext, plaintext, `i64` literal 8 | mul | ciphertext, plaintext, `i64` literal 9 | 10 | Additionally, you can perform unary negation on encrypted `Signed` values. 11 | 12 | ## Representation 13 | 14 | `Signed` values contain thousands of binary digits of precision, easily enough to store any `i64` value. 15 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/writing_an_fhe_program/fhe_programs.md: -------------------------------------------------------------------------------- 1 | # FHE Programs 2 | 3 | Now, we're going to get into the anatomy of an FHE program, what they can and can't do, and best practices for maximizing code re-use. 4 | 5 | You write FHE programs in Sunscreen's domain-specific language (DSL) built within Rust. Leveraging an existing ecosystem in this way provides many synergies; you can consume and share FHE programs/subroutines with Cargo's package manager, no new syntax to learn, and how one expresses FHE programs can evolve with the Rust language. 6 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/fhe_programs/writing_an_fhe_program/writing_an_fhe_program.md: -------------------------------------------------------------------------------- 1 | # Writing an FHE program 2 | 3 | An FHE program is simply a Rust function with an annotation and a few restrictions. However, unlike standard Rust functions, FHE programs work with encrypted data! 4 | 5 | ## The `#[fhe_program(...)]` attribute 6 | To indicate that a function is an FHE program, simply add the `#[fhe_program()]` attribute to an `fn` function: 7 | 8 | ```rust 9 | # use sunscreen::{ 10 | # fhe_program, 11 | # }; 12 | 13 | #[fhe_program(scheme = "bfv")] 14 | fn my_fhe_program() { 15 | } 16 | ``` 17 | 18 | This attribute takes a single `scheme` argument. Currently, this argument value should always be `"bfv"`, our supported FHE scheme. 19 | 20 | ## FHE program interface requirements 21 | 22 | FHE programs implement their logic in the `fn` function beneath the `#[fhe_program()]` attribute. The function you write must satisfy some conditions: 23 | 24 | * Your `fn` function must be non-generic and stand-alone (i.e. not a `struct` method, closure, `trait` method, etc). 25 | * Your `fn` function may take any number of arguments. 26 | * Each argument must be of either type `T` (i.e. plaintext) or `Cipher` (i.e. ciphertext), where `T` is a type [supported](../types/types.md) in FHE programs. Every argument need not be the same `T`. 27 | * Your `fn` function must return either a `Cipher` or a tuple of `(Cipher, Cipher, ...)` values (i.e. return values are always encrypted). As with arguments, types must be supported in FHE programs. 28 | 29 | Here's an example of an FHE program that returns a tuple containing two encrypted values: `a * b` and `a + c`. 30 | 31 | ```rust 32 | # use sunscreen::{ 33 | # fhe_program, 34 | # types::{bfv::Signed, Cipher}, 35 | # }; 36 | # 37 | #[fhe_program(scheme = "bfv")] 38 | fn multiple_returns(a: Cipher, b: Cipher, c: Signed) -> (Cipher, Cipher) { 39 | (a * b, a + c) 40 | } 41 | ``` 42 | 43 | ## Operations 44 | In FHE programs, you can: 45 | * Perform basic operations (`+`, `-`, `*`, `/`, `<<`, `>>`). The supported set of operations vary from type to [type](../types/types.md). Note that at least one of the operands must be a ciphertext. 46 | * Call functions. 47 | * Use any Rust construct (e.g. `match`, `for i in ...`, `if...else`) on data *not* derived from any argument. We walk through a number of examples in the [limitations](./limitations.md#branching-restricted-to-constant-expressions) chapter. 48 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/getting_started/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | This chapter walks through installation. We also provide an overview of Sunscreen through a simple example of multiplying two encrypted numbers. 3 | 4 | If you'd like to try out the compiler before installing it, check out [our playground](https://playground.sunscreen.tech/). 5 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/intro/intro.md: -------------------------------------------------------------------------------- 1 | This documentation is about the BFV version of our compiler; for our new FHE compiler based on the TFHE scheme, see our [TFHE Compiler](https://docs.sunscreen.tech) documentation. 2 | 3 | # Introduction to FHE and our compiler 4 | 5 | Fully homomorphic encryption (FHE) is the next generation of public key encryption schemes. Standard public key encryption allows anyone to share data in a secure way. However, you can't really *do* anything with this encrypted data, apart from decrypting it. That's where FHE comes in! 6 | 7 | Using FHE, anyone can perform computations directly on private (i.e. encrypted) data—no need to decrypt. 8 | 9 | We recommend starting with [this article](https://blog.sunscreen.tech/an-intro-to-fully-homomorphic-encryption-for-engineers/) if you're new to FHE. 10 | 11 | 12 | ### Why should I care about FHE? 13 | 14 | FHE has applications to a variety of fields. We'll briefly consider two applications of FHE in the blockchain and machine learning space. 15 | 16 | In blockchain, FHE enables [privacy-preserving smart contracts](https://eprint.iacr.org/2021/727). We have two parties in this setting: users and miners. Users can share their private data to the chain in the form of transactions. Miners can then run computations (encoded as smart contracts) directly on users' private data. 17 | 18 | In machine learning, FHE allows for private inference. We have two parties in this setting: the user (who owns the data) and the server (who owns the trained machine learning model). The user can share her private data with the server. The server can then run the model on the user's encrypted data to give her a private prediction (which only she knows!). 19 | 20 | ### Why haven't I already used FHE? 21 | 22 | FHE used to be incredibly slow. Performance has come a long way in the past few years; operations that used to take seconds (or even minutes) now take milliseconds (if not microseconds). 23 | 24 | As magical as FHE is, it's actually [very hard](../intro/why.md) to write FHE programs unless you're a cryptography expert (even then it's pretty hard). 25 | Researchers built various FHE compilers in an attempt to improve usability. Unfortunately, these compilers failed for one of the following reasons: they introduced a [huge performance overhead](../compiler/performance.md), expected the user to know quite a bit about how FHE works, or were poorly designed for the target applications. 26 | 27 | For FHE to see widespread adoption, we need usability *and* great performance. 28 | 29 | -------------------------------------------------------------------------------- /sunscreen_docs/src/fhe/intro/prelim.md: -------------------------------------------------------------------------------- 1 | # Preliminary knowledge 2 | To effectively use our library, we assume some basic knowledge of public key cryptography as well as Rust. 3 | 4 | ## Cryptography and math 5 | - **Plaintext vs. ciphertext**: Plaintext refers to *un*encrypted data (i.e. data in the clear) whereas ciphertext refers to *encrypted* data. 6 | - **Public key vs. private key**: In public key cryptography, there are two types of keys. The *public* key (aka the encryption key) can be shared with others. Using the public key, people can send encrypted messages. The *private* key (aka the decryption key) should *not* be shared. Whoever holds the private key can decrypt the messages addressed to them (which are encrypted under the corresponding public key). Usually, each person has their own public/private key pair. 7 | - **"Homomorphic"**: We use this term to denote computations performed directly on encrypted data. For example, if we say "homomorphic addition," we are referring to addition of two *ciphertexts*. 8 | - **Modulus**: We use this term in the context of discussing modular arithmetic (aka clock arithmetic). Under modular arithmetic, values "wrap around" when they exceed the *modulus*. In the example `7 mod 4 = 3`, `4` is the modulus. 9 | 10 | ## Rust and programming 11 | - Rust basics (e.g. [rust types](https://doc.rust-lang.org/book/ch03-02-data-types.html), [traits](https://doc.rust-lang.org/book/ch10-02-traits.html), [`.unwrap()`](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html) and other error handling techniques) 12 | - [Generic types](https://doc.rust-lang.org/book/ch10-01-syntax.html) 13 | - [Generic functions](https://doc.rust-lang.org/book/ch10-00-generics.html) 14 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/advanced/advanced.md: -------------------------------------------------------------------------------- 1 | # Advanced 2 | Now that you've gotten the basics down, let's dive into some more complex topics. 3 | 4 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/advanced/optimized.md: -------------------------------------------------------------------------------- 1 | # Writing even better ZKP programs 2 | 3 | In this section, we're going to cover a few techniques that allow you to get the most out of your ZKP programs. 4 | 5 | Specifically, we'll look at: 6 | - Creating gadgets to improve performance and be re-used across programs (gadgets also allow you to do stuff like division which isn't native to R1CS!) 7 | - Using constant (instead of public) inputs to improve performance where possible 8 | - Creating your own types for certain use cases 9 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/applications/applications.md: -------------------------------------------------------------------------------- 1 | # Applications 2 | 3 | In this section, we'll look at some less trivial ZKP programs. 4 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/compiling/compiling.md: -------------------------------------------------------------------------------- 1 | # Compiling 2 | 3 | After you've defined a ZKP program, you'll need to compile it. The simplest way, 4 | as we saw in the [first example](../getting_started/example.md), is to just call 5 | `.compile()` on the program: 6 | 7 | ```rust 8 | use sunscreen::{ 9 | bulletproofs::BulletproofsBackend, 10 | types::zkp::{Field, FieldSpec}, 11 | zkp_program, Error, ZkpProgramFnExt 12 | }; 13 | 14 | #[zkp_program] 15 | fn tautology(x: Field) { 16 | x.constrain_eq(x); 17 | } 18 | 19 | fn main() -> Result<(), Error> { 20 | let tautology_compiled = tautology.compile::()?; 21 | Ok(()) 22 | } 23 | ``` 24 | 25 | Remember that you'll need to specify the proof system being used in the backend (e.g. `BulletproofsBackend`). 26 | 27 | However, if you have multiple ZKP programs to compile, you may find it easier to 28 | invoke a full [`Compiler`][compiler-docs] and get back an 29 | [`Application`][application-docs] holding multiple programs. 30 | 31 | ```rust 32 | use sunscreen::{ 33 | bulletproofs::BulletproofsBackend, 34 | types::zkp::{Field, FieldSpec}, 35 | zkp_program, zkp_var, Error, Compiler 36 | }; 37 | 38 | #[zkp_program] 39 | fn tautology(x: Field) { 40 | x.constrain_eq(x); 41 | } 42 | 43 | #[zkp_program] 44 | fn contradiction(x: Field) { 45 | x.constrain_eq(x + zkp_var!(1)); 46 | } 47 | 48 | fn main() -> Result<(), Error> { 49 | let app = Compiler::new() 50 | .zkp_backend::() 51 | .zkp_program(tautology) 52 | .zkp_program(contradiction) 53 | .compile()?; 54 | 55 | let tautology_compiled = app.get_zkp_program(tautology); 56 | let contradiction_compiled = app.get_zkp_program(contradiction); 57 | Ok(()) 58 | } 59 | ``` 60 | 61 | To actually run your compiled ZKP programs, you'll need a runtime! This is 62 | covered in the next chapter. 63 | 64 | [compiler-docs]: https://docs.rs/sunscreen/latest/sunscreen/type.Compiler.html 65 | [application-docs]: https://docs.rs/sunscreen/latest/sunscreen/struct.Application.html 66 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/faq/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ### Why did you create your own ZKP compiler? 4 | We created our own ZKP compiler mainly to ensure compatibility with our FHE compiler—existing ZKP compilers were not designed with FHE's needs in mind. 5 | 6 | ### How will this fit in with Sunscreen's FHE compiler? 7 | Our ZKP compiler is currently offered as a standalone product. 8 | 9 | In the future, Sunscreen's FHE compiler and ZKP compiler will be linked together so that you can prove statements about FHE-encrypted inputs! This is especially important in trustless settings like web3. You will still be able to use either of these offerings independently if desired. 10 | 11 | As part of linking together our FHE and ZKP compiler, we're working on an implementation of a proof system that allows us to (somewhat efficiently) show that FHE ciphertexts are well-formed. This proof system is called [Short Discrete Log Proofs for FHE and Ring-LWE Ciphertexts](https://eprint.iacr.org/2019/057) (SDLP). Once we have linked our FHE compiler with SDLP and SDLP with our ZKP compiler, you'll be able to use our FHE and ZKP compilers together. 12 | 13 | ### Why Bulletproofs as the proof backend? Aren't there more performant proof systems? 14 | As mentioned earlier, our ZKP compiler was designed with the end goal of it being used in conjunction with our FHE compiler. 15 | 16 | Before we can prove arbitrary statements about our FHE-encrypted inputs, we need to prove our FHE ciphertext is well-formed. The proof system we use for this is [Short Discrete Log Proofs for FHE and Ring-LWE Ciphertexts](https://eprint.iacr.org/2019/057) (SDLP); you can track our implementation progress [here](https://github.com/Sunscreen-tech/Sunscreen/tree/main/logproof/src). Both Bulletproofs and SDLP use Pedersen commitments constructed of Ristretto group elements, which allows us to prove secret inputs to both proofs are the same. For this reason, we decided to first start with support of Bulletproofs in our ZKP compiler as it meant we could release something sooner for developers to try out. Additionally, while Bulletproofs is far from being the most performant SNARK, SDLP prover and verifier times dwarf that of Bulletproofs so we have chosen to focus our initial efforts on optimizations for SDLP. 17 | 18 | ### What are your future plans? 19 | Beyond integration with our FHE compiler, we're considering adding support for other proof backends and arithmetizations. 20 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/getting_started/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | This chapter walks through installation. We also provide an overview of Sunscreen's ZKP compiler through a simple example of proving set inclusion (i.e. our number is on a given list of numbers without revealing which one it is). 3 | 4 | If you'd like to try out our ZKP compiler before installing it, check out our [playground](https://playground.sunscreen.tech/). 5 | 6 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/intro/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to ZKPs and our compiler 2 | 3 | ZKPs allow one to prove that a statement is correct 4 | without revealing any information, other than the correctness of the 5 | statement. 6 | 7 | A simple real world example might involve Alice trying to convince Bob the bartender she's over 21 without revealing exactly how old she is. 8 | 9 | ### Why should I care about ZKPs? 10 | 11 | At first, ZKPs may seem paradoxical or totally unrealistic but they are 12 | already out in the wild and have become increasingly popular, particularly for 13 | their ability to provide privacy in transparent systems. 14 | 15 | For example, in Ethereum, every transaction is recorded on a public blockchain. With ZKPs, 16 | users can conduct private transactions (in which the transaction amount and their balance are hidden to outside parties) while only publishing proof of the validity of the transaction (e.g. user shows the hidden amount is less than their hidden balance, the hidden amount is greater than 0). An example of private transactions deployed in practice is [Zcash](https://z.cash/). ZKPs also enable other [use cases in the web3 17 | setting][thirdweb], such as decentralized identity and EVM scalability (via 18 | "zk-rollups"). 19 | 20 | While the above ZKP applications relate to the web3 space, there are interesting applications in web2 as well (e.g. Cloudflare's [attestation of personhood](https://blog.cloudflare.com/introducing-zero-knowledge-proofs-for-private-web-attestation-with-cross-multi-vendor-hardware/)). 21 | 22 | [thirdweb]: https://blog.thirdweb.com/zero-knowledge-proof-zkp/#what-are-some-use-cases-for-zero-knowledge-proofs-zkps-on-the-blockchain 23 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/runtime/runtime.md: -------------------------------------------------------------------------------- 1 | # Runtime 2 | 3 | To create a runtime, you simply call `ZkpRuntime::new`, passing a `ZkpBackend` 4 | reference. Currently, we support Bulletproofs as the only proof backend. 5 | 6 | ```rust 7 | # use sunscreen::{ 8 | # bulletproofs::BulletproofsBackend, 9 | # types::zkp::{BulletproofsField, Field, FieldSpec}, 10 | # zkp_program, zkp_var, Compiler, Error, ZkpRuntime, 11 | # }; 12 | # fn main() -> Result<(), Error> { 13 | let runtime = ZkpRuntime::new(BulletproofsBackend::new())?; 14 | # Ok(()) 15 | # } 16 | ``` 17 | 18 | Once you're created a runtime, you can: 19 | * [make a proof](./prove.md) 20 | * [verify a proof](./verify.md) 21 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/runtime/serialization.md: -------------------------------------------------------------------------------- 1 | # Serialization 2 | 3 | So far, our examples have computed everything in one place. In practice, writing a ZKP program, proving, and verifying are often split among multiple machines. 4 | 5 | In Sunscreen, you can serialize proofs. 6 | 7 | Sunscreen uses [`serde`](https://serde.rs/) for serialization and can serialize data in a 8 | number of formats including JSON and bincode. Since most data in Sunscreen is 9 | high entropy byte arrays, we recommend using 10 | [bincode](https://docs.rs/bincode/latest/bincode/) since it reduces storage and 11 | network requirements by efficiently packing byte arrays. 12 | 13 | Below is an example of serialization and deserialization of a proof: 14 | 15 | ```rust 16 | # use std::io; 17 | # 18 | # use sunscreen::{ 19 | # bulletproofs::BulletproofsBackend, types::zkp::{BulletproofsField, Field, FieldSpec}, 20 | # Compiler, ZkpProgramInput, ZkpRuntime, Proof, zkp_var, zkp_program 21 | # }; 22 | # 23 | # #[zkp_program] 24 | # fn zkp(x: Field, #[public] y: Field) { 25 | # x.constrain_eq(y + zkp_var!(1)); 26 | # } 27 | # 28 | # fn main() -> Result<(), Box> { 29 | # let app = Compiler::new() 30 | # .zkp_backend::() 31 | # .zkp_program(zkp) 32 | # .compile()?; 33 | # let prog = app.get_zkp_program(zkp).unwrap(); 34 | # let runtime = ZkpRuntime::new(BulletproofsBackend::new())?; 35 | # 36 | # let x: BulletproofsField = BulletproofsField::from(1); 37 | # let y: BulletproofsField = BulletproofsField::from(2); 38 | let proof: Proof = runtime.prove(prog, vec![y], vec![x], vec![])?; 39 | let serialized_proof = bincode::serialize(&proof)?; 40 | let deserialized_proof: Proof = bincode::deserialize(&serialized_proof)?; 41 | # Ok(()) 42 | # } 43 | ``` 44 | 45 | As with any dependency, you'll need to add `bincode` as a dependency in your `Cargo.toml`. 46 | 47 | To see how serialization works in practice, check out our [set inclusion 48 | application](../applications/allowlist.md). 49 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/zkp_programs/attributes.md: -------------------------------------------------------------------------------- 1 | # Attributes 2 | 3 | We've already discussed the `#[zkp_program]` 4 | attribute. However, there are also attributes on program _arguments_. 5 | 6 | ```rust 7 | # use sunscreen::{ 8 | # types::zkp::{ConstrainEq, Field, FieldSpec}, zkp_program, zkp_var 9 | # }; 10 | # 11 | #[zkp_program] 12 | fn all_kinds( 13 | #[private] x: Field, 14 | #[public] p: Field, 15 | ) { 16 | // prove that our secret value x is equal to either p or 1 17 | let poly = (x - p) * (x - zkp_var!(1)); 18 | poly.constrain_eq(zkp_var!(0)); 19 | } 20 | ``` 21 | Note that `zkp_program` arguments should appear in the order private, public, and 22 | finally constant to match `prove` call. 23 | 24 | ## Private 25 | 26 | The `#[private]` attribute is used for any arguments that are private to the 27 | prover (i.e. shouldn't be seen by anyone else). In more formal terminology, these arguments are the *witnesses*. For example, this might be a 28 | private key in an encryption scheme or something application specific like an 29 | account balance that you (as the prover) wish to keep hidden. 30 | 31 | This is the default argument type; omitting an attribute is the same as 32 | specifying `#[private]`. 33 | 34 | ## Public 35 | 36 | The `#[public]` attribute is used for any arguments that are public to both the 37 | prover and verifier. For example, this could be the public generator of a group 38 | in some encryption scheme or something application specific like a minimum balance 39 | threshold for issuing transactions. 40 | 41 | ## Constant 42 | 43 | We do not discuss these in the main docs; please see the [advanced 44 | section](../advanced/constant_inputs.md) if you're interested in working with 45 | constant arguments. 46 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/zkp_programs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## How do I debug my program? 4 | 5 | Debugging ZKP programs is currently quite difficult. We recommend you follow standard best coding practices for now. We will soon offer a debugger that should make this process easier. 6 | 7 | ## What the heck is a `ProgramNode`? 8 | 9 | It's a type wrapper needed to compile your ZKP program. Internally, the 10 | `#[zkp_program]` macro turns all your program inputs and outputs into graph 11 | nodes — i.e. `ProgramNodes`. Operator inputs and outputs are actually 12 | `ProgramNode`s, which build up the circuit during compilation. 13 | Unfortunately, they tend to be a leaky abstraction that wind up in error 14 | messages. 15 | 16 | Usually, these errors tell you a `ProgramNode` doesn't support an operation 17 | you're trying to perform. In the example below, the compiler is saying you can't 18 | compare values: 19 | 20 | ```text 21 | error[E0369]: binary operation `>` cannot be applied to type `ProgramNode>` 22 | --> examples/ordering_zkp/src/main.rs:13:15 23 | | 24 | 13 | let b = x > y; 25 | | - ^ - ProgramNode> 26 | | | 27 | | ProgramNode> 28 | ``` 29 | 30 | This can also crop up when using explicit annotations. For example, the 31 | following will fail to compile: 32 | 33 | ```rust,no_run,compile_fail 34 | use sunscreen::{zkp_program, zkp_var, types::zkp::{Field, FieldSpec}}; 35 | #[zkp_program] 36 | fn either( 37 | x: Field, 38 | y: Field, 39 | ) { 40 | let diff: Field = x - y; // This type annotation isn't correct! 41 | diff.constrain_eq(zkp_var!(0)); 42 | } 43 | ``` 44 | 45 | Unnecessary type annotations are unidiomatic and thus we advise against them. 46 | Usually, type inference is sufficient, but if you really need one you can import 47 | and use `sunscreen::types::zkp::ProgramNode`. 48 | 49 | ```rust 50 | use sunscreen::{zkp_program, zkp_var, types::zkp::{Field, FieldSpec, ProgramNode}}; 51 | #[zkp_program] 52 | fn either( 53 | x: Field, 54 | y: Field, 55 | ) { 56 | let diff: ProgramNode> = x - y; // Fixed! 57 | diff.constrain_eq(zkp_var!(0)); 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /sunscreen_docs/src/zkp/zkp_programs/zkp_programs.md: -------------------------------------------------------------------------------- 1 | # What's in a ZKP program? 2 | 3 | If you've already read through [our FHE compiler documentation](https://bfv-docs.sunscreen.tech/), you'll find that ZKPs 4 | are, in many ways, much simpler. This is perhaps unsurprising; whereas FHE 5 | enables general purpose private computation, ZKPs have a narrower purpose. For 6 | example, ZKP programs don't even have return values! 7 | 8 | This section describes the anatomy of a ZKP program, what you can and can't do, 9 | and the different types involved in ZKP programs. 10 | 11 | 12 | ## ZKP program interface requirements 13 | 14 | ZKP programs implement their logic in the `fn` function beneath the 15 | `#[zkp_program]` attribute. 16 | 17 | The `fn` function you write has to satisfy the following 18 | conditions: 19 | * Must be stand-alone (i.e. not a `struct` method, closure, `trait` method, etc). 20 | * Must take one generic param `F: FieldSpec`. 21 | * May take any number of arguments, but each type must be [supported](./types.md). 22 | * May *not* have any return values. 23 | 24 | ## Operations 25 | 26 | Some aspects of writing ZKP programs may be surprising to you if you're not familiar with [R1CS constraints](https://learn.0xparc.org/materials/circom/additional-learning-resources/r1cs%20explainer/) at a high level. Not to worry, we'll explain how this plays into the operations available to you. 27 | 28 | In ZKP programs, you can: 29 | 30 | - Perform basic arithmetic operations (`+`, `-`, `*`)—specially only those native to R1CS. Integer division (with remainder) and field inversion are not directly possible since these operations are *not* native to R1CS, but can be implemented via a [gadget](../advanced/gadgets.md). 31 | - Add [constraints](./constraints.md) to enforce that certain mathematical statements hold in the proof. 32 | - Call other functions. 33 | - Use any Rust construct (e.g. `match`, `for i in ...`, `if...else`) on data 34 | *not* derived from any argument. We walk through a number of examples in the 35 | [limitations](./limitations.md) chapter. 36 | -------------------------------------------------------------------------------- /sunscreen_fhe_program/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_fhe_program" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate provides the data format for Sunscreen FHE programs." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [dependencies] 20 | petgraph = { workspace = true } 21 | serde = { workspace = true } 22 | seal_fhe = { workspace = true } 23 | static_assertions = { workspace = true } 24 | sunscreen_compiler_common = { workspace = true } 25 | thiserror = { workspace = true } 26 | 27 | [dev-dependencies] 28 | serde_json = { workspace = true } 29 | -------------------------------------------------------------------------------- /sunscreen_fhe_program/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). -------------------------------------------------------------------------------- /sunscreen_fhe_program/src/literal.rs: -------------------------------------------------------------------------------- 1 | use core::hash::Hash; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)] 5 | /** 6 | * Represents a literal value in an expression. 7 | */ 8 | pub enum Literal { 9 | /** 10 | * An unsigned 64-bit integer. 11 | */ 12 | U64(u64), 13 | 14 | /** 15 | * A plaintext stored as a sequence of bytes. 16 | */ 17 | Plaintext(Vec), 18 | } 19 | 20 | impl From for Literal { 21 | fn from(val: u64) -> Self { 22 | Self::U64(val) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sunscreen_math/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_math" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.67.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate contains GPU implementations that support the Sunscreen compiler." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | [dependencies] 18 | bytemuck = { workspace = true, optional = true } 19 | lazy_static = { workspace = true } 20 | metal = { workspace = true, optional = true } 21 | rand = { workspace = true } 22 | curve25519-dalek = { workspace = true } 23 | rayon = { workspace = true } 24 | ocl = { workspace = true, optional = true } 25 | futures = { workspace = true, optional = true } 26 | tokio = { workspace = true, optional = true } 27 | wgpu = { workspace = true, optional = true } 28 | cust = { workspace = true, optional = true } 29 | num = { workspace = true } 30 | crypto-bigint = { workspace = true } 31 | thiserror = { workspace = true } 32 | serde = { workspace = true } 33 | sunscreen_math_macros = { workspace = true } 34 | subtle = { workspace = true } 35 | log.workspace = true 36 | statrs = "0.16.0" 37 | 38 | [build-dependencies] 39 | naga = { workspace = true, optional = true } 40 | wgpu-core = { workspace = true, optional = true } 41 | ocl = { workspace = true, optional = true } 42 | find_cuda_helper = { workspace = true, optional = true } 43 | 44 | [dev-dependencies] 45 | bytemuck = { workspace = true } 46 | criterion = { workspace = true } 47 | 48 | [features] 49 | default = [] 50 | nightly-features = [] 51 | cuda = ["dep:find_cuda_helper", "dep:cust", "gpu"] 52 | metal = ["dep:metal", "gpu"] 53 | webgpu = [ 54 | "dep:wgpu", 55 | "dep:tokio", 56 | "dep:futures", 57 | "dep:naga", 58 | "dep:wgpu-core", 59 | "dep:bytemuck", 60 | "gpu", 61 | ] 62 | opencl = ["dep:ocl", "gpu"] 63 | gpu = [] 64 | pina = [] 65 | 66 | [[bench]] 67 | name = "gpu" 68 | harness = false 69 | 70 | [[bench]] 71 | name = "cpu" 72 | harness = false 73 | -------------------------------------------------------------------------------- /sunscreen_math/benches/cpu.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | use curve25519_dalek::scalar::Scalar; 5 | use rand::thread_rng; 6 | use sunscreen_math::CpuScalarVec; 7 | 8 | fn invert(_c: &mut Criterion) { 9 | println!("Invert scalars"); 10 | 11 | let a = (0..(256 * 1024)) 12 | .map(|_| Scalar::random(&mut thread_rng())) 13 | .collect::>(); 14 | 15 | let a_vec = CpuScalarVec::new(&a); 16 | 17 | let now = Instant::now(); 18 | let _ = a_vec.invert(); 19 | println!( 20 | "{} inversions/s", 21 | a.len() as f64 / now.elapsed().as_secs_f64() 22 | ); 23 | } 24 | 25 | criterion_group!(benches, invert); 26 | criterion_main!(benches); 27 | -------------------------------------------------------------------------------- /sunscreen_math/benches/gpu.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "gpu")] 2 | use criterion::{criterion_group, criterion_main}; 3 | 4 | #[cfg(feature = "gpu")] 5 | mod benches { 6 | use criterion::Criterion; 7 | use curve25519_dalek::{ristretto::RistrettoPoint, scalar::Scalar}; 8 | use rand::thread_rng; 9 | use std::time::Instant; 10 | use sunscreen_math::{GpuRistrettoPointVec, GpuScalarVec}; 11 | 12 | pub fn invert(_c: &mut Criterion) { 13 | println!("Invert scalars"); 14 | 15 | let a = (0..(256 * 1024)) 16 | .map(|_| Scalar::random(&mut thread_rng())) 17 | .collect::>(); 18 | 19 | let a_vec = GpuScalarVec::new(&a); 20 | // Do it once to cache JIT 21 | let _ = a_vec.invert(); 22 | 23 | // Time it for real. 24 | let now = Instant::now(); 25 | let _ = a_vec.invert(); 26 | println!( 27 | "{} inversions/s", 28 | a.len() as f64 / now.elapsed().as_secs_f64() 29 | ); 30 | } 31 | 32 | pub fn scalar_mul(_c: &mut Criterion) { 33 | println!("Scalar multiplication"); 34 | 35 | let a = (0..(256 * 1024)) 36 | .map(|_| RistrettoPoint::random(&mut thread_rng())) 37 | .collect::>(); 38 | 39 | let b = (0..(256 * 1024)) 40 | .map(|_| Scalar::random(&mut thread_rng())) 41 | .collect::>(); 42 | 43 | let a_vec = GpuRistrettoPointVec::new(&a); 44 | let b_vec = GpuScalarVec::new(&b); 45 | 46 | loop { 47 | let now = Instant::now(); 48 | let _ = &a_vec * &b_vec; 49 | println!("{} sm/s", a.len() as f64 / now.elapsed().as_secs_f64()); 50 | } 51 | } 52 | } 53 | 54 | #[cfg(feature = "gpu")] 55 | criterion_group!(benches, benches::invert, benches::scalar_mul); 56 | 57 | #[cfg(feature = "gpu")] 58 | criterion_main!(benches); 59 | 60 | #[cfg(not(feature = "gpu"))] 61 | fn main() {} 62 | -------------------------------------------------------------------------------- /sunscreen_math/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). -------------------------------------------------------------------------------- /sunscreen_math/src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, thiserror::Error)] 2 | /// Errors that can occur in this crate. 3 | pub enum Error { 4 | #[error("The given value is out of range.")] 5 | /// The given value is out of range. 6 | OutOfRange, 7 | } 8 | -------------------------------------------------------------------------------- /sunscreen_math/src/field/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::ring::Ring; 2 | 3 | /// A trait for fields. 4 | pub trait Field: Ring { 5 | /// Compute the inverse of `self` 6 | fn inverse(&self) -> Self; 7 | } 8 | 9 | /// A marker trait denoting this configuration is for a field 10 | pub trait FieldConfig {} 11 | -------------------------------------------------------------------------------- /sunscreen_math/src/geometry.rs: -------------------------------------------------------------------------------- 1 | /// A 2D point. 2 | #[derive(Debug, Clone, Copy)] 3 | pub struct Point2D { 4 | x: f64, 5 | y: f64, 6 | } 7 | 8 | impl Point2D { 9 | /// Create a new 2D point. 10 | pub fn new(x: f64, y: f64) -> Self { 11 | Self { x, y } 12 | } 13 | 14 | /// The x-coordinate of the point. 15 | pub fn x(&self) -> f64 { 16 | self.x 17 | } 18 | 19 | /// The y-coordinate of the point. 20 | pub fn y(&self) -> f64 { 21 | self.y 22 | } 23 | } 24 | 25 | /// A half-space in 2D space. It is defined by a normal vector `a` and a scalar 26 | /// `b`. The half-space is the set of points `x` such that `a * x <= b`. 27 | #[derive(Debug, Clone, Copy)] 28 | pub struct HalfSpace2D { 29 | a: (f64, f64), 30 | b: f64, 31 | } 32 | 33 | impl HalfSpace2D { 34 | /// Create a new half-space. 35 | pub fn new(a: (f64, f64), b: f64) -> Self { 36 | Self { a, b } 37 | } 38 | 39 | /// Is a point inside the half-space? 40 | pub fn inside(&self, point: Point2D) -> bool { 41 | self.a.0 * point.x + self.a.1 * point.y <= self.b 42 | } 43 | } 44 | 45 | /// A convex polytope in 2D space. It is defined by a set of half-spaces. There 46 | /// is no function to convert to the vertices representation. 47 | #[derive(Debug, Clone)] 48 | pub struct ConvexPolytope2D { 49 | pub(crate) half_spaces: Vec, 50 | } 51 | 52 | impl ConvexPolytope2D { 53 | /// Create a new convex polytope. 54 | pub fn new(half_spaces: &[HalfSpace2D]) -> Self { 55 | Self { 56 | half_spaces: half_spaces.to_owned(), 57 | } 58 | } 59 | 60 | /// Is a point inside the polytope? 61 | pub fn inside(&self, point: Point2D) -> bool { 62 | self.half_spaces 63 | .iter() 64 | .all(|half_space| half_space.inside(point)) 65 | } 66 | 67 | /// The half-spaces that a point violates. 68 | pub fn violations(&self, point: Point2D) -> Vec { 69 | self.half_spaces 70 | .iter() 71 | .filter_map(|half_space| { 72 | if half_space.inside(point) { 73 | None 74 | } else { 75 | Some(*half_space) 76 | } 77 | }) 78 | .collect() 79 | } 80 | 81 | /// The half-spaces of the polytope. 82 | pub fn half_spaces(&self) -> &[HalfSpace2D] { 83 | &self.half_spaces 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /sunscreen_math/src/metal_impl/shaders/include/constants.hpp.metal: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace constants { 8 | constant const u32 _L[9] = { 9 | 0x1cf5d3ed, 0x009318d2, 0x1de73596, 0x1df3bd45, 0x0000014d, 0x00000000, 0x00000000, 0x00000000, 10 | 0x00100000, 11 | }; 12 | 13 | /// `L` is the order of base point, i.e. 2^252 + 14 | /// 27742317777372353535851937790883648493 15 | constant const Scalar29 L(_L); 16 | 17 | /// `L` * `LFACTOR` = -1 (mod 2^29) 18 | constant const u32 LFACTOR = 0x12547e1b; 19 | 20 | constant const u32 _RR[9] = { 21 | 0x0b5f9d12, 0x1e141b17, 0x158d7f3d, 0x143f3757, 0x1972d781, 0x042feb7c, 0x1ceec73d, 0x1e184d1e, 22 | 0x0005046d 23 | }; 24 | 25 | constant const Scalar29 RR = Scalar29(_RR); 26 | 27 | constant const u32 _EDWARDS_D2[10] = { 28 | 45281625, 27714825, 36363642, 13898781, 229458, 15978800, 54557047, 27058993, 29715967, 9444199, 29 | }; 30 | 31 | /// Edwards `2*d` value, equal to `2*(-121665/121666) mod p`. 32 | constant const FieldElement2625 EDWARDS_D2(_EDWARDS_D2); 33 | } -------------------------------------------------------------------------------- /sunscreen_math/src/metal_impl/shaders/include/field.hpp.metal: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct u64_10 { 6 | u64 data[10]; 7 | 8 | inline thread u64& operator[](const size_t i) thread { 9 | return data[i]; 10 | } 11 | inline const thread u64& operator[](const size_t i) const thread { 12 | return data[i]; 13 | } 14 | }; 15 | 16 | class FieldElement2625 { 17 | private: 18 | u32 _limbs[10]; 19 | 20 | u64_10 square_inner() const; 21 | 22 | public: 23 | FieldElement2625() { } 24 | 25 | static const constant FieldElement2625 ONE; 26 | static const constant FieldElement2625 ZERO; 27 | 28 | FieldElement2625(constant const u32 limbs[10]): _limbs{limbs[0], limbs[1], limbs[2], limbs[3], limbs[4], limbs[5], limbs[6], limbs[7], limbs[8], limbs[9]} { } 29 | 30 | FieldElement2625(thread const u32 limbs[10]): _limbs{limbs[0], limbs[1], limbs[2], limbs[3], limbs[4], limbs[5], limbs[6], limbs[7], limbs[8], limbs[9]} { } 31 | 32 | /// Loads the value at grid_tid from an `10 x n` row-major u32 matrix. `n` is the length 33 | /// of the Scalar array. 34 | /// 35 | /// # Remarks 36 | /// Each thread should pass the same base address. 37 | /// 38 | /// When reach thread in a group executes this 39 | /// function with a consecutive grid_tid, 40 | /// unpacking is fully coalesced. 41 | static FieldElement2625 unpack(device const u32* ptr, const size_t grid_tid, const size_t n); 42 | 43 | /// Packs this value into an `10 x n` row-major 44 | /// u32 matrix. 45 | /// 46 | /// # Remarks 47 | /// Each thread should pass the same base address. 48 | /// 49 | /// When reach thread in a group executes this 50 | /// function with a consecutive grid_tid, 51 | /// unpacking is fully coalesced. 52 | void pack(device u32* ptr, size_t grid_tid, size_t n); 53 | 54 | FieldElement2625 operator+(const thread FieldElement2625& rhs) const thread; 55 | FieldElement2625 operator-(const thread FieldElement2625& rhs) const thread; 56 | FieldElement2625 operator*(const thread FieldElement2625& rhs) const thread; 57 | 58 | FieldElement2625 operator-() const thread; 59 | 60 | /// Compute `this^2`. 61 | FieldElement2625 square() const; 62 | 63 | /// Compute `2*this^2`. 64 | FieldElement2625 square2() const; 65 | 66 | inline thread const u32& operator[](const size_t i) const thread { 67 | return _limbs[i]; 68 | } 69 | 70 | inline thread u32& operator[](const size_t i) thread { 71 | return _limbs[i]; 72 | } 73 | }; -------------------------------------------------------------------------------- /sunscreen_math/src/metal_impl/shaders/include/inttypes.hpp.metal: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef uint32_t u32; 4 | typedef uint64_t u64; 5 | typedef int8_t i8; 6 | typedef uint8_t u8; 7 | typedef int16_t i16; -------------------------------------------------------------------------------- /sunscreen_math/src/metal_impl/shaders/include/lookuptable.hpp.metal: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | using namespace metal; 6 | 7 | // Note: N must be >= 1. 8 | template class LookupTable { 9 | private: 10 | ProjectiveNielsPoint _entries[N]; 11 | 12 | public: 13 | LookupTable(const thread RistrettoPoint& p) { 14 | _entries[0] = p.as_projective_niels(); 15 | 16 | for (size_t i = 1; i < N; i++) { 17 | _entries[i] = (p + _entries[i - 1]).as_extended().as_projective_niels(); 18 | } 19 | } 20 | 21 | // TODO: Eventually make this non vartime. Or not, as Sunscreen doesn't require it. 22 | thread ProjectiveNielsPoint select(i8 x) { 23 | ProjectiveNielsPoint ret = ProjectiveNielsPoint::IDENTITY; 24 | 25 | size_t idx = abs(x); 26 | 27 | ret = x > 0 ? _entries[idx - 1] : ret; 28 | ret = x < 0 ? -_entries[idx - 1] : ret; 29 | 30 | return ret; 31 | } 32 | }; -------------------------------------------------------------------------------- /sunscreen_math/src/metal_impl/shaders/lookuptable.metal: -------------------------------------------------------------------------------- 1 | #if defined(TEST) 2 | #include 3 | #include 4 | 5 | kernel void test_lut( 6 | u32 tid [[thread_position_in_grid]], 7 | device const u32* a [[buffer(0)]], 8 | device u32* b0 [[buffer(1)]], 9 | device u32* b1 [[buffer(2)]], 10 | device u32* b2 [[buffer(3)]], 11 | device u32* b3 [[buffer(4)]], 12 | device u32* b4 [[buffer(5)]], 13 | device u32* b5 [[buffer(6)]], 14 | device u32* b6 [[buffer(7)]], 15 | device u32* b7 [[buffer(8)]], 16 | constant u32& len [[buffer(9)]] 17 | ) { 18 | auto x = RistrettoPoint::unpack(a, tid, len); 19 | 20 | LookupTable<4> lut(x); 21 | 22 | auto identity = RistrettoPoint::IDENTITY; 23 | 24 | auto _b0 = lut.select(0); 25 | auto _b1 = lut.select(1); 26 | auto _b2 = lut.select(2); 27 | auto _b3 = lut.select(3); 28 | auto _b4 = lut.select(-1); 29 | auto _b5 = lut.select(-2); 30 | auto _b6 = lut.select(-3); 31 | auto _b7 = lut.select(-4); 32 | 33 | // Add identity to coerse the point to completed so we can project back to RistrettoPoint. 34 | (identity + _b0).as_extended().pack(b0, tid, len); 35 | (identity + _b1).as_extended().pack(b1, tid, len); 36 | (identity + _b2).as_extended().pack(b2, tid, len); 37 | (identity + _b3).as_extended().pack(b3, tid, len); 38 | (identity + _b4).as_extended().pack(b4, tid, len); 39 | (identity + _b5).as_extended().pack(b5, tid, len); 40 | (identity + _b6).as_extended().pack(b6, tid, len); 41 | (identity + _b7).as_extended().pack(b7, tid, len); 42 | } 43 | 44 | #endif -------------------------------------------------------------------------------- /sunscreen_math/src/misc_traits.rs: -------------------------------------------------------------------------------- 1 | use curve25519_dalek::scalar::Scalar; 2 | 3 | /// For an algebraic structure, this defines the additive identity. 4 | pub trait Zero 5 | where 6 | Self: Sized, 7 | { 8 | /// The additive identity. 9 | fn zero() -> Self; 10 | 11 | /// Whether or not this item is zero. 12 | fn vartime_is_zero(&self) -> bool; 13 | } 14 | 15 | /// For an algebraic structure, this defines the multiplicative identity. 16 | pub trait One 17 | where 18 | Self: Sized, 19 | { 20 | /// The multiplicative identity. 21 | fn one() -> Self; 22 | } 23 | 24 | impl Zero for Scalar { 25 | fn zero() -> Self { 26 | Self::zero() 27 | } 28 | 29 | fn vartime_is_zero(&self) -> bool { 30 | self == &Scalar::zero() 31 | } 32 | } 33 | 34 | impl One for Scalar { 35 | fn one() -> Self { 36 | Self::one() 37 | } 38 | } 39 | 40 | /// Methods for switching elements between finite rings. 41 | pub trait ModSwitch { 42 | /// Treat the input value as unsigned in the current Ring and produce 43 | /// the same unsigned value in the ring `R`. 44 | fn mod_switch_unsigned(&self) -> R; 45 | 46 | /// Treat the input value as signed in the current field 47 | /// (i.e. [-q/2, q/2]) and produce the same signed value in `R` 48 | /// (i.e. [-p/2, p/2]). 49 | fn mod_switch_signed(&self) -> R; 50 | } 51 | 52 | pub use num::traits::ToBytes; 53 | -------------------------------------------------------------------------------- /sunscreen_math/src/opencl_impl/multiexp.md: -------------------------------------------------------------------------------- 1 | What remains: 2 | * In bucket point matrix, sort buckets in descending order rather than ascending as is done today. 3 | * Modify prefix sum to allow specifying inclusive or exclusive. 4 | * Perform an inclusive prefix sum on buckets (today, this is exclusive). 5 | * Perform an inclusive prefix sum on said prefix sum. Since we're now sorting largest to smallest, this should scale each bucket point by its bucket factor and produce the window total in the final bucket. 6 | 7 | 8 | # Example 9 | Let P_i be the previously computed sum of all points in bucket i. 10 | 11 | | 3 | 2 | 1 | 0 | 12 | |----|----|----|----| 13 | | P3 | P2 | P1 | P0 | 14 | 15 | Computing an inclusive Prefix sum gives us 16 | 17 | | 3 | 2 | 1 | 0 | 18 | |----|---------|--------------|-------------------| 19 | | P3 | P3 + P2 | P3 + P2 + P1 | P3 + P2 + P1 + P0 | 20 | 21 | Computing an inclusive prefix sum again gives us 22 | 23 | | 3 | 2 | 1 | 0 | 24 | |----|-----------|----------------------|-------------------------------| 25 | | P3 | 2*P3 + P2 | 3 * P3 + 2 * P2 + P1 | 4 * P3 + 3 * P2 + 2 * P1 + P0 | 26 | 27 | Note that the entry in bucket 1 has each point correctly scaled by it's bucket scalar! -------------------------------------------------------------------------------- /sunscreen_math/src/opencl_impl/shaders/constants.cl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const constant Scalar29 Scalar29_Zero = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; 4 | const constant Scalar29 Scalar29_L = 5 | {{ 6 | 0x1cf5d3ed, 0x009318d2, 0x1de73596, 0x1df3bd45, 7 | 0x0000014d, 0x00000000, 0x00000000, 0x00000000, 8 | 0x00100000 9 | }}; 10 | 11 | const constant Scalar29 Scalar29_RR = {{ 12 | 0x0b5f9d12, 0x1e141b17, 0x158d7f3d, 0x143f3757, 13 | 0x1972d781, 0x042feb7c, 0x1ceec73d, 0x1e184d1e, 14 | 0x0005046d 15 | }}; 16 | 17 | const constant FieldElement2625 FieldElement2625_EDWARDS_D2 = {{ 18 | 45281625, 27714825, 36363642, 13898781, 229458, 15978800, 54557047, 27058993, 29715967, 9444199, 19 | }}; -------------------------------------------------------------------------------- /sunscreen_math/src/opencl_impl/shaders/include/constants.h.cl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define FieldElement2625_ZERO {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} 8 | #define FieldElement2625_ONE {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}} 9 | 10 | extern const constant Scalar29 Scalar29_Zero; 11 | extern const constant Scalar29 Scalar29_L; 12 | extern const constant Scalar29 Scalar29_RR; 13 | extern const constant Scalar29 Scalar29_Zero; 14 | extern const constant Scalar29 Scalar29_L; 15 | extern const constant Scalar29 Scalar29_RR; 16 | extern const constant FieldElement2625 FieldElement2625_EDWARDS_D2; 17 | extern const constant RistrettoPoint RistrettoPoint_IDENTITY; -------------------------------------------------------------------------------- /sunscreen_math/src/opencl_impl/shaders/include/field2625.h.cl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | u32 limbs[10]; 7 | } FieldElement2625; 8 | 9 | typedef struct { 10 | u64 data[10]; 11 | } U64_10; 12 | 13 | FieldElement2625 FieldElement2625_unpack(const global u32* words, size_t grid_tid, size_t stride); 14 | FieldElement2625 FieldElement2625_unpack_local(const local u32* words, size_t grid_tid, size_t stride); 15 | void FieldElement2625_pack(const FieldElement2625* a, global u32* ptr, const size_t grid_tid, const size_t n); 16 | void FieldElement2625_pack_local(const FieldElement2625* a, local u32* ptr, const size_t grid_tid, const size_t n); 17 | FieldElement2625 FieldElement2625_add(const FieldElement2625* a, const FieldElement2625* b); 18 | FieldElement2625 FieldElement2625_reduce(private U64_10* val); 19 | FieldElement2625 FieldElement2625_sub(const FieldElement2625* lhs, const FieldElement2625* rhs); 20 | FieldElement2625 FieldElement2625_mul(const FieldElement2625* lhs, const FieldElement2625* rhs); 21 | FieldElement2625 FieldElement2625_neg(const FieldElement2625* lhs); 22 | U64_10 FieldElement2625_square_inner(const FieldElement2625* val); 23 | FieldElement2625 FieldElement2625_square(const FieldElement2625* val); 24 | FieldElement2625 FieldElement2625_square2(const FieldElement2625* val); 25 | -------------------------------------------------------------------------------- /sunscreen_math/src/opencl_impl/shaders/include/inttypes.h.cl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef uint u32; 4 | typedef ulong u64; 5 | typedef char i8; 6 | typedef uchar u8; 7 | typedef short i16; 8 | 9 | inline u64 m(u32 a, u32 b); 10 | -------------------------------------------------------------------------------- /sunscreen_math/src/opencl_impl/shaders/include/scalar29.h.cl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | u32 limbs[10]; 7 | } Scalar29; 8 | 9 | typedef struct { 10 | i8 data[64]; 11 | } Radix16; 12 | 13 | typedef struct { 14 | ulong limbs[17]; 15 | } MulResult; 16 | 17 | typedef struct { 18 | u64 carry; 19 | u32 n; 20 | } MontMulLRes; 21 | 22 | Scalar29 Scalar29_add(const Scalar29* lhs, const Scalar29* rhs); 23 | Scalar29 Scalar29_sub(const Scalar29* lhs, const Scalar29* rhs); 24 | Scalar29 Scalar29_mul(const Scalar29* a, const Scalar29* b); 25 | void Scalar29_pack(const Scalar29* this, global u32* words, size_t grid_tid, size_t stride); 26 | MulResult Scalar29_square_internal(const Scalar29* a); 27 | MulResult Scalar29_mul_internal(const Scalar29* a, const Scalar29* b); 28 | Scalar29 Scalar29_montgomery_reduce(MulResult* limbs); 29 | Scalar29 Scalar29_unpack(const global u32* words, size_t grid_tid, size_t stride); 30 | Scalar29 Scalar29_montgomery_square(const Scalar29* x); 31 | Scalar29 Scalar29_montgomery_mul(const Scalar29* a, const Scalar29* b); 32 | void Scalar29_square_multiply(volatile Scalar29* y, int squarings, const Scalar29* x); 33 | Scalar29 Scalar29_to_montgomery(const Scalar29* val); 34 | Scalar29 Scalar29_from_montgomery(const Scalar29* val); 35 | Scalar29 Scalar29_invert(const Scalar29* a); 36 | Scalar29 Scalar29_montgomery_invert(const Scalar29* this); 37 | Scalar29 Scalar29_square(const Scalar29* val); 38 | Radix16 Scalar29_as_radix_16(const Scalar29* this); -------------------------------------------------------------------------------- /sunscreen_math/src/opencl_impl/shaders/inttypes.cl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | inline u64 m(u32 a, u32 b) { 4 | return (u64)a * (u64)b; 5 | } -------------------------------------------------------------------------------- /sunscreen_math/src/opencl_impl/shaders/main.cl: -------------------------------------------------------------------------------- 1 | #include "constants.cl" 2 | #include "field2625.cl" 3 | #include "inttypes.cl" 4 | #include "multiexp.cl" 5 | #include "ristrettopoint.cl" 6 | #include "scalar29.cl" 7 | #include "radix_sort.cl" 8 | #include "rle.cl" 9 | #include "ristrettopoint_prefixsum.cl" -------------------------------------------------------------------------------- /sunscreen_math/src/stats.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * Running mean and variance calculation. 3 | */ 4 | #[derive(Debug, PartialEq, Default, Clone, Copy)] 5 | pub struct RunningMeanVariance { 6 | mean: f64, 7 | variance: f64, 8 | k: usize, 9 | } 10 | 11 | impl RunningMeanVariance { 12 | /** 13 | * Create a new running mean and variance calculator. 14 | */ 15 | pub fn new() -> Self { 16 | Self { 17 | mean: 0.0, 18 | variance: 0.0, 19 | k: 0, 20 | } 21 | } 22 | 23 | /** 24 | * Add a sample to the running mean and variance calculator. 25 | */ 26 | pub fn add_sample(&mut self, x: f64) { 27 | if self.k == 0 { 28 | self.mean = x; 29 | self.variance = 0.0; 30 | 31 | self.k += 1; 32 | } else { 33 | // https://math.stackexchange.com/a/116344 34 | self.k += 1; 35 | 36 | let new_mean = self.mean + (x - self.mean) / (self.k as f64); 37 | let new_variance = self.variance + (x - self.mean) * (x - new_mean); 38 | 39 | self.mean = new_mean; 40 | self.variance = new_variance; 41 | } 42 | } 43 | 44 | /** 45 | * Get the mean of the samples. 46 | */ 47 | pub fn mean(&self) -> f64 { 48 | self.mean 49 | } 50 | 51 | /** 52 | * Get the variance of the samples. 53 | */ 54 | pub fn variance(&self) -> f64 { 55 | self.variance / (self.k as f64) 56 | } 57 | 58 | /** 59 | * Get the standard deviation of the samples. 60 | */ 61 | pub fn std(&self) -> f64 { 62 | self.variance().sqrt() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sunscreen_math/src/webgpu_impl/ristrettovec.rs: -------------------------------------------------------------------------------- 1 | pub struct GpuRistrettoPointVec {} 2 | -------------------------------------------------------------------------------- /sunscreen_math/src/webgpu_impl/shaders/basic.test.wgsl: -------------------------------------------------------------------------------- 1 | @compute 2 | @workgroup_size(128, 1, 1) 3 | fn add( 4 | @builtin(global_invocation_id) gid: vec3, 5 | ) { 6 | if gid.x < g_len { 7 | g_c[gid.x] = g_a[gid.x] + g_b[gid.x]; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sunscreen_math/src/webgpu_impl/shaders/bindings.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var g_a : array; 2 | @group(0) @binding(1) var g_b : array; 3 | @group(0) @binding(2) var g_c : array; 4 | @group(0) @binding(3) var g_len : u32; 5 | 6 | /// This stupid function exists to force a usage on binding b (needed for unary ops). 7 | fn unused_b() { 8 | if false { 9 | let x = g_b[0]; 10 | } 11 | } -------------------------------------------------------------------------------- /sunscreen_math/src/webgpu_impl/shaders/constants.wgsl: -------------------------------------------------------------------------------- 1 | const Scalar29_Zero: Scalar29 = Scalar29(array(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)); 2 | 3 | const Scalar29_L: Scalar29 = Scalar29(array( 4 | 0x1cf5d3edu, 0x009318d2u, 0x1de73596u, 0x1df3bd45u, 0x0000014du, 0x00000000u, 0x00000000u, 0x00000000u, 5 | 0x00100000u, 6 | )); 7 | 8 | const Scalar29_RR: Scalar29 = Scalar29(array( 9 | 0x0b5f9d12u, 0x1e141b17u, 0x158d7f3du, 0x143f3757u, 0x1972d781u, 0x042feb7cu, 0x1ceec73du, 0x1e184d1eu, 10 | 0x0005046du 11 | )); 12 | 13 | const Scalar29_LFACTOR: u32 = 0x12547e1bu; -------------------------------------------------------------------------------- /sunscreen_math/src/webgpu_impl/shaders/u64.test.wgsl: -------------------------------------------------------------------------------- 1 | @compute 2 | @workgroup_size(128, 1, 1) 3 | fn test_wide_mul( 4 | @builtin(global_invocation_id) gid: vec3, 5 | ) { 6 | if gid.x >= g_len { 7 | return; 8 | } 9 | 10 | let a = g_a[gid.x]; 11 | let b = g_b[gid.x]; 12 | 13 | let c = mul_wide(a, b); 14 | 15 | g_c[gid.x] = c.lo; 16 | g_c[g_len + gid.x] = c.hi; 17 | } 18 | 19 | @compute 20 | @workgroup_size(128, 1, 1) 21 | fn test_u64_add( 22 | @builtin(global_invocation_id) gid: vec3, 23 | ) { 24 | if gid.x >= g_len { 25 | return; 26 | } 27 | 28 | let a = u64(g_a[gid.x], g_a[gid.x + g_len]); 29 | let b = u64(g_b[gid.x], g_b[gid.x + g_len]); 30 | 31 | let c = u64_add(a, b); 32 | 33 | g_c[gid.x] = c.lo; 34 | g_c[g_len + gid.x] = c.hi; 35 | } 36 | 37 | @compute 38 | @workgroup_size(128, 1, 1) 39 | fn test_u64_sub( 40 | @builtin(global_invocation_id) gid: vec3, 41 | ) { 42 | if gid.x >= g_len { 43 | return; 44 | } 45 | 46 | let a = u64(g_a[gid.x], g_a[gid.x + g_len]); 47 | let b = u64(g_b[gid.x], g_b[gid.x + g_len]); 48 | 49 | let c = u64_sub(a, b); 50 | 51 | g_c[gid.x] = c.lo; 52 | g_c[g_len + gid.x] = c.hi; 53 | } 54 | 55 | @compute 56 | @workgroup_size(128, 1, 1) 57 | fn test_u64_shr( 58 | @builtin(global_invocation_id) gid: vec3, 59 | ) { 60 | if gid.x >= g_len { 61 | return; 62 | } 63 | 64 | let a = u64(g_a[gid.x], g_a[gid.x + g_len]); 65 | let b = g_b[gid.x]; 66 | 67 | let c = u64_shr(a, b); 68 | 69 | g_c[gid.x] = c.lo; 70 | g_c[g_len + gid.x] = c.hi; 71 | } -------------------------------------------------------------------------------- /sunscreen_math/src/webgpu_impl/shaders/u64.wgsl: -------------------------------------------------------------------------------- 1 | struct u64 { 2 | lo: u32, 3 | hi: u32 4 | } 5 | 6 | fn mul_wide(a: u32, b: u32) -> u64 { 7 | // Break a and b into 16-bit words. 8 | // Compute product as 9 | // a b 10 | // * c d 11 | // ======= 12 | // d*b 13 | // d*a 14 | // c*b 15 | // c*a 16 | // 17 | // Note that the low 16-bit words of d*a and c*b overlap the high word 18 | // of d*b. Similarly, the low 16-bit word of these overlaps c*a, so we 19 | // need to do shifting to align things properly. 20 | 21 | let a_lo = a & 0xFFFFu; 22 | let b_lo = b & 0xFFFFu; 23 | let a_hi = a >> 16u; 24 | let b_hi = b >> 16u; 25 | 26 | // The product of 2 16-bit words will fit in 32 bits 27 | let db = a_lo * b_lo; 28 | let ad = a_hi * b_lo; 29 | let cb = a_lo * b_hi; 30 | let ac = a_hi * b_hi; 31 | 32 | let ad_shift = ad << 16u; 33 | let cb_shift = cb << 16u; 34 | 35 | // When computing the sum of the low word components, carries can 36 | // occur in each addition. A carry occurs in unsigned arithmetic 37 | // if sum < either operand. 38 | let dbad = db + ad_shift; 39 | var carry1 = u32(dbad < db); 40 | let lo = dbad + cb_shift; 41 | var carry2 = u32(lo < dbad); 42 | 43 | // For the high word, we add the carries. 44 | return u64(lo, ac + (ad >> 16u) + (cb >> 16u) + carry1 + carry2); 45 | } 46 | 47 | fn u64_add(a: u64, b: u64) -> u64 { 48 | let lo = a.lo + b.lo; 49 | let carry = u32(lo < a.lo); 50 | 51 | return u64(lo, a.hi + b.hi + carry); 52 | } 53 | 54 | fn u64_sub(a: u64, b: u64) -> u64 { 55 | let lo = a.lo - b.lo; 56 | let borrow = u32(b.lo > a.lo); 57 | 58 | return u64(lo, a.hi - b.hi - borrow); 59 | } 60 | 61 | /// Shift right by sh amount. 62 | /// 63 | /// # Remarks 64 | /// This implementation is correct up to sh == 31. 65 | fn u64_shr(a: u64, sh: u32) -> u64 { 66 | let mask = (0x1u << sh) - 1u; 67 | 68 | let in = (a.hi & mask) << (32u - sh); 69 | 70 | return u64((a.lo >> sh) | in, a.hi >> sh); 71 | } -------------------------------------------------------------------------------- /sunscreen_math_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_math_macros" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate contains macros that support the Sunscreen compiler." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | [lib] 18 | proc-macro = true 19 | 20 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 21 | 22 | [dependencies] 23 | bytemuck = { workspace = true } 24 | num = { workspace = true } 25 | darling = { workspace = true } 26 | proc-macro2 = { workspace = true } 27 | quote = { workspace = true } 28 | syn = { workspace = true } 29 | -------------------------------------------------------------------------------- /sunscreen_math_macros/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). -------------------------------------------------------------------------------- /sunscreen_runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_runtime" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen LLC"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate provides a runtime for performing various FHE operations within Sunscreen." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [dependencies] 20 | bincode = { workspace = true } 21 | bitvec = { workspace = true, optional = true } 22 | bulletproofs = { workspace = true, optional = true } 23 | crossbeam = { workspace = true } 24 | curve25519-dalek = { workspace = true } 25 | log = { workspace = true } 26 | logproof = { workspace = true, optional = true } 27 | merlin = { workspace = true } 28 | seal_fhe = { workspace = true } 29 | seq-macro = { version = "0.3", optional = true } 30 | sunscreen_fhe_program = { workspace = true } 31 | sunscreen_compiler_common = { workspace = true } 32 | sunscreen_math = { workspace = true } 33 | sunscreen_zkp_backend = { workspace = true } 34 | paste = { workspace = true, optional = true } 35 | petgraph = { workspace = true } 36 | rayon = { workspace = true } 37 | rlp = { workspace = true } 38 | serde = { workspace = true } 39 | semver = { workspace = true } 40 | static_assertions = { workspace = true } 41 | thiserror = { workspace = true } 42 | 43 | [dev-dependencies] 44 | serde_json = { workspace = true } 45 | 46 | [features] 47 | linkedproofs = [ 48 | "bulletproofs", 49 | "logproof", 50 | "dep:bitvec", 51 | "dep:seq-macro", 52 | "dep:paste", 53 | ] 54 | deterministic = ["seal_fhe/deterministic"] 55 | insecure-params = ["seal_fhe/insecure-params"] 56 | -------------------------------------------------------------------------------- /sunscreen_runtime/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). -------------------------------------------------------------------------------- /sunscreen_runtime/src/array.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Error, FheProgramInputTrait, InnerPlaintext, NumCiphertexts, Params, Plaintext, Result, 3 | TryFromPlaintext, TryIntoPlaintext, Type, TypeName, TypeNameInstance, WithContext, 4 | }; 5 | use seal_fhe::Plaintext as SealPlaintext; 6 | 7 | impl TryIntoPlaintext for [T; N] 8 | where 9 | T: TryIntoPlaintext, 10 | Self: TypeName, 11 | { 12 | fn try_into_plaintext(&self, params: &Params) -> Result { 13 | let element_plaintexts = self 14 | .iter() 15 | .map(|v| v.try_into_plaintext(params)) 16 | .collect::<Result<Vec<Plaintext>>>()? 17 | .drain(0..) 18 | .flat_map(|p| match p.inner { 19 | InnerPlaintext::Seal(v) => v, 20 | }) 21 | .collect::<Vec<WithContext<SealPlaintext>>>(); 22 | 23 | Ok(Plaintext { 24 | inner: InnerPlaintext::Seal(element_plaintexts), 25 | data_type: Self::type_name(), 26 | }) 27 | } 28 | } 29 | 30 | impl<T, const N: usize> TryFromPlaintext for [T; N] 31 | where 32 | T: TryFromPlaintext + TypeName + NumCiphertexts, 33 | Self: TypeName + NumCiphertexts, 34 | { 35 | fn try_from_plaintext(plaintext: &Plaintext, params: &Params) -> Result<Self> { 36 | let data = match &plaintext.inner { 37 | InnerPlaintext::Seal(p) => { 38 | if p.len() != Self::NUM_CIPHERTEXTS { 39 | return Err(Error::MalformedPlaintext); 40 | } 41 | 42 | p.chunks(T::NUM_CIPHERTEXTS) 43 | .map(|c| { 44 | let p = Plaintext { 45 | data_type: T::type_name(), 46 | inner: InnerPlaintext::Seal(c.to_owned()), 47 | }; 48 | 49 | T::try_from_plaintext(&p, params) 50 | }) 51 | .collect::<Result<Vec<T>>>()? 52 | } 53 | }; 54 | 55 | Ok(match data.try_into() { 56 | Ok(v) => v, 57 | _ => unreachable!(), 58 | }) 59 | } 60 | } 61 | 62 | impl<T, const N: usize> TypeNameInstance for [T; N] 63 | where 64 | T: TypeName, 65 | { 66 | fn type_name_instance(&self) -> Type { 67 | Self::type_name() 68 | } 69 | } 70 | 71 | impl<T, const N: usize> FheProgramInputTrait for [T; N] where T: TypeName + TryIntoPlaintext {} 72 | 73 | impl<T, const N: usize> NumCiphertexts for [T; N] 74 | where 75 | T: NumCiphertexts, 76 | { 77 | const NUM_CIPHERTEXTS: usize = T::NUM_CIPHERTEXTS * N; 78 | } 79 | -------------------------------------------------------------------------------- /sunscreen_tfhe/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "launch", 7 | "name": "Debug unit tests in library 'sunscreen_tfhe'", 8 | "cargo": { 9 | "args": [ 10 | "test", 11 | "--no-run", 12 | "--lib", 13 | "--package=sunscreen_tfhe" 14 | ], 15 | "filter": { 16 | "name": "sunscreen_tfhe", 17 | "kind": "lib" 18 | } 19 | }, 20 | "args": [], 21 | "cwd": "${workspaceFolder}" 22 | }, 23 | { 24 | "type": "lldb", 25 | "request": "launch", 26 | "name": "Debug benchmark 'tfhe_proof'", 27 | "cargo": { 28 | "args": [ 29 | "test", 30 | "--no-run", 31 | "--bench=tfhe_proof", 32 | "--package=sunscreen_tfhe" 33 | ], 34 | "filter": { 35 | "name": "tfhe_proof", 36 | "kind": "bench" 37 | } 38 | }, 39 | "args": [], 40 | "cwd": "${workspaceFolder}" 41 | }, 42 | { 43 | "type": "lldb", 44 | "request": "launch", 45 | "name": "Debug benchmark 'fft'", 46 | "cargo": { 47 | "args": [ 48 | "test", 49 | "--no-run", 50 | "--bench=fft", 51 | "--package=sunscreen_tfhe" 52 | ], 53 | "filter": { 54 | "name": "fft", 55 | "kind": "bench" 56 | } 57 | }, 58 | "args": [], 59 | "cwd": "${workspaceFolder}" 60 | }, 61 | { 62 | "type": "lldb", 63 | "request": "launch", 64 | "name": "Debug benchmark 'ops'", 65 | "cargo": { 66 | "args": [ 67 | "test", 68 | "--no-run", 69 | "--bench=ops", 70 | "--package=sunscreen_tfhe" 71 | ], 72 | "filter": { 73 | "name": "ops", 74 | "kind": "bench" 75 | } 76 | }, 77 | "args": [], 78 | "cwd": "${workspaceFolder}" 79 | } 80 | ] 81 | } -------------------------------------------------------------------------------- /sunscreen_tfhe/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.showUnlinkedFileNotification": false 3 | } -------------------------------------------------------------------------------- /sunscreen_tfhe/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_tfhe" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.70.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate contains the Sunscreen Torus FHE (TFHE) implementation" 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "TFHE", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | [dependencies] 18 | aligned-vec = { workspace = true } 19 | bytemuck = { workspace = true } 20 | logproof = { workspace = true, optional = true } 21 | num = { workspace = true } 22 | paste = { workspace = true } 23 | rand = { workspace = true } 24 | rand_distr = { workspace = true } 25 | raw-cpuid = { workspace = true } 26 | rayon = { workspace = true } 27 | realfft = "3.3.0" 28 | rustfft = "6.1.0" 29 | serde = { workspace = true } 30 | sunscreen_math = { workspace = true } 31 | thiserror = { workspace = true } 32 | 33 | [dev-dependencies] 34 | criterion = "0.5.1" 35 | merlin = "3.0.0" 36 | proptest = "1.4.0" 37 | 38 | [features] 39 | logproof = ["dep:logproof"] 40 | metal = ["logproof/metal"] 41 | 42 | [[bench]] 43 | name = "tfhe_proof" 44 | harness = false 45 | required-features= ["logproof"] 46 | 47 | [[bench]] 48 | name = "fft" 49 | harness = false 50 | 51 | [[bench]] 52 | name = "ops" 53 | harness = false 54 | -------------------------------------------------------------------------------- /sunscreen_tfhe/barrett.py: -------------------------------------------------------------------------------- 1 | import math 2 | import sys 3 | 4 | radix = 10 5 | 6 | if len(sys.argv) < 3: 7 | print("Usage: barrett <number of 64-bit limbs> <modulus> [<modulus radix>]") 8 | 9 | n = int(sys.argv[1]) 10 | 11 | if len(sys.argv) == 4: 12 | radix = int(sys.argv[3]) 13 | 14 | p = int(sys.argv[2], radix) 15 | 16 | def compute_vals(n, p): 17 | r = math.floor(2**(64 * n) // p) 18 | s = math.floor(2**(128 * n) // p) - 2**(64 * n) * r 19 | t = 2**(64*n) - r * p 20 | 21 | return (n, r, s, t) 22 | 23 | (_, r, s, t) = compute_vals(n, p) 24 | 25 | def print_value(n, name, x): 26 | print(name + " = [") 27 | 28 | for i in range(n): 29 | print(" " + str((x >> (64 * i)) & 0xFFFFFFFFFFFFFFFF) + ",") 30 | 31 | print("]") 32 | 33 | print_value(n, "r", r) 34 | print_value(n, "s", s) 35 | print_value(n, "t", t) 36 | -------------------------------------------------------------------------------- /sunscreen_tfhe/benches/fft.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use num::Complex; 3 | use sunscreen_tfhe::{math::fft::negacyclic::TwistedFft, FrequencyTransform}; 4 | 5 | fn negacyclic_fft(c: &mut Criterion) { 6 | let n = 2048; 7 | 8 | let plan = TwistedFft::<f64>::new(n); 9 | 10 | let x = (0..n).map(|x| x as f64).collect::<Vec<_>>(); 11 | let mut y = vec![Complex::from(0.0); x.len() / 2]; 12 | 13 | c.bench_function("FFT 2048", |s| { 14 | s.iter(|| { 15 | plan.forward(&x, &mut y); 16 | }); 17 | }); 18 | 19 | let n = 1024; 20 | 21 | let plan = TwistedFft::<f64>::new(n); 22 | 23 | let x = (0..n).map(|x| x as f64).collect::<Vec<_>>(); 24 | let mut y = vec![Complex::from(0.0); x.len() / 2]; 25 | 26 | c.bench_function("FFT 1024", |s| { 27 | s.iter(|| { 28 | plan.forward(&x, &mut y); 29 | }); 30 | }); 31 | 32 | let n: usize = 256; 33 | 34 | let plan = TwistedFft::<f64>::new(n); 35 | 36 | let x = (0..n).map(|x| x as f64).collect::<Vec<_>>(); 37 | let mut y = vec![Complex::from(0.0); x.len() / 2]; 38 | 39 | c.bench_function("FFT 256", |s| { 40 | s.iter(|| { 41 | plan.forward(&x, &mut y); 42 | }); 43 | }); 44 | } 45 | 46 | criterion_group!(benches, negacyclic_fft); 47 | criterion_main!(benches); 48 | -------------------------------------------------------------------------------- /sunscreen_tfhe/images/circuit_bootstrapping.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sunscreen-tech/Sunscreen/d7f58b39c145d530ca9b5abdf6172bd292523696/sunscreen_tfhe/images/circuit_bootstrapping.graffle -------------------------------------------------------------------------------- /sunscreen_tfhe/images/circuit_bootstrapping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sunscreen-tech/Sunscreen/d7f58b39c145d530ca9b5abdf6172bd292523696/sunscreen_tfhe/images/circuit_bootstrapping.png -------------------------------------------------------------------------------- /sunscreen_tfhe/mont.py: -------------------------------------------------------------------------------- 1 | p = 13 2 | r = 16 3 | rinv = (r**(p-2)) % p 4 | 5 | p_inv = (p**(p-2)) % r 6 | p_prime = (p_inv * (r - 1)) % r 7 | print((p_inv * p) % r) 8 | 9 | print((r * rinv) % p) 10 | 11 | def to_mont(x): 12 | return (r * x) % p 13 | 14 | def from_mont(x): 15 | return (x * rinv) % p 16 | 17 | def mont_add(x, y): 18 | return (x + y) % p 19 | 20 | def mont_mul(x, y): 21 | return (x * y * rinv) % p 22 | 23 | def redc(): 24 | pass 25 | 26 | a = to_mont(5) 27 | b = to_mont(6) 28 | 29 | 30 | c = mont_mul(a, b) 31 | d = mont_add(a, b) 32 | 33 | print(from_mont(c), from_mont(d)) 34 | 35 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/entities/glev_ciphertext_fft.rs: -------------------------------------------------------------------------------- 1 | use num::Complex; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::{ 5 | dst::{NoWrapper, OverlaySize}, 6 | GlweDef, GlweDimension, RadixCount, TorusOps, 7 | }; 8 | 9 | use super::{ 10 | GlevCiphertextRef, GlweCiphertextFftIterator, GlweCiphertextFftIteratorMut, 11 | GlweCiphertextFftRef, 12 | }; 13 | 14 | dst! { 15 | /// The FFT variant of a GLEV ciphertext. See 16 | /// [GlevCiphertext](crate::entities::GlevCiphertext) for more details. 17 | GlevCiphertextFft, 18 | GlevCiphertextFftRef, 19 | NoWrapper, 20 | (Clone, Debug, Serialize, Deserialize), 21 | () 22 | } 23 | dst_iter! { GlevCiphertextFftIterator, GlevCiphertextFftIteratorMut, ParallelGlevCiphertextFftIterator, ParallelGlevCiphertextFftIteratorMut, NoWrapper, GlevCiphertextFftRef, ()} 24 | 25 | impl OverlaySize for GlevCiphertextFftRef<Complex<f64>> { 26 | type Inputs = (GlweDimension, RadixCount); 27 | 28 | fn size(t: Self::Inputs) -> usize { 29 | GlweCiphertextFftRef::<Complex<f64>>::size(t.0) * t.1 .0 30 | } 31 | } 32 | 33 | impl GlevCiphertextFftRef<Complex<f64>> { 34 | /// Returns an iterator over the rows of the GLEV ciphertext, which are 35 | /// [`GlweCiphertextFft`](crate::entities::GlweCiphertextFft)s. 36 | pub fn glwe_ciphertexts(&self, params: &GlweDef) -> GlweCiphertextFftIterator<Complex<f64>> { 37 | GlweCiphertextFftIterator::new( 38 | &self.data, 39 | GlweCiphertextFftRef::<Complex<f64>>::size(params.dim), 40 | ) 41 | } 42 | 43 | /// Returns a mutable iterator over the rows of the GLEV ciphertext, which are 44 | /// [`GlweCiphertextFft`](crate::entities::GlweCiphertextFft)s. 45 | pub fn glwe_ciphertexts_mut( 46 | &mut self, 47 | params: &GlweDef, 48 | ) -> GlweCiphertextFftIteratorMut<Complex<f64>> { 49 | GlweCiphertextFftIteratorMut::new( 50 | &mut self.data, 51 | GlweCiphertextFftRef::<Complex<f64>>::size(params.dim), 52 | ) 53 | } 54 | 55 | /// Computes the inverse FFT of the GLEV ciphertexts and stores computation 56 | /// in `result`. 57 | pub fn ifft<S: TorusOps>(&self, result: &mut GlevCiphertextRef<S>, params: &GlweDef) { 58 | for (i, ifft) in self 59 | .glwe_ciphertexts(params) 60 | .zip(result.glwe_ciphertexts_mut(params)) 61 | { 62 | i.ifft(ifft, params); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/entities/lev_ciphertext.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{dst::OverlaySize, LweDef, LweDimension, RadixCount, Torus, TorusOps}; 4 | 5 | use super::{LweCiphertextIterator, LweCiphertextIteratorMut, LweCiphertextRef}; 6 | 7 | // Iteration over LWE ciphertexts 8 | dst! { 9 | /// A Lev Ciphertext is a collection of LWE ciphertexts. 10 | LevCiphertext, 11 | LevCiphertextRef, 12 | Torus, 13 | (Clone, Debug, Serialize, Deserialize), 14 | (TorusOps,) 15 | } 16 | dst_iter! { LevCiphertextIterator, LevCiphertextIteratorMut, ParallelLevCiphertextIterator, ParallelLevCiphertextIteratorMut, Torus, LevCiphertextRef, (TorusOps,)} 17 | 18 | impl<S> OverlaySize for LevCiphertextRef<S> 19 | where 20 | S: TorusOps, 21 | { 22 | type Inputs = (LweDimension, RadixCount); 23 | 24 | fn size(t: Self::Inputs) -> usize { 25 | LweCiphertextRef::<S>::size(t.0) * t.1 .0 26 | } 27 | } 28 | 29 | impl<S> LevCiphertextRef<S> 30 | where 31 | S: TorusOps, 32 | { 33 | /// Returns an iterator over the rows of the Lev ciphertext, which are 34 | /// [`LweCiphertext`](crate::entities::LweCiphertext)s. 35 | pub fn lwe_ciphertexts(&self, params: &LweDef) -> LweCiphertextIterator<S> { 36 | LweCiphertextIterator::new(&self.data, LweCiphertextRef::<S>::size(params.dim)) 37 | } 38 | 39 | /// Returns a mutable iterator over the rows of the Lev ciphertext, which are 40 | /// [`LweCiphertext`](crate::entities::LweCiphertext)s. 41 | pub fn lwe_ciphertexts_mut(&mut self, params: &LweDef) -> LweCiphertextIteratorMut<S> { 42 | LweCiphertextIteratorMut::new(&mut self.data, LweCiphertextRef::<S>::size(params.dim)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/entities/lwe_ciphertext_list.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use sunscreen_math::Zero; 3 | 4 | use crate::{ 5 | dst::{AsMutSlice, AsSlice, OverlaySize}, 6 | LweDef, LweDimension, Torus, TorusOps, 7 | }; 8 | 9 | use super::{LweCiphertextIterator, LweCiphertextIteratorMut, LweCiphertextRef}; 10 | 11 | dst! { 12 | /// A list of LWE ciphertexts. Used during 13 | /// [`circuit_bootstrap`](crate::ops::bootstrapping::circuit_bootstrap). 14 | LweCiphertextList, 15 | LweCiphertextListRef, 16 | Torus, 17 | (Clone, Debug, Serialize, Deserialize), 18 | (TorusOps) 19 | } 20 | 21 | impl<S: TorusOps> OverlaySize for LweCiphertextListRef<S> { 22 | type Inputs = (LweDimension, usize); 23 | 24 | #[inline(always)] 25 | fn size(t: Self::Inputs) -> usize { 26 | LweCiphertextRef::<S>::size(t.0) * t.1 27 | } 28 | } 29 | 30 | impl<S: TorusOps> LweCiphertextList<S> { 31 | /// Create a new zero [LweCiphertextList] with the given parameters. 32 | /// 33 | /// This data structure represents is a list of LWE ciphertexts, used for 34 | /// [`circuit_bootstrap`](crate::ops::bootstrapping::circuit_bootstrap). 35 | pub fn new(lwe: &LweDef, count: usize) -> Self { 36 | Self { 37 | data: avec![Torus::zero(); LweCiphertextListRef::<S>::size((lwe.dim, count))], 38 | } 39 | } 40 | } 41 | 42 | impl<S: TorusOps> LweCiphertextListRef<S> { 43 | /// Iterate over the LWE ciphertexts in the list. 44 | pub fn ciphertexts(&self, lwe: &LweDef) -> LweCiphertextIterator<S> { 45 | LweCiphertextIterator::new(self.as_slice(), LweCiphertextRef::<S>::size(lwe.dim)) 46 | } 47 | 48 | /// Iterate over the LWE ciphertexts in the list mutably. 49 | pub fn ciphertexts_mut(&mut self, lwe: &LweDef) -> LweCiphertextIteratorMut<S> { 50 | LweCiphertextIteratorMut::new(self.as_mut_slice(), LweCiphertextRef::<S>::size(lwe.dim)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/entities/mod.rs: -------------------------------------------------------------------------------- 1 | mod public_functional_keyswitch_key; 2 | pub use public_functional_keyswitch_key::*; 3 | 4 | mod blind_rotation_shift; 5 | pub use blind_rotation_shift::*; 6 | 7 | mod lwe_ciphertext_list; 8 | pub use lwe_ciphertext_list::*; 9 | 10 | mod private_functional_keyswitch_key; 11 | pub use private_functional_keyswitch_key::*; 12 | 13 | mod circuit_bootstrapping_private_keyswitch_keys; 14 | pub use circuit_bootstrapping_private_keyswitch_keys::*; 15 | 16 | mod bootstrap_key; 17 | pub use bootstrap_key::*; 18 | 19 | mod univariate_lookup_table; 20 | pub use univariate_lookup_table::*; 21 | 22 | mod bivariate_lookup_table; 23 | pub use bivariate_lookup_table::*; 24 | 25 | mod glwe_secret_key; 26 | pub use glwe_secret_key::*; 27 | 28 | mod glwe_ciphertext; 29 | pub use glwe_ciphertext::*; 30 | 31 | mod glwe_ciphertext_fft; 32 | pub use glwe_ciphertext_fft::*; 33 | 34 | mod lwe_ciphertext; 35 | pub use lwe_ciphertext::*; 36 | 37 | mod lwe_secret_key; 38 | pub use lwe_secret_key::*; 39 | 40 | mod lwe_public_key; 41 | pub use lwe_public_key::*; 42 | 43 | mod glev_ciphertext; 44 | pub use glev_ciphertext::*; 45 | 46 | mod glev_ciphertext_fft; 47 | pub use glev_ciphertext_fft::*; 48 | 49 | mod ggsw_ciphertext; 50 | pub use ggsw_ciphertext::*; 51 | 52 | mod ggsw_ciphertext_fft; 53 | pub use ggsw_ciphertext_fft::*; 54 | 55 | mod lev_ciphertext; 56 | pub use lev_ciphertext::*; 57 | 58 | mod lwe_keyswitch_key; 59 | pub use lwe_keyswitch_key::*; 60 | 61 | mod glwe_keyswitch_key; 62 | pub use glwe_keyswitch_key::*; 63 | 64 | mod rlwe_public_key; 65 | pub use rlwe_public_key::*; 66 | 67 | mod polynomial; 68 | pub use polynomial::*; 69 | 70 | mod polynomial_fft; 71 | pub use polynomial_fft::*; 72 | 73 | mod polynomial_list; 74 | pub use polynomial_list::*; 75 | 76 | mod scheme_switch_key; 77 | pub use scheme_switch_key::*; 78 | 79 | mod scheme_switch_key_fft; 80 | pub use scheme_switch_key_fft::*; 81 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/entities/polynomial_list.rs: -------------------------------------------------------------------------------- 1 | use num::Zero; 2 | 3 | use crate::{ 4 | dst::{NoWrapper, OverlaySize}, 5 | PolynomialDegree, 6 | }; 7 | 8 | use super::{PolynomialIterator, PolynomialIteratorMut, PolynomialRef}; 9 | 10 | dst! { 11 | /// A list of polynomials. 12 | PolynomialList, 13 | PolynomialListRef, 14 | NoWrapper, 15 | (Debug, Clone), 16 | () 17 | } 18 | 19 | impl<S: Clone> OverlaySize for PolynomialListRef<S> { 20 | type Inputs = (PolynomialDegree, usize); 21 | 22 | fn size(t: Self::Inputs) -> usize { 23 | PolynomialRef::<S>::size(t.0) * t.1 24 | } 25 | } 26 | 27 | impl<S> PolynomialList<S> 28 | where 29 | S: Clone + Zero, 30 | { 31 | /// Create a new polynomial list, where each polynomial has the same degree. 32 | pub fn new(degree: PolynomialDegree, count: usize) -> Self { 33 | Self { 34 | data: avec![S::zero(); degree.0 * count], 35 | } 36 | } 37 | } 38 | 39 | impl<S> PolynomialListRef<S> 40 | where 41 | S: Clone + Zero, 42 | { 43 | /// Iterate over the polynomials in the list. 44 | pub fn iter(&self, degree: PolynomialDegree) -> PolynomialIterator<S> { 45 | PolynomialIterator::new(&self.data, PolynomialRef::<S>::size(degree)) 46 | } 47 | 48 | /// Iterate over the polynomials in the list mutably. 49 | pub fn iter_mut(&mut self, degree: PolynomialDegree) -> PolynomialIteratorMut<S> { 50 | PolynomialIteratorMut::new(&mut self.data, PolynomialRef::<S>::size(degree)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/entities/public_functional_keyswitch_key.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use sunscreen_math::Zero; 3 | 4 | use crate::{ 5 | dst::{AsMutSlice, AsSlice, OverlaySize}, 6 | entities::{GlevCiphertextIterator, GlevCiphertextIteratorMut, GlevCiphertextRef}, 7 | GlweDef, GlweDimension, LweDef, LweDimension, RadixCount, RadixDecomposition, Torus, TorusOps, 8 | }; 9 | 10 | use super::GlweCiphertextRef; 11 | 12 | dst! { 13 | /// Public Functional Key Switching Key. See 14 | /// [`module`](crate::ops::keyswitch::public_functional_keyswitch) 15 | /// documentation for more details. 16 | PublicFunctionalKeyswitchKey, 17 | PublicFunctionalKeyswitchKeyRef, 18 | Torus, 19 | (Clone, Debug, Serialize, Deserialize), 20 | (TorusOps) 21 | } 22 | 23 | impl<S: TorusOps> OverlaySize for PublicFunctionalKeyswitchKeyRef<S> { 24 | type Inputs = (LweDimension, GlweDimension, RadixCount); 25 | 26 | fn size(t: Self::Inputs) -> usize { 27 | GlweCiphertextRef::<S>::size(t.1) * t.0 .0 * t.2 .0 28 | } 29 | } 30 | 31 | impl<S: TorusOps> PublicFunctionalKeyswitchKey<S> { 32 | /// Construct a new uninitialized [`PublicFunctionalKeyswitchKey`]. This key is used 33 | /// when performing a [`public_functional_keyswitch`](crate::ops::keyswitch::public_functional_keyswitch). 34 | pub fn new(from_lwe: &LweDef, to_glwe: &GlweDef, radix: &RadixDecomposition) -> Self { 35 | let len = 36 | PublicFunctionalKeyswitchKeyRef::<S>::size((from_lwe.dim, to_glwe.dim, radix.count)); 37 | 38 | Self { 39 | data: avec![Torus::zero(); len], 40 | } 41 | } 42 | } 43 | 44 | impl<S: TorusOps> PublicFunctionalKeyswitchKeyRef<S> { 45 | /// Iterate over the rows of the [`PublicFunctionalKeyswitchKey`]. 46 | pub fn glevs( 47 | &self, 48 | to_glwe: &GlweDef, 49 | radix: &RadixDecomposition, 50 | ) -> GlevCiphertextIterator<S> { 51 | let stride = GlevCiphertextRef::<S>::size((to_glwe.dim, radix.count)); 52 | 53 | GlevCiphertextIterator::new(self.as_slice(), stride) 54 | } 55 | 56 | /// Iterate over the rows of the [`PublicFunctionalKeyswitchKey`] mutably. 57 | pub fn glevs_mut( 58 | &mut self, 59 | to_glwe: &GlweDef, 60 | radix: &RadixDecomposition, 61 | ) -> GlevCiphertextIteratorMut<S> { 62 | let stride = GlevCiphertextRef::<S>::size((to_glwe.dim, radix.count)); 63 | 64 | GlevCiphertextIteratorMut::new(self.as_mut_slice(), stride) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/entities/rlwe_public_key.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use sunscreen_math::Zero; 3 | 4 | use crate::dst::{FromMutSlice, FromSlice}; 5 | use crate::GlweDef; 6 | use crate::{dst::OverlaySize, GlweDimension, Torus, TorusOps}; 7 | 8 | use crate::entities::GlweCiphertextRef; 9 | 10 | use super::PolynomialRef; 11 | 12 | dst! { 13 | /// An RLWE public key. 14 | RlwePublicKey, 15 | RlwePublicKeyRef, 16 | Torus, 17 | (Clone, Debug, Serialize, Deserialize), 18 | (TorusOps) 19 | } 20 | 21 | impl<S> OverlaySize for RlwePublicKeyRef<S> 22 | where 23 | S: TorusOps, 24 | { 25 | type Inputs = GlweDimension; 26 | 27 | fn size(t: Self::Inputs) -> usize { 28 | GlweCiphertextRef::<S>::size(t) 29 | } 30 | } 31 | 32 | impl<S> RlwePublicKey<S> 33 | where 34 | S: TorusOps, 35 | { 36 | /// Creates an uninitialized public key. 37 | pub fn new(glwe: &GlweDef) -> Self { 38 | assert_eq!(glwe.dim.size.0, 1); 39 | 40 | Self { 41 | data: avec![Torus::zero(); GlweCiphertextRef::<S>::size(glwe.dim)], 42 | } 43 | } 44 | } 45 | 46 | impl<S> RlwePublicKeyRef<S> 47 | where 48 | S: TorusOps, 49 | { 50 | /// Returns a reference to the internal encryption of zero. 51 | pub fn zero_encryption(&self) -> &GlweCiphertextRef<S> { 52 | GlweCiphertextRef::from_slice(&self.data) 53 | } 54 | 55 | /// Returns a mutable reference to the internal encryption of zero. 56 | pub fn zero_encryption_mut(&mut self) -> &mut GlweCiphertextRef<S> { 57 | GlweCiphertextRef::from_mut_slice(&mut self.data) 58 | } 59 | 60 | /// Returns the p0 and p1 components of this public key. 61 | pub fn p0_p1(&self, glwe: &GlweDef) -> (&PolynomialRef<Torus<S>>, &PolynomialRef<Torus<S>>) { 62 | let (mut p0, p1) = self.zero_encryption().a_b(glwe); 63 | 64 | (p0.next().unwrap(), p1) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/error.rs: -------------------------------------------------------------------------------- 1 | /// Errors that can occur using this crate. 2 | #[derive(Debug, thiserror::Error)] 3 | pub enum Error { 4 | /// The size of the given entity is invalid under the given scheme parameters. 5 | #[error("The given entity is the incorrect size for the requested parameters.")] 6 | InvalidSize, 7 | } 8 | 9 | /// A result that can occur in this crate. 10 | pub type Result<T> = std::result::Result<T, Error>; 11 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/iteration/mod.rs: -------------------------------------------------------------------------------- 1 | mod triangular_pairs; 2 | pub use triangular_pairs::*; 3 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! TFHE low-level library 2 | 3 | #![deny(missing_docs)] 4 | #![deny(rustdoc::broken_intra_doc_links)] 5 | 6 | #[macro_use] 7 | mod dst; 8 | pub use dst::OverlaySize; 9 | 10 | /// Methods for iterating over data structures. 11 | pub(crate) mod iteration; 12 | 13 | /// The entities module contains the main data structures used in the library. 14 | pub mod entities; 15 | 16 | /// Higher level operations in TFHE such as programmable bootstrapping, keyswitching, etc. 17 | pub mod ops; 18 | 19 | /// Parameters that define a TFHE scheme. 20 | pub mod params; 21 | pub use params::*; 22 | 23 | /// Math operations on various math primitives such as polynomials. 24 | pub mod math; 25 | pub use math::*; 26 | 27 | mod macros; 28 | 29 | /// Random number generation. 30 | pub mod rand; 31 | mod scratch; 32 | 33 | /// A high-level API for interfacing with TFHE. Allocates, computes with and returns 34 | /// objects as you would expect from a Rust API. 35 | pub mod high_level; 36 | 37 | /// Zero Knowledge proofs for TFHE. 38 | #[cfg(feature = "logproof")] 39 | pub mod zkp; 40 | 41 | /// Container [`Error`] and [`Result`] types for this crate. 42 | mod error; 43 | pub use error::*; 44 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/math/basic.rs: -------------------------------------------------------------------------------- 1 | /// An integer type that supports rounding division. 2 | pub trait RoundedDiv { 3 | /// Divides two numbers and rounds the result to the nearest integer. 4 | fn div_rounded(&self, divisor: Self) -> Self; 5 | } 6 | 7 | macro_rules! div_rounded { 8 | ($t:ty) => { 9 | impl RoundedDiv for $t { 10 | #[inline(always)] 11 | fn div_rounded(&self, divisor: $t) -> $t { 12 | // There are a few ways to do this, but we chose the following 13 | // because it allows the entire range of a type to be used. The 14 | // other common method is to add half the divisor to the 15 | // numerator, but that effectively cuts the size of the possible 16 | // inputs in half before overflow. 17 | let q = self / divisor; 18 | let r = self % divisor; 19 | if r >= divisor / 2 { 20 | q + 1 21 | } else { 22 | q 23 | } 24 | } 25 | } 26 | }; 27 | } 28 | 29 | div_rounded!(u8); 30 | div_rounded!(u16); 31 | div_rounded!(u32); 32 | div_rounded!(u64); 33 | div_rounded!(u128); 34 | div_rounded!(usize); 35 | div_rounded!(i8); 36 | div_rounded!(i16); 37 | div_rounded!(i32); 38 | div_rounded!(i64); 39 | div_rounded!(i128); 40 | div_rounded!(isize); 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use rand::{thread_rng, RngCore}; 45 | 46 | use super::*; 47 | 48 | #[test] 49 | fn test_div_rounded() { 50 | for _ in 0..1_000 { 51 | let a = thread_rng().next_u64(); 52 | let mut b = thread_rng().next_u64(); 53 | 54 | if b == 0 { 55 | b = 1; 56 | } 57 | 58 | let expected = ((a as f64) / (b as f64)).round() as u64; 59 | let actual = a.div_rounded(b); 60 | 61 | assert_eq!(expected, actual); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/math/fft/mod.rs: -------------------------------------------------------------------------------- 1 | /// FFT based operations over real numbers. 2 | pub mod cyclic; 3 | 4 | /// FFT based operations over twisted cyclotomics. 5 | pub mod negacyclic; 6 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/math/simd/mod.rs: -------------------------------------------------------------------------------- 1 | mod scalar; 2 | #[cfg(not(all( 3 | any(target_arch = "x86", target_arch = "x86_64"), 4 | target_feature = "avx2" 5 | )))] 6 | pub use scalar::*; 7 | 8 | #[cfg(all( 9 | any(target_arch = "x86", target_arch = "x86_64"), 10 | target_feature = "avx2" 11 | ))] 12 | mod avx2; 13 | #[cfg(all( 14 | any(target_arch = "x86", target_arch = "x86_64"), 15 | target_feature = "avx2" 16 | ))] 17 | pub use avx2::*; 18 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/math/simd/scalar.rs: -------------------------------------------------------------------------------- 1 | use num::Complex; 2 | 3 | pub fn complex_mad(c: &mut [Complex<f64>], a: &[Complex<f64>], b: &[Complex<f64>]) { 4 | for ((c, a), b) in c.iter_mut().zip(a.iter()).zip(b.iter()) { 5 | *c += a * b; 6 | } 7 | } 8 | 9 | #[cfg(test)] 10 | mod test { 11 | use rand::{thread_rng, RngCore}; 12 | 13 | use super::*; 14 | 15 | #[test] 16 | fn can_scalar_mad_complex_f64_slice() { 17 | let a = (0..16) 18 | .map(|_| { 19 | Complex::new( 20 | thread_rng().next_u64() as f64, 21 | thread_rng().next_u64() as f64, 22 | ) 23 | }) 24 | .collect::<Vec<_>>(); 25 | 26 | let b = (0..16) 27 | .map(|_| { 28 | Complex::new( 29 | thread_rng().next_u64() as f64, 30 | thread_rng().next_u64() as f64, 31 | ) 32 | }) 33 | .collect::<Vec<_>>(); 34 | 35 | let mut expected = (0..16) 36 | .map(|_| { 37 | Complex::new( 38 | thread_rng().next_u64() as f64, 39 | thread_rng().next_u64() as f64, 40 | ) 41 | }) 42 | .collect::<Vec<_>>(); 43 | 44 | let mut actual = expected.clone(); 45 | 46 | complex_mad(&mut actual, &a, &b); 47 | 48 | for ((c, a), b) in expected.iter_mut().zip(a.iter()).zip(b.iter()) { 49 | *c += a * b; 50 | } 51 | 52 | assert_eq!(actual, expected); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/bootstrapping/mod.rs: -------------------------------------------------------------------------------- 1 | mod blind_rotation; 2 | pub use blind_rotation::*; 3 | 4 | mod circuit_bootstrapping; 5 | pub use circuit_bootstrapping::*; 6 | 7 | mod programmable_bootstrapping; 8 | pub use programmable_bootstrapping::*; 9 | 10 | mod scheme_switch; 11 | pub use scheme_switch::*; 12 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/ciphertext/glev_ciphertext_ops.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | entities::{GlevCiphertextRef, GlweCiphertextRef, Polynomial}, 3 | radix::{PolynomialRadixIterator, ScalarRadixIterator}, 4 | GlweDef, TorusOps, 5 | }; 6 | 7 | use super::{glwe_polynomial_mad, glwe_scalar_mad}; 8 | 9 | /// Compute `c += (G^-1 * a) \[*\] b`, where 10 | /// * `G^-1 * a`` is the radix decomposition of `a` 11 | /// * `b` is a GLEV ciphertext. 12 | /// * `c` is a GLWE ciphertext. 13 | /// * \[*\] is the external product between a GLEV ciphertext and `l` polynomials 14 | /// 15 | /// # Remarks 16 | /// This functions takes a PolynomialRadixIterator to perform the decomposition. 17 | /// This function is also known as the gadget product. 18 | pub fn decomposed_polynomial_glev_mad<S>( 19 | c: &mut GlweCiphertextRef<S>, 20 | mut a: PolynomialRadixIterator<S>, 21 | b: &GlevCiphertextRef<S>, 22 | params: &GlweDef, 23 | ) where 24 | S: TorusOps, 25 | { 26 | // a = decomp(a_i) 27 | // b = r 28 | 29 | let b_glwe = b.glwe_ciphertexts(params); 30 | let mut cur_radix: Polynomial<S> = Polynomial::zero(params.dim.polynomial_degree.0); 31 | 32 | // The decomposition of 33 | // <Decomp^{beta, l}(gamma), GLEV> 34 | // can be performed using 35 | // sum_{j = 1}^l gamma_j * C_j 36 | // where gamma_j is the polynomial to decompose multiplied by q/B^{j+1} 37 | // Note the reverse of the GLWE ciphertexts here! The decomposition iterator 38 | // returns the decomposed values in the opposite order. 39 | for b in b_glwe.rev() { 40 | a.write_next(&mut cur_radix); 41 | glwe_polynomial_mad(c, b, &cur_radix, params); 42 | } 43 | } 44 | 45 | /// Compute `c += (G^-1 * a) \[*\] b`, where 46 | /// * `G^-1 * a`` is the radix decomposition of `a` 47 | /// * `b` is a GLEV ciphertext. 48 | pub fn decomposed_scalar_glev_mad<S>( 49 | c: &mut GlweCiphertextRef<S>, 50 | a: ScalarRadixIterator<S>, 51 | b: &GlevCiphertextRef<S>, 52 | params: &GlweDef, 53 | ) where 54 | S: TorusOps, 55 | { 56 | for (b, a) in b.glwe_ciphertexts(params).rev().zip(a) { 57 | glwe_scalar_mad(c, b, a, params); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/ciphertext/lev_ciphertext_ops.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | entities::{LevCiphertextRef, LweCiphertextRef, Polynomial}, 3 | radix::PolynomialRadixIterator, 4 | LweDef, TorusOps, 5 | }; 6 | 7 | use super::scalar_mul_ciphertext_mad; 8 | 9 | /// Compute `c += (G^-1 * a) \[*\] b`, where 10 | /// * `G^-1 * a`` is the radix decomposition of `a` 11 | /// * `b` is a LEV ciphertext. 12 | /// * `c` is a LWE ciphertext. 13 | /// * \[*\] is the external product between a LEV ciphertext and the decomposed 14 | /// LWE ciphertext. 15 | /// 16 | /// # Remarks 17 | /// This functions takes a PolynomialRadixIterator to perform the decomposition. 18 | pub fn decomposed_scalar_lev_mad<S>( 19 | c: &mut LweCiphertextRef<S>, 20 | mut a: PolynomialRadixIterator<S>, 21 | b: &LevCiphertextRef<S>, 22 | params: &LweDef, 23 | ) where 24 | S: TorusOps, 25 | { 26 | let b_lwe = b.lwe_ciphertexts(params); 27 | let mut cur_radix: Polynomial<S> = Polynomial::zero(1); 28 | 29 | // The decomposition of 30 | // <Decomp^{beta, l}(gamma), GLEV> 31 | // can be performed using 32 | // sum_{j = 1}^l gamma_j * C_j 33 | // where gamma_j is the polynomial to decompose multiplied by q/B^{j+1} 34 | // Note the reverse of the GLWE ciphertexts here! The decomposition iterator 35 | // returns the decomposed values in the opposite order. 36 | for b in b_lwe.rev() { 37 | a.write_next(&mut cur_radix); 38 | let radix = cur_radix.coeffs()[0]; 39 | 40 | scalar_mul_ciphertext_mad(c, &radix, b, params); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/ciphertext/mod.rs: -------------------------------------------------------------------------------- 1 | mod lwe_ciphertext_ops; 2 | pub use lwe_ciphertext_ops::*; 3 | 4 | mod lev_ciphertext_ops; 5 | pub use lev_ciphertext_ops::*; 6 | 7 | mod glev_ciphertext_ops; 8 | pub use glev_ciphertext_ops::*; 9 | 10 | mod glwe_ciphertext_ops; 11 | pub use glwe_ciphertext_ops::*; 12 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/encryption/mod.rs: -------------------------------------------------------------------------------- 1 | mod lwe_encryption; 2 | pub use lwe_encryption::*; 3 | 4 | mod glev_encryption; 5 | pub use glev_encryption::*; 6 | 7 | mod glwe_encryption; 8 | pub use glwe_encryption::*; 9 | 10 | mod ggsw_encryption; 11 | pub use ggsw_encryption::*; 12 | 13 | mod rlwe_encryption; 14 | pub use rlwe_encryption::*; 15 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/homomorphisms/lwe.rs: -------------------------------------------------------------------------------- 1 | use crate::{entities::LweCiphertextRef, LweDef, OverlaySize, Torus, TorusOps}; 2 | 3 | /// Add `amount` to each torus element (mod q) in the ciphertext. 4 | /// This shifts where messages lie on the torus and adds no noise. 5 | /// 6 | /// # Remark 7 | /// Suppose we have plaintexts 0 and 1 that lie centered at 0 and q/2 respectively. 8 | /// If we rotate by q/4, then the 0 lies centered at q/4 and 1 lies at 3q/4 == -q/4. 9 | pub fn rotate<S: TorusOps>( 10 | output: &mut LweCiphertextRef<S>, 11 | input: &LweCiphertextRef<S>, 12 | amount: Torus<S>, 13 | lwe: &LweDef, 14 | ) { 15 | output.assert_is_valid(lwe.dim); 16 | input.assert_is_valid(lwe.dim); 17 | 18 | output.a_mut(lwe).clone_from_slice(input.a(lwe)); 19 | *output.b_mut(lwe) = input.b(lwe) + amount; 20 | } 21 | 22 | #[cfg(test)] 23 | mod tests { 24 | use crate::{ 25 | entities::LweCiphertext, 26 | high_level::{keygen, TEST_LWE_DEF_1}, 27 | PlaintextBits, Torus, 28 | }; 29 | 30 | use super::rotate; 31 | 32 | #[test] 33 | fn can_rotate() { 34 | let lwe_params = TEST_LWE_DEF_1; 35 | 36 | for _ in 0..100 { 37 | let sk = keygen::generate_binary_lwe_sk(&lwe_params); 38 | let val = sk.encrypt(0, &lwe_params, PlaintextBits(1)).0; 39 | 40 | let mut res = LweCiphertext::new(&lwe_params); 41 | 42 | rotate( 43 | &mut res, 44 | &val, 45 | Torus::encode(1, PlaintextBits(2)), 46 | &lwe_params, 47 | ); 48 | 49 | let t = sk.decrypt_without_decode(&res, &lwe_params); 50 | 51 | assert!(t.inner() < 0x1u64 << 63); 52 | 53 | let val = sk.encrypt(1, &lwe_params, PlaintextBits(1)).0; 54 | 55 | rotate( 56 | &mut res, 57 | &val, 58 | Torus::encode(1, PlaintextBits(2)), 59 | &lwe_params, 60 | ); 61 | 62 | let t = sk.decrypt_without_decode(&res, &lwe_params); 63 | 64 | assert!(t.inner() > 0x1u64 << 63); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/homomorphisms/mod.rs: -------------------------------------------------------------------------------- 1 | mod lwe; 2 | pub use lwe::*; 3 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/keyswitch/lwe_keyswitch_key.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | entities::{LweKeyswitchKeyRef, LweSecretKeyRef}, 3 | ops::encryption::encrypt_lwe_ciphertext, 4 | LweDef, OverlaySize, RadixDecomposition, Torus, TorusOps, 5 | }; 6 | 7 | /// Generates a keyswitch key from an original LWE key to a new LWE key. The 8 | /// resulting keyswitch key is encrypted under the new key. 9 | /// 10 | /// Arguments: 11 | /// 12 | /// * keyswitch_key: the resulting keyswitch key 13 | /// * original_lwe_secret_key: the original LWE secret key 14 | /// * new_lwe_secret_key: the new LWE secret key 15 | /// * new_params: the parameters of the new LWE secret key 16 | pub fn generate_keyswitch_key_lwe<S>( 17 | keyswitch_key: &mut LweKeyswitchKeyRef<S>, 18 | original_lwe_secret_key: &LweSecretKeyRef<S>, 19 | new_lwe_secret_key: &LweSecretKeyRef<S>, 20 | old_params: &LweDef, 21 | new_params: &LweDef, 22 | radix: &RadixDecomposition, 23 | ) where 24 | S: TorusOps, 25 | { 26 | old_params.assert_valid(); 27 | new_params.assert_valid(); 28 | radix.assert_valid::<S>(); 29 | new_lwe_secret_key.assert_is_valid(new_params.dim); 30 | original_lwe_secret_key.assert_is_valid(old_params.dim); 31 | new_lwe_secret_key.assert_is_valid(new_params.dim); 32 | keyswitch_key.assert_is_valid((old_params.dim, new_params.dim, radix.count)); 33 | 34 | let decomposition_radix_log = radix.radix_log.0; 35 | 36 | for (i, row) in keyswitch_key.rows_mut(new_params, radix).enumerate() { 37 | let s_i = original_lwe_secret_key.s()[i]; 38 | 39 | for (j, col) in row.lwe_ciphertexts_mut(new_params).enumerate() { 40 | // The factor is q / B^{i+1}. Since B is a power of 2, this is equivalent to 41 | // multiplying by 2^{log2(q) - log2(B) * (i + 1)} 42 | let decomp_factor = 43 | S::from_u64(0x1 << (S::BITS as usize - decomposition_radix_log * (j + 1))); 44 | 45 | let msg = decomp_factor * s_i; 46 | 47 | encrypt_lwe_ciphertext(col, new_lwe_secret_key, Torus::from(msg), new_params); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/keyswitch/mod.rs: -------------------------------------------------------------------------------- 1 | /// Methods for performing a private functional keyswitch (PFKS) 2 | pub mod private_functional_keyswitch; 3 | 4 | /// Methods for performing a public functional keyswitch (PuFKS) 5 | pub mod public_functional_keyswitch; 6 | 7 | /// Generate LWE keyswitch keys. 8 | pub mod lwe_keyswitch_key; 9 | 10 | /// Generate GLWE keyswitch keys. 11 | pub mod glwe_keyswitch_key; 12 | 13 | /// Methods for performing a LWE keyswitch. 14 | pub mod lwe_keyswitch; 15 | 16 | /// Methods for performing a GLWE keyswitch. 17 | pub mod glwe_keyswitch; 18 | -------------------------------------------------------------------------------- /sunscreen_tfhe/src/ops/mod.rs: -------------------------------------------------------------------------------- 1 | /// Ciphertext operations where one of the operands is in FFT form. 2 | pub mod fft_ops; 3 | 4 | /// Methods for key switching a ciphertext from one key to another, potentially 5 | /// switching the parameters at the same time. 6 | pub mod keyswitch; 7 | 8 | /// Methods for bootstrapping an LWE ciphertext from one key to another, while 9 | /// refreshing the noise in the ciphertext. 10 | pub mod bootstrapping; 11 | 12 | /// Methods for homomorphic operations on ciphertexts. 13 | pub mod homomorphisms; 14 | 15 | /// Methods for operating on different ciphertext types. 16 | pub mod ciphertext; 17 | 18 | /// Methods for encrypting and decrypting to various ciphertext types. 19 | pub mod encryption; 20 | 21 | /// Methods for working with polynomials. 22 | pub mod polynomial; 23 | -------------------------------------------------------------------------------- /sunscreen_zkp_backend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sunscreen_zkp_backend" 3 | version = "0.8.1" 4 | edition = "2021" 5 | 6 | authors = ["Sunscreen"] 7 | rust-version = "1.56.0" 8 | license = "AGPL-3.0-only" 9 | description = "This crate contains the ZKP backend of the Sunscreen compiler." 10 | homepage = "https://sunscreen.tech" 11 | repository = "https://github.com/Sunscreen-tech/Sunscreen" 12 | documentation = "https://bfv-docs.sunscreen.tech" 13 | keywords = ["FHE", "BFV", "lattice", "cryptography"] 14 | categories = ["cryptography"] 15 | readme = "crates-io.md" 16 | 17 | [dependencies] 18 | curve25519-dalek = { workspace = true } 19 | bulletproofs = { workspace = true, optional = true } 20 | crypto-bigint = { workspace = true } 21 | merlin = { workspace = true, optional = true } 22 | petgraph = { workspace = true } 23 | rand = { workspace = true } 24 | sunscreen_compiler_common = { workspace = true } 25 | serde = { workspace = true } 26 | thiserror = { workspace = true } 27 | static_assertions = { workspace = true } 28 | log = { workspace = true } 29 | 30 | [features] 31 | default = ["bulletproofs"] 32 | bulletproofs = ["dep:bulletproofs", "dep:merlin"] 33 | -------------------------------------------------------------------------------- /sunscreen_zkp_backend/crates-io.md: -------------------------------------------------------------------------------- 1 | This crate is a component of the [Sunscreen compiler](https://crates.io/crates/sunscreen). -------------------------------------------------------------------------------- /sunscreen_zkp_backend/src/exec.rs: -------------------------------------------------------------------------------- 1 | use crate::BigInt; 2 | use sunscreen_compiler_common::{CompilationResult, Operation as OperationTrait}; 3 | 4 | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] 5 | pub enum Operation { 6 | Input(usize), 7 | 8 | /** 9 | * A hidden input. When running the prover's algorithm, this will 10 | * be [`Some`]. Otherwise [`None`]. 11 | */ 12 | HiddenInput(Option<BigInt>), 13 | 14 | Add, 15 | 16 | Mul, 17 | 18 | Sub, 19 | 20 | Neg, 21 | 22 | Constraint(BigInt), 23 | 24 | Constant(BigInt), 25 | } 26 | 27 | impl OperationTrait for Operation { 28 | fn is_binary(&self) -> bool { 29 | matches!(self, Operation::Add | Operation::Sub | Operation::Mul) 30 | } 31 | 32 | fn is_commutative(&self) -> bool { 33 | matches!(self, Operation::Add | Operation::Mul) 34 | } 35 | 36 | fn is_unary(&self) -> bool { 37 | matches!(self, Operation::Neg) 38 | } 39 | 40 | fn is_unordered(&self) -> bool { 41 | matches!(self, Operation::Constraint(_)) 42 | } 43 | 44 | fn is_ordered(&self) -> bool { 45 | false 46 | } 47 | } 48 | 49 | /** 50 | * A ZKP program that has been JIT'd and is ready for use in a ZKP backend. 51 | */ 52 | pub type ExecutableZkpProgram = CompilationResult<Operation>; 53 | -------------------------------------------------------------------------------- /website/deploy.bash: -------------------------------------------------------------------------------- 1 | aws s3 cp src/* s3://sunscreen-site 2 | aws cloudfront create-invalidation --distribution-id E10NWJGCYHNFM4 --paths "/*" 3 | --------------------------------------------------------------------------------