From e3c8d6389cbf0b13a2d89b797d24f7c385ed06b7 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 12:42:58 +0200 Subject: [PATCH 01/30] move schemes to new schemes repository and make it run --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/bug_report.md | 46 ++ .github/ISSUE_TEMPLATE/documentation.md | 29 + .github/ISSUE_TEMPLATE/feature_request.md | 33 + .github/pull_request_template.md | 38 + .github/workflows/main.yml | 89 +++ .github/workflows/pull_request.yml | 88 +++ .github/workflows/push.yml | 57 ++ .gitignore | 14 + CITATION.cff | 24 + Cargo.toml | 23 + LICENSE | 373 ++++++++++ README.md | 71 +- benches/README.md | 94 +++ benches/benchmarks.rs | 16 + benches/k_pke.rs | 145 ++++ benches/pfdh.rs | 60 ++ benches/regev.rs | 59 ++ src/hash.rs | 31 + src/hash/sha256.rs | 373 ++++++++++ src/hash/sis.rs | 270 +++++++ src/identity_based_encryption.rs | 82 +++ .../dual_regev_ibe.rs | 577 +++++++++++++++ src/lib.rs | 50 ++ src/pk_encryption.rs | 173 +++++ src/pk_encryption/ccs_from_ibe.rs | 143 ++++ .../ccs_from_ibe/dual_regev_ibe_pfdh.rs | 127 ++++ src/pk_encryption/dual_regev.rs | 670 +++++++++++++++++ .../dual_regev_discrete_gauss.rs | 697 ++++++++++++++++++ src/pk_encryption/k_pke.rs | 274 +++++++ src/pk_encryption/lpr.rs | 693 +++++++++++++++++ src/pk_encryption/regev.rs | 672 +++++++++++++++++ src/pk_encryption/regev_discrete_gauss.rs | 692 +++++++++++++++++ src/pk_encryption/ring_lpr.rs | 645 ++++++++++++++++ src/signature.rs | 49 ++ src/signature/fdh.rs | 42 ++ src/signature/fdh/gpv.rs | 202 +++++ src/signature/fdh/gpv_ring.rs | 229 ++++++ src/signature/pfdh.rs | 42 ++ src/signature/pfdh/gpv.rs | 173 +++++ 40 files changed, 8165 insertions(+), 1 deletion(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/documentation.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/pull_request.yml create mode 100644 .github/workflows/push.yml create mode 100644 .gitignore create mode 100644 CITATION.cff create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 benches/README.md create mode 100644 benches/benchmarks.rs create mode 100644 benches/k_pke.rs create mode 100644 benches/pfdh.rs create mode 100644 benches/regev.rs create mode 100644 src/hash.rs create mode 100644 src/hash/sha256.rs create mode 100644 src/hash/sis.rs create mode 100644 src/identity_based_encryption.rs create mode 100644 src/identity_based_encryption/dual_regev_ibe.rs create mode 100644 src/lib.rs create mode 100644 src/pk_encryption.rs create mode 100644 src/pk_encryption/ccs_from_ibe.rs create mode 100644 src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs create mode 100644 src/pk_encryption/dual_regev.rs create mode 100644 src/pk_encryption/dual_regev_discrete_gauss.rs create mode 100644 src/pk_encryption/k_pke.rs create mode 100644 src/pk_encryption/lpr.rs create mode 100644 src/pk_encryption/regev.rs create mode 100644 src/pk_encryption/regev_discrete_gauss.rs create mode 100644 src/pk_encryption/ring_lpr.rs create mode 100644 src/signature.rs create mode 100644 src/signature/fdh.rs create mode 100644 src/signature/fdh/gpv.rs create mode 100644 src/signature/fdh/gpv_ring.rs create mode 100644 src/signature/pfdh.rs create mode 100644 src/signature/pfdh/gpv.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..80d0efd --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +@qfall/pg diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..fd1cc88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + + + +**Describe the bug** + + +**To Reproduce** + + +```rust +// write your code here +``` + +**Expected behavior** + + + +**Screenshots** + + + +**Desktop (please complete the following information):** + - OS: + - Version of qFALL-crypto: + +**Additional context** + + +**Solution** + diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000..d7dbb63 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,29 @@ +--- +name: Documentation +about: Report a problem with the documentation +title: '' +labels: "documentation" +assignees: '' + +--- + + + +**Documentation** + +Type of documentation issue: + + +**Where can we find it** + + +**Please describe what the current documentation is lacking** + + +**Solution** + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..6a21403 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,33 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + + +**Description** + + +**Motivation** + + +**Best available solution** + +```rust +// write your API call to the new feature here +``` + +**Additional context** + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..e9d687f --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,38 @@ +**Description** + + + +This PR implements... +- [ ] feature/ revision/ hotfix/ optimisation/ ... + +for/ of `Component`. + + + +**Testing** + + + + +- [ ] I added basic working examples (possibly in doc-comment) +- [ ] I triggered all possible errors in my test in every possible way +- [ ] I included tests for all reasonable edge cases +- [ ] I provided an intuition regarding how certain inputs have to be set + + +**Checklist:** + + + +- [ ] I have performed a self-review of my own code + - [ ] The code provides good readability and maintainability s.t. it fulfills best practices like talking code, modularity, ... + - [ ] The chosen implementation is not more complex than it has to be + - [ ] My code should work as intended and no side effects occur (e.g. memory leaks) + - [ ] The doc comments fit our style guide + - [ ] I have credited related sources if needed diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..1100b27 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,89 @@ +name: Pipeline + +on: + push: + branches: [main] + +env: + CARGO_TERM_COLOR: always + RUSTDOCFLAGS: "-Dwarnings" + +jobs: + full_pipeline: + name: Full Pipeline + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup dtolnay/rust-toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + components: clippy, rustfmt + + # load project cache to reduce compilation time + - name: Setup project cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: release-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }} + restore-keys: release-${{ runner.os }}-cargo- + + - name: Set environment variables + run: | + echo "PROJECT_NAME=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0] | [ .name ] | join("")')" >> $GITHUB_ENV + echo "PROJECT_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0] | [ .version ] | join("")')" >> $GITHUB_ENV + + - name: Update dependencies + run: cargo update + - name: Build + run: cargo build --release + - name: Generate docs + run: cargo doc + - name: Run doc tests # Unit tests are run by tarpaulin + run: cargo test --doc --verbose + + - name: Install cargo-tarpaulin + uses: baptiste0928/cargo-install@v2 + with: + crate: cargo-tarpaulin + - name: Calculate test coverage + run: cargo tarpaulin --out Html + - name: Archive code coverage results + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT_NAME }}-code_coverage_report-v${{ env.PROJECT_VERSION }} + path: tarpaulin-report.html + + # Lints: Clippy and Fmt + - name: Clippy + run: cargo clippy -- -D warnings + - name: Format + run: cargo fmt --all -- --check + + # Cargo check for security issues + - name: Install cargo-audit + uses: baptiste0928/cargo-install@v2 + with: + crate: cargo-audit + - name: Security audit + run: cargo audit + + # Check for outdated dependencies + - name: Install cargo-outdated + uses: dtolnay/install@cargo-outdated + - name: Outdated dependencies + run: cargo outdated --exit-code 1 + + - name: Archive release build + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT_NAME }}-release_build-v${{ env.PROJECT_VERSION }} + path: target/release/${{ env.PROJECT_NAME }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000..2f0e0d1 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,88 @@ +name: Pipeline +# consistency regarding formatting and idiomatic Rust + +on: + push: + branches: + - dev + pull_request: + branches: + - "**" + +env: + CARGO_TERM_COLOR: always + RUSTDOCFLAGS: "-Dwarnings" + +jobs: + pipeline: + name: Pipeline - code coverage + dependency check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup dtolnay/rust-toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + components: clippy, rustfmt + + # load project cache to reduce compilation time + - name: Setup project cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }} + restore-keys: ${{ runner.os }}-cargo- + + - name: Set environment variables + run: | + echo "PROJECT_NAME=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0] | [ .name ] | join("")')" >> $GITHUB_ENV + echo "PROJECT_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0] | [ .version ] | join("")')" >> $GITHUB_ENV + + - name: Update dependencies + run: cargo update + - name: Build + run: cargo build + - name: Generate docs + run: cargo doc + - name: Run doc tests # Unit tests are run by tarpaulin + run: cargo test --doc --verbose + + - name: Install cargo-tarpaulin + uses: baptiste0928/cargo-install@v2 + with: + crate: cargo-tarpaulin + - name: Calculate test coverage + run: cargo tarpaulin --out Html + - name: Archive code coverage results + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT_NAME }}-code_coverage_report-v${{ env.PROJECT_VERSION }} + path: tarpaulin-report.html + + # Lints: Clippy and Fmt + - name: Clippy + run: cargo clippy -- -D warnings + - name: Format + run: cargo fmt --all -- --check + + # Cargo check for security issues + - name: Install cargo-audit + uses: baptiste0928/cargo-install@v2 + with: + crate: cargo-audit + - name: Security audit + run: cargo audit + + # Check for outdated dependencies + - name: Install cargo-outdated + uses: dtolnay/install@cargo-outdated + - name: Outdated dependencies + run: cargo outdated --exit-code 1 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000..9342bc7 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,57 @@ +name: Pipeline +# consistency regarding formatting and idiomatic Rust + +on: + push: + branches-ignore: + - main + - dev + +env: + CARGO_TERM_COLOR: always + RUSTDOCFLAGS: "-Dwarnings" + +jobs: + pipeline: + name: Pipeline - test and lints + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup dtolnay/rust-toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + components: clippy, rustfmt + + # load project cache to reduce compilation time + - name: Setup project cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }} + restore-keys: ${{ runner.os }}-cargo- + + - name: Update dependencies + run: cargo update + - name: Build + run: cargo build + - name: Generate docs + run: cargo doc + + - name: Test + run: cargo test --verbose + + # Lints: Clippy and Fmt + - name: Clippy + run: cargo clippy -- -D warnings + - name: Format + run: cargo fmt --all -- --check + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a735b44 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# ignore files generated while using flamegraph +perf.* +flamegraph.svg diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..b1125c4 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,24 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: qFALL-schemes +message: "University Paderborn, Codes and Cryptography" +type: software +authors: + - given-names: Laurens + family-names: Porzenheim + - given-names: Marvin + family-names: Beckmann + - given-names: Paul + family-names: Kramer + - given-names: Phil + family-names: Milewski + - given-names: Sven + family-names: Moog + - given-names: Marcel + family-names: Schmidt + - given-names: Niklas + family-names: Siemer +repository-code: "https://github.com/qfall/schemes" +license: MPL-2.0 diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7db6c95 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "qfall-schemes" +version = "0.1.0" +edition = "2021" +autobenches = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +qfall-crypto = { git = "https://github.com/qfall/crypto", branch="move-schemes" } +qfall-math = { git = "https://github.com/qfall/math", rev="5f50c9cd31c869462d959774fb4b51fcd1727dbe" } +sha2 = "0.10.6" +serde = {version="1.0", features=["derive"]} +serde_json = "1.0" +typetag = "0.2" +criterion = { version = "0.7", features = ["html_reports"] } + +[profile.bench] +debug = true + +[[bench]] +name = "benchmarks" +harness = false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/README.md b/README.md index 8df5b90..4a0095e 100644 --- a/README.md +++ b/README.md @@ -1 +1,70 @@ -# schemes +# qFALL-schemes + +[![made-with-rust](https://img.shields.io/badge/Made%20with-Rust-1f425f.svg)](https://www.rust-lang.org/) +[![CI](https://github.com/qfall/crypto/actions/workflows/push.yml/badge.svg?branch=dev)](https://github.com/qfall/schemes/actions/workflows/pull_request.yml) +[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0) + +This repository is currently being developed by the project group [qFALL - quantum resistant fast lattice library](https://cs.uni-paderborn.de/cuk/lehre/veranstaltungen/ws-2022-23/project-group-qfall) in the winter term 2022 and summer term 2023 by the Codes and Cryptography research group in Paderborn. + +The main objective of this project is to provide researchers and students with the possibility to easily and quickly prototype (lattice-based) cryptography. + +## Disclaimer + +Currently, we are in the development phase and interfaces might change. +Feel free to check out the current progress, but be aware, that the content will +change in the upcoming weeks and months. An official release will most likely be published in the second half of 2023. + +## Quick-Start + +Please refer to [our website](https://qfall.github.io/) as central information point. + +To install and add our library to your project, please refer to [our tutorial](https://qfall.github.io/book/index.html). +It provides a step-by-step guide to install the required libraries and gives further insights in the usage of our crates. + +## What does qFALL-schemes offer? + +qFALL-crypto offers a variety of implementations of cryptographic schemes, constructions, and primitives. +We provide a brief overview in the following list. +For a more detailed description, please refer to [our tutorial section](https://qfall.github.io/book/crypto/features.html). + +Full-fledged Cryptographic Features + +- [Public Key Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption.rs) + - [LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/regev.rs) + - [Dual LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/dual_regev.rs) + - [LPR Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/lpr.rs) + - [Ring-based LPR Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/ring_lpr.rs) + - [CCA-secure Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/ccs_from_ibe.rs) +- [Signatures](https://github.com/qfall/crypto/blob/dev/src/construction/signature.rs) + - [Full-Domain Hash (FDH)](https://github.com/qfall/crypto/blob/dev/src/construction/signature/fdh.rs) + - [Probabilistic FDH (PFDH)](https://github.com/qfall/crypto/blob/dev/src/construction/signature/pfdh.rs) + - [Ring-based FDH](https://github.com/qfall/crypto/blob/dev/src/construction/signature/fdh/gpv_ring.rs) +- [Identity Based Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/identity_based_encryption.rs) + - [From Dual LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/identity_based_encryption/dual_regev_ibe.rs) +- [Hash Functions](https://github.com/qfall/crypto/blob/dev/src/construction/hash.rs) + - [SIS-Hash Function](https://github.com/qfall/crypto/blob/dev/src/construction/hash/sis.rs) + - [SHA-256-based Hash](https://github.com/qfall/crypto/blob/dev/src/construction/hash/sha256.rs) + +## License + +This library is distributed under the **Mozilla Public License Version 2.0** which can be found here [License](https://github.com/qfall/crypto/blob/dev/LICENSE). +Permissions of this weak copyleft license are conditioned on making available source code of licensed files and modifications of those files under the same license (or in certain cases, one of the GNU licenses). Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. However, a larger work using the licensed work may be distributed under different terms and without source code for files added in the larger work. + +## Citing + +Please use the following bibtex entry to cite [qFALL-schemes](https://github.com/qfall/schemes): + +```text +@misc{qFALL-crypto, + author = {Porzenheim, Laurens and Beckmann, Marvin and Kramer, Paul and Milewski, Phil and Moog, Sven and Schmidt, Marcel and Siemer, Niklas}, + title = {qFALL-crypto v0.0}, + howpublished = {Online: \url{https://github.com/qfall/crypto}}, + month = Mar, + year = 2023, + note = {University Paderborn, Codes and Cryptography} +} +``` + +## Get in Touch + +One can contact the members of the project group with our mailing list `pg-qfall(at)lists.upb.de`. diff --git a/benches/README.md b/benches/README.md new file mode 100644 index 0000000..3d692bc --- /dev/null +++ b/benches/README.md @@ -0,0 +1,94 @@ + + +# How to run benchmarks: + +## Criterion + +We use criterion for statistical analysis. A plotting library has to be installed to generate graphs. You can find more information and help here: + +- [Criterion-rs GitHub](https://github.com/bheisler/criterion.rs) +- [Cargo-criterion GitHub](https://github.com/bheisler/cargo-criterion) +- [Criterion Book](https://bheisler.github.io/criterion.rs/book/criterion_rs.html) (!Watchout for the criterion version, as of writing this the book is not on the latest version!) + +### Commands + +a) `cargo criterion ` +Has to be installed with `cargo install cargo-criterion`. +Pros: + +- You can remove `features = ["html_reports"]` from the `Cargo.toml` leading to a (slightly) faster compile times. +- Criterion aims to move to just using cargo criterion +- The large Probability Density Function graph shows the samples and marks the outlier categorization boarders. +- Can use either [gnuplot](http://www.gnuplot.info/) or [plotters](https://crates.io/crates/plotters) + +b) `cargo bench ` +Pros: + +- Can visualize the change in performance compared to previous run or other baseline + Cons: +- Can only use [gnuplot](http://www.gnuplot.info/) + +## Flamegraph + +You can also run the benchmarks using the profiler flamegraph. Details can be found here: + +- [Flamegraph GitHub](https://github.com/flamegraph-rs/flamegraph). + This provides insights on the execution time of the executed functions and their subroutines. + +Note: Flamegraph does not work in WSL + +### Command + +`cargo flamegraph --freq 63300 --bench benchmarks -- --bench --profile-time 5 ` +Generates a flamegraph that allows to approximate how long each function executes. The accuracy of the approximation is better the more samples are produced. This can be improved by + +- increasing the sample frequency (`--freq 63300`), This frequency is throttled to the highest possible frequency which depends on the cpu, cpu-temperature, power settings and much more... +- increasing `profile-time` (in seconds). This is how long the benchmark code will be executed. + This parameter also disables the statistical analysis of criterion which prevents it from showing up in the graph. + This parameter is optional, but suggested. + +The flamegraph can be overwhelming since it exposes a lot of internal workings of rust, criterion, and more. +The easiest way to find the function you are looking for is to search for it with `Ctrl + F`. +You have to enter a part of the rust function name or regex (not the benchmark name). + +# How to create benchmarks + +## No appropriate file exists so far: + +1. create the file +2. Insert in new file: + + ```rust + use criterion::*; + + criterion_group!(benches); + ``` + +3. Insert in [benchmarks.rs](/benches/benchmarks.rs): + ```rust + pub mod ; + ``` + and `::benches` in the `criterion_main!` macro. + +## Appropriately named benchmark file exists in `/benches` (e.g. `integer.rs`) + +1. Create a function that performs the functionality that should be benchmarked (called `do_stuff` below). +2. Add a function to handle the interaction with criterion. + e.g.: + ```rust + /// Add Comment describing the benchmark here + pub fn bench_do_stuff(c: &mut Criterion) { + c.bench_function("", |b| b.iter(|| do_stuff())); + } + ``` + The benchmark name specified here is later used to select which benchmark to run and also displayed in the output. + This function can also look differently, for example, because it uses [criterion groups](https://docs.rs/criterion/latest/criterion/struct.BenchmarkGroup.html). +3. Add function created in step 2 in the `criterion_group!` macro (bottom of file). diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs new file mode 100644 index 0000000..c20d019 --- /dev/null +++ b/benches/benchmarks.rs @@ -0,0 +1,16 @@ +// Copyright © 2023 Sven Moog +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . +//! This file collects the benchmarks from other files. + +use criterion::criterion_main; + +pub mod k_pke; +pub mod pfdh; +pub mod regev; + +criterion_main! {regev::benches, pfdh::benches, k_pke::benches} diff --git a/benches/k_pke.rs b/benches/k_pke.rs new file mode 100644 index 0000000..88ec1e1 --- /dev/null +++ b/benches/k_pke.rs @@ -0,0 +1,145 @@ +// Copyright © 2025 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +use criterion::*; +use qfall_schemes::pk_encryption::PKEncryptionScheme; +use qfall_schemes::pk_encryption::KPKE; + +/// Performs a full-cycle of gen, enc, dec with [`KPKE`]. +fn kpke_cycle(k_pke: &KPKE) { + let (pk, sk) = k_pke.gen(); + let cipher = k_pke.enc(&pk, 1); + let _ = k_pke.dec(&sk, &cipher); +} + +/// Benchmark [kpke_cycle] with [KPKE::ml_kem_512]. +/// +/// This benchmark can be run with for example: +/// - `cargo criterion K-PKE\ cycle\ 512` +/// - `cargo bench --bench benchmarks K-PKE\ cycle\ 512` +/// - `cargo flamegraph --bench benchmarks -- --bench K-PKE\ cycle\ 512` +fn bench_kpke_cycle_512(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_512(); + + c.bench_function("K-PKE cycle 512", |b| b.iter(|| kpke_cycle(&k_pke))); +} + +/// Benchmark [KPKE::gen] with [KPKE::ml_kem_512]. +fn bench_kpke_gen_512(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_512(); + + c.bench_function("K-PKE gen 512", |b| b.iter(|| k_pke.gen())); +} + +/// Benchmark [KPKE::enc] with [KPKE::ml_kem_512]. +fn bench_kpke_enc_512(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_512(); + let (pk, _) = k_pke.gen(); + let msg = i64::MAX; + + c.bench_function("K-PKE enc 512", |b| b.iter(|| k_pke.enc(&pk, msg))); +} + +/// Benchmark [KPKE::dec] with [KPKE::ml_kem_512]. +fn bench_kpke_dec_512(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_512(); + let (pk, sk) = k_pke.gen(); + let cipher = k_pke.enc(&pk, i64::MAX); + + c.bench_function("K-PKE dec 512", |b| b.iter(|| k_pke.dec(&sk, &cipher))); +} + +/// Benchmark [kpke_cycle] with [KPKE::ml_kem_768]. +/// +/// This benchmark can be run with for example: +/// - `cargo criterion K-PKE\ cycle\ 768` +/// - `cargo bench --bench benchmarks K-PKE\ cycle\ 768` +/// - `cargo flamegraph --bench benchmarks -- --bench K-PKE\ cycle\ 768` +fn bench_kpke_cycle_768(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_768(); + + c.bench_function("K-PKE cycle 768", |b| b.iter(|| kpke_cycle(&k_pke))); +} + +/// Benchmark [KPKE::gen] with [KPKE::ml_kem_768]. +fn bench_kpke_gen_768(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_768(); + + c.bench_function("K-PKE gen 768", |b| b.iter(|| k_pke.gen())); +} + +/// Benchmark [KPKE::enc] with [KPKE::ml_kem_768]. +fn bench_kpke_enc_768(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_768(); + let (pk, _) = k_pke.gen(); + let msg = i64::MAX; + + c.bench_function("K-PKE enc 768", |b| b.iter(|| k_pke.enc(&pk, msg))); +} + +/// Benchmark [KPKE::dec] with [KPKE::ml_kem_768]. +fn bench_kpke_dec_768(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_768(); + let (pk, sk) = k_pke.gen(); + let cipher = k_pke.enc(&pk, i64::MAX); + + c.bench_function("K-PKE dec 768", |b| b.iter(|| k_pke.dec(&sk, &cipher))); +} + +/// Benchmark [kpke_cycle] with [KPKE::ml_kem_1024]. +/// +/// This benchmark can be run with for example: +/// - `cargo criterion K-PKE\ cycle\ 1024` +/// - `cargo bench --bench benchmarks K-PKE\ cycle\ 1024` +/// - `cargo flamegraph --bench benchmarks -- --bench K-PKE\ cycle\ 1024` +fn bench_kpke_cycle_1024(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_1024(); + + c.bench_function("K-PKE cycle 1024", |b| b.iter(|| kpke_cycle(&k_pke))); +} + +/// Benchmark [KPKE::gen] with [KPKE::ml_kem_1024]. +fn bench_kpke_gen_1024(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_1024(); + + c.bench_function("K-PKE gen 1024", |b| b.iter(|| k_pke.gen())); +} + +/// Benchmark [KPKE::enc] with [KPKE::ml_kem_1024]. +fn bench_kpke_enc_1024(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_1024(); + let (pk, _) = k_pke.gen(); + let msg = i64::MAX; + + c.bench_function("K-PKE enc 1024", |b| b.iter(|| k_pke.enc(&pk, msg))); +} + +/// Benchmark [KPKE::dec] with [KPKE::ml_kem_1024]. +fn bench_kpke_dec_1024(c: &mut Criterion) { + let k_pke = KPKE::ml_kem_1024(); + let (pk, sk) = k_pke.gen(); + let cipher = k_pke.enc(&pk, i64::MAX); + + c.bench_function("K-PKE dec 1024", |b| b.iter(|| k_pke.dec(&sk, &cipher))); +} + +criterion_group!( + benches, + bench_kpke_cycle_512, + bench_kpke_gen_512, + bench_kpke_enc_512, + bench_kpke_dec_512, + bench_kpke_cycle_768, + bench_kpke_gen_768, + bench_kpke_enc_768, + bench_kpke_dec_768, + bench_kpke_cycle_1024, + bench_kpke_gen_1024, + bench_kpke_enc_1024, + bench_kpke_dec_1024, +); diff --git a/benches/pfdh.rs b/benches/pfdh.rs new file mode 100644 index 0000000..fc19126 --- /dev/null +++ b/benches/pfdh.rs @@ -0,0 +1,60 @@ +// Copyright © 2023 Marvin Beckmann +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +use criterion::{criterion_group, Criterion}; +use qfall_schemes::signature::{pfdh::PFDHGPV, SignatureScheme}; + +/// Performs a full instantiation with an additional signing and verifying of a signature. +fn pfdh_cycle(n: i64) { + let mut pfdh = PFDHGPV::setup(n, 113, 17, 128); + + let m = "Hello World!"; + + let (pk, sk) = pfdh.gen(); + let sigma = pfdh.sign(m.to_owned(), &sk, &pk); + + pfdh.vfy(m.to_owned(), &sigma, &pk); +} + +/// Benchmark [bench_pfdh_full_cycle] with `n = 8`. +/// +/// This benchmark can be run with for example: +/// - `cargo criterion Full\ Cycle\ PFDH\ n=8` +/// - `cargo bench --bench benchmarks Full\ Cycle\ PFDH\ n=8` +/// - `cargo flamegraph --bench benchmarks -- --bench Full\ Cycle\ PFDH\ n=8` +/// +/// Shorter variants or regex expressions can also be used to specify the +/// benchmark name. The `\ ` is used to escape the space, alternatively, +/// quotation marks can be used. +fn bench_pfdh_full_cycle(c: &mut Criterion) { + c.bench_function("Full Cycle PFDH n=8", |b| b.iter(|| pfdh_cycle(8))); +} + +/// Benchmark [bench_pfdh_signature] with `n = 8`. +/// +/// This benchmark can be run with for example: +/// - `cargo criterion Signing\ PFDH\ n=8` +/// - `cargo bench --bench benchmarks Signing\ PFDH\ n=8` +/// - `cargo flamegraph --bench benchmarks -- --bench Signing\ PFDH\ n=8` +/// +/// Shorter variants or regex expressions can also be used to specify the +/// benchmark name. The `\ ` is used to escape the space, alternatively, +/// quotation marks can be used. +fn bench_pfdh_signature(c: &mut Criterion) { + let mut pfdh = PFDHGPV::setup(8, 113, 17, 128); + + let m = "Hello World!"; + + let (pk, sk) = pfdh.gen(); + + c.bench_function("Signing PFDH n=8", |b| { + b.iter(|| pfdh.sign(m.to_owned(), &sk, &pk)) + }); +} + +criterion_group!(benches, bench_pfdh_full_cycle, bench_pfdh_signature); diff --git a/benches/regev.rs b/benches/regev.rs new file mode 100644 index 0000000..deb5ff3 --- /dev/null +++ b/benches/regev.rs @@ -0,0 +1,59 @@ +// Copyright © 2023 Sven Moog +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +use criterion::*; +use qfall_math::integer::Z; +use qfall_schemes::pk_encryption::PKEncryptionScheme; +use qfall_schemes::pk_encryption::Regev; + +/// Performs a full-cycle of gen, enc, dec with regev. +fn regev_cycle(n: i64) { + let msg = Z::ONE; + let regev = Regev::new_from_n(n); + + let (pk, sk) = regev.gen(); + let cipher = regev.enc(&pk, &msg); + let _ = regev.dec(&sk, &cipher); +} + +/// Benchmark [regev_cycle] with `n = 50`. +/// +/// This benchmark can be run with for example: +/// - `cargo criterion Regev\ n=50` +/// - `cargo bench --bench benchmarks Regev\ n=50` +/// - `cargo flamegraph --bench benchmarks -- --bench Regev\ n=50` +/// +/// Shorter variants or regex expressions can also be used to specify the +/// benchmark name. The `\ ` is used to escape the space, alternatively, +/// quotation marks can be used. +fn bench_regev_cycle(c: &mut Criterion) { + c.bench_function("Regev n=50", |b| b.iter(|| regev_cycle(50))); +} + +/// Benchmark [regev_cycle] with `n = 10, 20, 30, 40, 50, 60` +/// +/// This benchmark can be run with for example: +/// - `cargo criterion "Regev\ n\ sweep"` +/// - `cargo criterion Regev\ n\ sweep/n=20` (only run the n=20 benchmark). +/// - `cargo criterion 'Regev.*n=20'` (only run the n=20 benchmark). +/// - `cargo bench --bench benchmarks Regev\ n\ sweep` +/// +/// Shorter variants or regex expressions can also be used to specify the +/// benchmark name. The `\ ` is used to escape the space, alternatively, +/// quotation marks can be used. +fn bench_regev_cycle_n_sweep(c: &mut Criterion) { + let mut group = c.benchmark_group("Regev n sweep"); + + for n in [10, 20, 30, 40, 50, 60].iter() { + group.bench_function(format!("n={n}"), |b| b.iter(|| regev_cycle(*n))); + } + + group.finish(); +} + +criterion_group!(benches, bench_regev_cycle, bench_regev_cycle_n_sweep); diff --git a/src/hash.rs b/src/hash.rs new file mode 100644 index 0000000..ec3bfea --- /dev/null +++ b/src/hash.rs @@ -0,0 +1,31 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains implementations of hash functions. +//! +//! The main references are listed in the following: +//! - \[1\] Peikert, Chris (2016). +//! A decade of lattice cryptography. +//! In: Theoretical Computer Science 10.4. +//! + +pub mod sha256; +mod sis; + +pub use sis::SISHash; + +/// This trait should be implemented by hashes with domain [`str`]. +pub trait HashInto { + /// Hashes a given String literal. + /// + /// Paramters: + /// - `m`: specifies the string message to be hashed + /// + /// Returns a hash of type Domain. + fn hash(&self, m: &str) -> DigestSpace; +} diff --git a/src/hash/sha256.rs b/src/hash/sha256.rs new file mode 100644 index 0000000..fbfcd77 --- /dev/null +++ b/src/hash/sha256.rs @@ -0,0 +1,373 @@ +// Copyright © 2023 Phil Milewski +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains sha256 hashes into different domains. + +use super::HashInto; +use qfall_math::traits::FromCoefficientEmbedding; +use qfall_math::utils::index::evaluate_indices; +use qfall_math::{ + integer::{MatPolyOverZ, Z}, + integer_mod_q::{MatPolynomialRingZq, MatZq, Modulus, ModulusPolynomialRingZq, Zq}, + traits::MatrixSetEntry, +}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::fmt::Display; + +/// Computes the sha256 hash value of a given String literal. +/// +/// Parameters: +/// - `string`: specifies the value that is hashed. +/// +/// Returns the sha256 value of the given string as a hex string. +/// +/// # Examples +/// ``` +/// use qfall_schemes::hash::sha256::sha256; +/// +/// let string = "Hello World!"; +/// let hash = sha256(string); +/// assert_eq!("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", hash); +/// ``` +pub fn sha256(string: &str) -> String { + let mut hasher = Sha256::new(); + hasher.update(string); + let result = hasher.finalize(); + format!("{result:x}") +} + +/// Hashes a given String literal into a [`Zq`] using sha256. +/// +/// Parameters: +/// - `string`: specifies the value that is hashed. +/// - `modulus`: specifies the modulus of the returned [`Zq`] value +/// +/// Returns a [`Zq`] as a hash value for the given string. +/// +/// # Examples +/// ``` +/// use qfall_schemes::hash::sha256::hash_to_zq_sha256; +/// use qfall_math::integer_mod_q::Zq; +/// +/// let string = "Hello World!"; +/// +/// let hash: Zq = hash_to_zq_sha256("Hello World!", 7); +/// assert_eq!(Zq::from((2, 7)), hash) +/// ``` +/// +/// # Panics ... +/// - if `modulus <= 1`. +pub fn hash_to_zq_sha256(string: &str, modulus: impl Into) -> Zq { + let modulus = modulus.into(); + let modulus_new = Z::from(&modulus); + let bitsize = modulus_new.bits(); + let mut hex = "".to_string(); + let string2 = format!("{modulus_new} {string}"); + + for i in 0..=bitsize / 128 + // hashing into e.g. Zq with 256 bit length of q from 256 bit will result in + // lower values to be up to two times as likely as higher values. + // Doubling the bit size of the hashed number will + // reduce this difference to 1/2^n which is negligible. + // https://crypto.stackexchange.com/questions/37305/how-can-i-instantiate-a-generalized-hash-function + { + hex = hex + &sha256(&format!("{i} {string2}")); + } + + Zq::from((Z::from_str_b(&hex, 16).unwrap(), modulus)) +} + +/// Hashes a given String literal into a [`MatZq`] using sha256. +/// +/// Parameters: +/// - `string`: specifies the value that is hashed +/// - `num_rows`: specifies the number of rows of the result +/// - `num_cols`: specifies the number of columns of the result +/// - `modulus`: specifies the modulus of the returned [`MatZq`] value +/// +/// Returns a [`MatZq`] as a hash for the given string. +/// +/// # Examples +/// ``` +/// use qfall_schemes::hash::sha256::hash_to_mat_zq_sha256; +/// use qfall_math::integer_mod_q::MatZq; +/// use std::str::FromStr; +/// +/// let string = "Hello World!"; +/// +/// let hash: MatZq = hash_to_mat_zq_sha256(string, 2, 2, 7); +/// assert_eq!(MatZq::from_str("[[6, 3],[5, 2]] mod 7").unwrap(), hash); +/// ``` +/// +/// # Panics ... +/// - if `modulus <= 1`. +/// - if the number of rows or columns is less or equal to `0` or does not fit into an [`i64`]. +pub fn hash_to_mat_zq_sha256( + string: &str, + num_rows: impl TryInto + Display, + num_cols: impl TryInto + Display, + modulus: impl Into, +) -> MatZq { + let modulus = modulus.into(); + let (num_rows_new, num_cols_new) = evaluate_indices(num_rows, num_cols).unwrap(); + let mut matrix = MatZq::new(num_rows_new, num_cols_new, modulus.clone()); + + let new_string = format!("{num_rows_new} {num_cols_new} {string}"); + for i in 0..num_rows_new { + for j in 0..num_cols_new { + matrix + .set_entry( + i, + j, + hash_to_zq_sha256(&format!("{i} {j} {new_string}"), &modulus), + ) + .unwrap(); + } + } + matrix +} + +/// Object for hashing Strings into a [`MatZq`]. +/// The object fixes the modulus and the corresponding dimensions. +/// +/// Parameters: +/// - `modulus`: Defines the range in which each entry is hashed +/// - `rows`: Defines the number of rows of the hash value +/// - `cols`: Defines the number of columns of the hash value +/// +/// Returns a [`MatZq`] as a hash for the given string. +/// +/// # Examples +/// ``` +/// use qfall_schemes::hash::{HashInto, sha256::{HashMatZq, hash_to_mat_zq_sha256}}; +/// use qfall_math::integer_mod_q::{Modulus, MatZq}; +/// use std::str::FromStr; +/// +/// let modulus = Modulus::from(7); +/// +/// let hasher = HashMatZq { +/// modulus, +/// rows: 17, +/// cols: 3, +/// }; +/// let hash_val = hasher.hash("Hello"); +/// ``` +#[derive(Serialize, Deserialize)] +pub struct HashMatZq { + pub modulus: Modulus, + pub rows: i64, + pub cols: i64, +} + +impl HashInto for HashMatZq { + /// Hashes a given String literal into a [`MatZq`] using sha256. + /// The dimensions and the modulus is fixed by the hash object. + /// + /// Parameters: + /// - `string`: specifies the value that is hashed + /// + /// Returns a [`MatZq`] as a hash for the given string. + /// + /// # Examples + /// ``` + /// use qfall_schemes::hash::{HashInto, sha256::{HashMatZq, hash_to_mat_zq_sha256}}; + /// use qfall_math::integer_mod_q::{Modulus, MatZq}; + /// use std::str::FromStr; + /// + /// let modulus = Modulus::from(7); + /// + /// let hasher = HashMatZq { + /// modulus, + /// rows: 17, + /// cols: 3, + /// }; + /// let hash_val = hasher.hash("Hello"); + /// ``` + fn hash(&self, m: &str) -> MatZq { + hash_to_mat_zq_sha256(m, self.rows, self.cols, &self.modulus) + } +} + +/// Object for hashing Strings into a [`MatPolynomialRingZq`]. +/// The object fixes the modulus and the corresponding dimensions. +/// +/// Parameters: +/// - `modulus`: Defines the range in which each entry is hashed +/// - `rows`: Defines the number of rows of the hash value +/// - `cols`: Defines the number of columns of the hash value +/// +/// Returns a [`MatZq`] as a hash for the given string. +/// +/// # Examples +/// ``` +/// use qfall_schemes::hash::{HashInto, sha256::HashMatPolynomialRingZq}; +/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; +/// +/// let gp = GadgetParametersRing::init_default(10, 99); +/// +/// let hasher = HashMatPolynomialRingZq { +/// modulus: gp.modulus, +/// rows: 17, +/// cols: 3, +/// }; +/// let hash_val = hasher.hash("Hello"); +/// ``` +#[derive(Serialize, Deserialize)] +pub struct HashMatPolynomialRingZq { + pub modulus: ModulusPolynomialRingZq, + pub rows: i64, + pub cols: i64, +} + +impl HashInto for HashMatPolynomialRingZq { + /// Hashes a given String literal into a [`MatPolynomialRingZq`] using sha256. + /// The dimensions and the modulus is fixed by the hash object. + /// + /// Parameters: + /// - `string`: specifies the value that is hashed + /// + /// Returns a [`MatPolynomialRingZq`] as a hash for the given string. + /// + /// # Examples + /// ``` + /// use qfall_schemes::hash::{HashInto, sha256::{HashMatPolynomialRingZq}}; + /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + /// + /// let gp = GadgetParametersRing::init_default(10, 99); + /// + /// let hasher = HashMatPolynomialRingZq { + /// modulus: gp.modulus, + /// rows: 17, + /// cols: 3, + /// }; + /// let hash_val = hasher.hash("Hello"); + /// ``` + fn hash(&self, m: &str) -> MatPolynomialRingZq { + let highest_deg = self.modulus.get_degree(); + let embedding = + hash_to_mat_zq_sha256(m, self.rows * highest_deg, self.cols, self.modulus.get_q()) + .get_representative_least_nonnegative_residue(); + let poly_mat = MatPolyOverZ::from_coefficient_embedding((&embedding, highest_deg - 1)); + MatPolynomialRingZq::from((&poly_mat, &self.modulus)) + } +} + +#[cfg(test)] +mod tests_sha { + use super::{hash_to_mat_zq_sha256, hash_to_zq_sha256, sha256, Z}; + use qfall_math::{ + integer_mod_q::{MatZq, Zq}, + traits::{Distance, Pow}, + }; + use std::str::FromStr; + + /// Ensure sha256 works. + #[test] + fn test_sha256() { + let str1 = "Hello World!"; + let str2 = "qfall"; + + let hash1 = sha256(str1); + let hash2 = sha256(str2); + + assert_eq!( + "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", + hash1 + ); + assert_eq!( + "eb6ed1369a670050bd04b24036e8c29144b0f6b10166dc9c8b4987a6026c715f", + hash2 + ); + } + + /// Ensure hashing into [`Zq`] works as intended. + #[test] + fn test_hash_to_zq_sha256() { + let str1 = "Hello World!"; + let str2 = "qfall"; + + let hash1 = hash_to_zq_sha256(str1, 256); + let hash2 = hash_to_zq_sha256(str2, 16); + + assert_eq!(Zq::from((150, 256)), hash1); + assert_eq!(Zq::from((12, 16)), hash2); + } + + /// Ensure hashing into [`Zq`] hits the whole domain not just the first 256 bit. + #[test] + fn test_hash_to_zq_sha256_large() { + let str1 = "Hello World!"; + + let mut large = false; + for i in 0..5 { + if hash_to_zq_sha256(&(i.to_string() + str1), Z::from(271).pow(100).unwrap()) + .get_representative_least_nonnegative_residue() + .distance(Z::ZERO) + > u64::MAX + { + large = true; + } + } + + assert!(large); + } + + /// Ensure hashing into [`MatZq`] works as intended. + #[test] + fn test_hash_to_mat_zq_sha256() { + let str1 = "Hello World!"; + let str2 = "qfall"; + + let hash1 = hash_to_mat_zq_sha256(str1, 2, 2, 256); + let hash2 = hash_to_mat_zq_sha256(str2, 2, 2, 16); + + assert_eq!( + MatZq::from_str("[[159, 26],[249, 141]] mod 256").unwrap(), + hash1 + ); + assert_eq!(MatZq::from_str("[[3, 12],[9, 12]] mod 16").unwrap(), hash2); + } + + /// Ensure hashing into [`MatZq`] works as intended. + #[test] + #[should_panic] + fn test_hash_to_mat_zq_sha256_negative_dimensions() { + let str1 = "Hello World!"; + + let _ = hash_to_mat_zq_sha256(str1, 0, 0, 16); + } +} + +#[cfg(test)] +mod hash_into_mat_polynomial_ring_zq { + use super::{HashInto, HashMatPolynomialRingZq}; + use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + use qfall_math::{integer::PolyOverZ, traits::*}; + + /// Ensure that the hash function maps into the correct dimension and it is also + /// static, i.e. the same value is returned, when the same value is hashed. + #[test] + fn correct_dimensions() { + let gp = GadgetParametersRing::init_default(10, 99); + + let hasher = HashMatPolynomialRingZq { + modulus: gp.modulus, + rows: 17, + cols: 3, + }; + let hash_val = hasher.hash("Hello"); + let hash_val_2 = hasher.hash("Hello"); + let entry: PolyOverZ = hash_val.get_entry(0, 0).unwrap(); + + assert_eq!(hasher.rows, hash_val.get_num_rows()); + assert_eq!(hasher.cols, hash_val.get_num_columns()); + assert_eq!(hash_val, hash_val_2); + assert_eq!(9, entry.get_degree()) + } +} diff --git a/src/hash/sis.rs b/src/hash/sis.rs new file mode 100644 index 0000000..7276162 --- /dev/null +++ b/src/hash/sis.rs @@ -0,0 +1,270 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains an implementation of the collision-resistant +//! SIS-based hash function. + +use qfall_math::{error::MathError, integer::Z, integer_mod_q::MatZq, traits::MatrixDimensions}; +use serde::{Deserialize, Serialize}; + +/// This struct keeps an instance of the [`SISHash`] including +/// its key and public parameters implicitly stored as `n = key.#rows()`, +/// `m = key.#columns`, and `q = key.modulus`. +/// +/// This construction is implemented according to the description in [\[1\]](). +/// +/// Attributes: +/// - `n`: specifies the security parameter, which is not equal to the bit-security level +/// - `m`: defines the number of columns of `A` defining this SIS instance +/// - `q`: specifies the modulus +/// +/// # Examples +/// ``` +/// use qfall_schemes::hash::SISHash; +/// use qfall_math::integer_mod_q::MatZq; +/// // setup public parameters and key pair +/// let hash = SISHash::gen(5, 18, 11).unwrap(); +/// +/// // check provable collision-resistance of hash +/// assert!(hash.check_security().is_ok()); +/// +/// // generate something to hash +/// let msg = MatZq::sample_uniform(18, 1, 11); +/// +/// // hash the message +/// let result = hash.hash(&msg); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct SISHash { + key: MatZq, // implicitly contains n = nrows, m = ncols, q = modulus +} + +impl SISHash { + /// Generates a new secret key for an [`SISHash`] instance. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// - `m`: specifies the number of columns of matrix `A` + /// - `q`: specifies the modulus + /// + /// Returns a new instance of a [`SISHash`] function with freshly + /// chosen secret key `A` of type [`MatZq`], dimensions `n x m`, + /// and modulus `q`. Otherwise, a [`MathError`] is returned, if `n <= 0`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::hash::SISHash; + /// + /// let hash = SISHash::gen(5, 18, 11).unwrap(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if `n <= 0`. + pub fn gen(n: impl Into, m: impl Into, q: impl Into) -> Result { + let n: Z = n.into(); + let m: Z = m.into(); + let q: Z = q.into(); + + if n < Z::ONE { + return Err(MathError::InvalidIntegerInput(String::from( + "n must be chosen bigger than 0.", + ))); + } + + let mat_a = MatZq::sample_uniform(&n, &m, q); + + Ok(Self { key: mat_a }) + } + + /// Checks whether the [`SISHash`] instance is provably collision-resistant. + /// + /// Returns an empty result if the instance is provably secure. + /// Otherwise, a [`MathError`] is returned, if or `m < n log q`, + /// or `q <= ⌈sqrt(n log q)⌉` as collision-resistance + /// would otherwise not be ensured. + /// + /// # Examples + /// ``` + /// use qfall_schemes::hash::SISHash; + /// let hash = SISHash::gen(5, 18, 11).unwrap(); + /// + /// assert!(hash.check_security().is_ok()); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if `m < n log q`, or `q <= ⌈sqrt(n log q)⌉` + /// as collision-resistance would otherwise not be ensured. + pub fn check_security(&self) -> Result<(), MathError> { + let n: Z = self.key.get_num_rows().into(); + let m: Z = self.key.get_num_columns().into(); + let q: Z = self.key.get_mod().into(); + + // computed according to bullet point 3 of section 4.1.1 in Decade + let m_bar = (&n * q.log(2).unwrap()).ceil(); + + // m >= m_bar according to bullet point 3 of section 4.1.1 in Decade + if m < m_bar { + return Err(MathError::InvalidIntegerInput(String::from( + "m was chosen smaller than n log q, but it must be larger to satisfy the pigeonhole principle.", + ))); + } + // q > ⌈sqrt(m_bar)⌉ according to bullet point 3 + 1 of section 4.1.1 in Decade + if q <= m_bar.sqrt().ceil() { + return Err(MathError::InvalidIntegerInput(String::from( + "q was chosen smaller than ⌈sqrt(n log q)⌉, but it must be larger to satisfy the pigeonhole principle.", + ))); + } + + Ok(()) + } + + /// Applies f_A to `value`, i.e. computes `A * value`. + /// + /// Parameters: + /// - `value`: specifies an element from the domain, + /// i.e. a column vector of length `m` with modulus `q` + /// + /// Returns the hash digest of dimension `n`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::hash::SISHash; + /// use qfall_math::integer_mod_q::MatZq; + /// use std::str::FromStr; + /// let hash = SISHash::gen(1, 3, 7).unwrap(); + /// let value = MatZq::from_str("[[1],[2],[3]] mod 7").unwrap(); + /// + /// hash.hash(&value); + /// ``` + /// + /// # Panics ... + /// - if `value` isn't a column vector. + /// - if `value` has a different modulus than the [`SISHash`] instance. + /// - if `value` has mismatching dimensions, i.e. the vector length isn't `m`. + pub fn hash(&self, value: &MatZq) -> MatZq { + if !value.is_column_vector() { + panic!("The hashed value has to be a column vector!"); + } + + &self.key * value + } +} + +#[cfg(test)] +mod test_gen { + use super::{SISHash, Z}; + use qfall_math::traits::MatrixDimensions; + + /// Checks whether too small chosen `n` results in an error. + #[test] + fn invalid_n() { + let res_0 = SISHash::gen(0, 2, 2); + let res_1 = SISHash::gen(-1, 2, 2); + let res_2 = SISHash::gen(i64::MIN, 2, 2); + + assert!(res_0.is_err()); + assert!(res_1.is_err()); + assert!(res_2.is_err()); + } + + /// Checks whether too small chosen `m` results in an error in the security check. + #[test] + fn insecure_m() { + let res_0 = SISHash::gen(1, 1, 4).unwrap(); + let res_1 = SISHash::gen(2, 2, 2).unwrap(); + let res_2 = SISHash::gen(4, 5, i64::MAX).unwrap(); + + assert!(res_0.check_security().is_err()); + assert!(res_1.check_security().is_err()); + assert!(res_2.check_security().is_err()); + } + + /// Checks whether too small chosen `q` results in an error in the security check. + #[test] + fn insecure_q() { + let res_0 = SISHash::gen(10, 50, 6).unwrap(); + let res_1 = SISHash::gen(5, 50, 4).unwrap(); + + assert!(res_0.check_security().is_err()); + assert!(res_1.check_security().is_err()); + } + + /// Ensures that a working example returns a proper instance. + #[test] + fn working_example() { + let hash = SISHash::gen(5, 18, 11).unwrap(); + + assert!(hash.check_security().is_ok()); + assert_eq!(5, hash.key.get_num_rows()); + assert_eq!(18, hash.key.get_num_columns()); + assert_eq!(Z::from(11), Z::from(hash.key.get_mod())); + } + + /// Ensures that the expected availability is provided. + #[test] + fn availability() { + let _ = SISHash::gen(4i8, 4i8, 4i8); + let _ = SISHash::gen(4i8, 4i16, 4i32); + let _ = SISHash::gen(4u8, 4i64, 4u16); + let _ = SISHash::gen(4u64, 4u32, 4); + let _ = SISHash::gen(Z::ONE, 4i64, 4u16); + let _ = SISHash::gen(Z::ONE, Z::from(2), Z::from(2)); + } +} + +#[cfg(test)] +mod test_hash { + use super::{MatZq, SISHash, Z}; + use qfall_math::traits::MatrixDimensions; + + /// Ensures that non-column-vectors result in a panic. + #[should_panic] + #[test] + fn not_column_vec() { + let hash = SISHash::gen(1, 3, 7).unwrap(); + let value = MatZq::new(1, 3, 7); + + hash.hash(&value); + } + + /// Ensures that mismatching dimensions result in a panic. + #[should_panic] + #[test] + fn mismatching_dimensions() { + let hash = SISHash::gen(1, 3, 7).unwrap(); + let value = MatZq::new(4, 1, 7); + + hash.hash(&value); + } + + /// Ensures that mismatching moduli result in a panic. + #[should_panic] + #[test] + fn mismatching_moduli() { + let hash = SISHash::gen(1, 3, 7).unwrap(); + let value = MatZq::new(3, 1, 8); + + hash.hash(&value); + } + + /// Ensures that a working example returns a proper instance. + #[test] + fn working_example() { + let hash = SISHash::gen(5, 18, 11).unwrap(); + let value = MatZq::new(18, 1, 11); + + let res = hash.hash(&value); + + assert_eq!(5, res.get_num_rows()); + assert_eq!(1, res.get_num_columns()); + assert_eq!(Z::from(11), Z::from(res.get_mod())); + } +} diff --git a/src/identity_based_encryption.rs b/src/identity_based_encryption.rs new file mode 100644 index 0000000..10cf514 --- /dev/null +++ b/src/identity_based_encryption.rs @@ -0,0 +1,82 @@ +// Copyright © 2023 Phil Milewski +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module provides the trait a struct should implement if it is an +//! instance of a identity based public key encryption scheme. Furthermore, +//! it contains cryptographic schemes implementing the [`IBEScheme`] trait. +//! +//! The main references are listed in the following +//! and will be further referenced in submodules by these numbers: +//! - \[1\] Gentry, Craig and Peikert, Chris and Vaikuntanathan, Vinod (2008). +//! Trapdoors for hard lattices and new cryptographic constructions. +//! In: Proceedings of the fortieth annual ACM symposium on Theory of computing. +//! +//! - \[2\] Regev, Oded (2009). +//! On lattices, learning with errors, random linear codes, and cryptography. +//! In: Journal of the ACM 6. +//! + +mod dual_regev_ibe; + +pub use dual_regev_ibe::DualRegevIBE; +use qfall_math::integer::Z; + +/// This trait should be implemented by every identity-based encryption scheme. +/// It offers a simple interface to use and implements the main functions supported by +/// IBEs. +pub trait IBEScheme { + type MasterPublicKey; + type MasterSecretKey; + type SecretKey; + type Cipher; + type Identity; + + /// Generates a master public key pair `(mpk, msk)` suitable for the specific identity-based encryption scheme (IBE). + /// + /// Returns a tuple `(mpk, msk)` consisting of [`Self::MasterPublicKey`] and [`Self::MasterSecretKey`]. + fn setup(&self) -> (Self::MasterPublicKey, Self::MasterSecretKey); + + /// Extracts a secret key corresponding to the specified `identity` using the master secret key `msk`. + /// + /// Parameters: + /// - `master_pk`: specifies the master public key + /// - `master_sk`: specifies the master secret key used for extracting the secret of `identity` + /// - `identity`: specifies the identity for which the secret key should be extracted + /// + /// Returns a secret key for the specified `identity` as a [`Self::SecretKey`]. + fn extract( + &mut self, + master_pk: &Self::MasterPublicKey, + master_sk: &Self::MasterSecretKey, + identity: &Self::Identity, + ) -> Self::SecretKey; + + /// Encrypts the provided `message` using the master public key `mpk` and `identity` of the recipient. + /// + /// Parameters: + /// - `master_pk`: specifies the master public key used for this IBE + /// - `identity`: specifies the recipient that should be able to decrypt the encrypted message + /// - `message`: specifies the message to be encrypted + /// + /// Returns the encryption of `message` as a [`Self::Cipher`] instance. + fn enc( + &self, + master_pk: &Self::MasterPublicKey, + identity: &Self::Identity, + message: impl Into, + ) -> Self::Cipher; + + /// Decrypts the provided `cipher` using the extracted secret key `sk`. + /// + /// Parameters: + /// - `sk`: specifies the extracted secret key used for decryption + /// - `cipher`: specifies the ciphertext to be decrypted + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; +} diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs new file mode 100644 index 0000000..2ca2814 --- /dev/null +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -0,0 +1,577 @@ +// Copyright © 2023 Phil Milewski +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains an implementation of the IND-CPA secure +//! identity based public key encryption scheme. The encryption scheme is based +//! on [`DualRegevIBE`]. + +use super::IBEScheme; +use crate::{ + hash::sha256::hash_to_mat_zq_sha256, + pk_encryption::{DualRegev, PKEncryptionScheme}, +}; +use qfall_crypto::{ + primitive::psf::{PSF, PSFGPV}, + sample::g_trapdoor::gadget_parameters::GadgetParameters, +}; +use qfall_math::{ + error::MathError, + integer::{MatZ, Z}, + integer_mod_q::{MatZq, Modulus}, + rational::{MatQ, Q}, + traits::{Concatenate, MatrixDimensions, Pow}, +}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// This struct manages and stores the public parameters of a [`IBEScheme`] +/// public key encryption instance based on [\[1\]](). +/// +/// Attributes: +/// - `r`: specifies the Gaussian parameter used by the [`PSF`] +/// - `dual_regev`: a [`DualRegev`] instance with fitting parameters `n`, `m`, `q`, `alpha` +/// - `psf`: specifies the PSF used for extracting secret keys +/// - `storage`: is a [`HashMap`] which stores all previously computed secret keys +/// corresponding to their identities +/// +/// # Examples +/// ``` +/// use qfall_schemes::identity_based_encryption::{DualRegevIBE, IBEScheme}; +/// use qfall_math::integer::Z; +/// // setup public parameters and key pair +/// let mut ibe = DualRegevIBE::default(); +/// let (pk, sk) = ibe.setup(); +/// +/// // extract a identity based secret key +/// let identity = String::from("identity"); +/// let id_sk = ibe.extract(&pk, &sk, &identity); +/// +/// // encrypt a bit +/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 +/// let cipher = ibe.enc(&pk, &identity, &msg); +/// +/// // decrypt +/// let m = ibe.dec(&id_sk, &cipher); +/// +/// assert_eq!(msg, m) +/// ``` +#[derive(Serialize, Deserialize)] +pub struct DualRegevIBE { + pub dual_regev: DualRegev, + pub psf: PSFGPV, + storage: HashMap, +} + +impl DualRegevIBE { + /// Initializes a [`DualRegevIBE`] struct with parameters generated by + /// `DualRegev::new(n, q, r, alpha)` + /// + /// Returns an [`DualRegevIBE`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::DualRegevIBE; + /// + /// let ibe = DualRegevIBE::new(4, 54983, 14, 0.0025); + /// ``` + pub fn new( + n: impl Into, // security parameter + q: impl Into, // modulus + r: impl Into, // Gaussian parameter for sampleD + alpha: impl Into, // Gaussian parameter for sampleZ + ) -> Self { + let n = n.into(); + let q = q.into(); + let r = r.into(); + let alpha = alpha.into(); + + let gadget = GadgetParameters::init_default(&n, &q); + + let log_q = Z::from(&q).log_ceil(2).unwrap(); + let n_log_q = &n * &log_q; + let m = &gadget.m_bar + n_log_q; + + let psf = PSFGPV { gp: gadget, s: r }; + Self { + psf, + dual_regev: DualRegev::new(n, m, q, alpha), + storage: HashMap::new(), + } + } + + /// Initializes a [`DualRegevIBE`] struct with parameters generated by `DualRegev::new_from_n(n)`. + /// + /// **WARNING:** Due to the [`PSF`] this schemes extract algorithm is slow for n > 5. + /// + /// Returns an [`DualRegevIBE`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::DualRegevIBE; + /// + /// let dual_regev = DualRegevIBE::new_from_n(4); + /// ``` + pub fn new_from_n(n: impl Into) -> Self { + let n: Z = n.into(); + if n < 2 { + panic!("Security parameter n has to be larger than 1"); + } + + let n_i64 = i64::try_from(&n).unwrap(); + // these powers are chosen according to experience s.t. at least every + // fifth generation of public parameters outputs a valid pair + // the exponent is only tested for n < 8 + let power = match n_i64 { + 2..=3 => 10, + 4 => 7, + 5..=7 => 6, + _ => 5, + }; + + // generate prime q in [n^power / 2, n^power] + let upper_bound: Z = n.pow(power).unwrap(); + let lower_bound = upper_bound.div_ceil(2); + // prime used due to guide from GPV08 after Proposition 8.1 + // on how to choose appropriate parameters, but prime is not + // necessarily needed for this scheme to be correct or secure + let q = Modulus::from(Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap()); + + let gadget = GadgetParameters::init_default(&n, &q); + let log_q = Z::from(&q).log_ceil(2).unwrap(); + let n_log_q = &n * &log_q; + + // m is computed due to the [`PSFGPV`] implementation + let m = &gadget.m_bar + n_log_q; + let r: Q = m.sqrt(); + let alpha = 1 / (&r * 2 * (&m + Z::ONE).sqrt() * (n).log(2).unwrap()); + + let psf = PSFGPV { gp: gadget, s: r }; + Self { + psf, + dual_regev: DualRegev::new(n, m, q, alpha), + storage: HashMap::new(), + } + } + + /// Checks the public parameters for security according to Theorem 1.1 + /// and Lemma 5.4 of [\[2\]](), as well as + /// the requirements of [\[1\]]()`s eprint version + /// at Section 7.1 of [GPV08 - eprint](https://eprint.iacr.org/2007/432.pdf). + /// + /// The required properties are: + /// - q >= 5 * r * (m + 1) + /// - r >= sqrt(m) + /// - m > (n + 1) * log(q) + /// + /// Returns an empty result if the public parameters guarantees security w.r.t. `n` + /// or a [`MathError`] if the instance would not be secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::DualRegevIBE; + /// let ibe = DualRegevIBE::default(); + /// + /// assert!(ibe.check_security().is_ok()); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// secure Dual Regev public key encryption instance. + pub fn check_security(&self) -> Result<(), MathError> { + let q = Q::from(&self.dual_regev.q); + + // Security requirements + // q >= 5 * r * (m + 1) + if q < (5 * &self.psf.s) * (&self.dual_regev.m + Q::ONE) { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as q < 5 * r * (m + 1), but q >= 5 * r * (m + 1) is required.", + ))); + } + + // r >= sqrt(m) + if self.psf.s < self.dual_regev.m.sqrt() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as r < sqrt(m), but r >= sqrt(m) is required.", + ))); + } + + // m >= (n + 1) * log(q) + if self.dual_regev.m <= (&self.dual_regev.n + 1) * &q.log(2).unwrap() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as m <= (n + 1) * log(q), \ + but m > (n + 1) * log(q) is required.", + ))); + } + + Ok(()) + } + + /// Checks the public parameters for + /// correctness according to Lemma 5.1 of [\[2\]](). + /// + /// The required properties are: + /// - α <= 1/(2 * r * sqrt(m) * log(n)) + /// + /// **WARNING:** Some requirements are missing to ensure overwhelming correctness of the scheme. + /// + /// Returns an empty result if the public parameters guarantee correctness + /// with overwhelming probability or a [`MathError`] if the instance would + /// not be correct. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::DualRegevIBE; + /// let ibe = DualRegevIBE::default(); + /// + /// assert!(ibe.check_correctness().is_ok()); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// correct Dual Regev IBE public key encryption instance. + pub fn check_correctness(&self) -> Result<(), MathError> { + if self.dual_regev.n <= Z::ONE { + return Err(MathError::InvalidIntegerInput(String::from( + "n must be chosen bigger than 1.", + ))); + } + + // α <= 1/(2 * r * sqrt(m) * log(n)) + if self.dual_regev.alpha + > 1 / (2 * &self.psf.s * (&self.dual_regev.m + Z::ONE).sqrt()) + * self.dual_regev.n.log(2).unwrap() + { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as α > 1/(r * sqrt(m) * log(n)), but α <= 1/(2 * r * sqrt(m) * log(n)) is required.", + ))); + } + + Ok(()) + } +} + +impl Default for DualRegevIBE { + /// Initializes a [`DualRegevIBE`] struct with parameters generated by `DualRegevIBE::new_from_n(4)`. + /// This parameter choice is not secure as the dimension of the lattice is too small, + /// but it provides an efficient working example. + /// + /// Returns an [`DualRegevIBE`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::DualRegevIBE; + /// + /// let ibe = DualRegevIBE::default(); + /// ``` + fn default() -> Self { + DualRegevIBE::new_from_n(4) + } +} + +impl IBEScheme for DualRegevIBE { + type Cipher = MatZq; + type MasterPublicKey = MatZq; + type MasterSecretKey = (MatZ, MatQ); + type SecretKey = MatZ; + type Identity = String; + + /// Generates a (pk, sk) pair for the Dual Regev public key encryption scheme + /// by following these steps: + /// - s <- Z_q^n + /// - A <- Z_q^{n x m} + /// - x <- χ^m + /// - p = A^t * s + x + /// + /// Then, `pk = (A, p)` and `sk = s` is output. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::{DualRegevIBE, IBEScheme}; + /// let ibe = DualRegevIBE::default(); + /// + /// let (pk, sk) = ibe.setup(); + /// ``` + fn setup(&self) -> (Self::MasterPublicKey, Self::MasterSecretKey) { + self.psf.trap_gen() + } + + /// Given an identity it extracts a corresponding secret key by using samp_p + /// of the given [`PSF`]. + /// + /// Parameters: + /// - `master_pk`: The master public key for the encryption scheme + /// - `master_sk`: Zhe master secret key of the encryption scheme, namely + /// the trapdoor for the [`PSF`] + /// - `identity`: The identity, for which the corresponding secret key + /// should be returned + /// + /// Returns the corresponding secret key of `identity` under public key + /// `master_pk`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::{IBEScheme, DualRegevIBE}; + /// let mut ibe = DualRegevIBE::default(); + /// let (master_pk, master_sk) = ibe.setup(); + /// + /// let id = String::from("identity"); + /// let sk = ibe.extract(&master_pk, &master_sk, &id); + /// ``` + fn extract( + &mut self, + master_pk: &Self::MasterPublicKey, + master_sk: &Self::MasterSecretKey, + identity: &Self::Identity, + ) -> Self::SecretKey { + // check if it is in the HashMap + if let Some(value) = self.storage.get(&format!( + "{master_pk} {} {} {identity}", + master_sk.0, master_sk.1 + )) { + return value.clone(); + } + + let u = hash_to_mat_zq_sha256(identity, &self.dual_regev.n, 1, &self.dual_regev.q); + let secret_key = self.psf.samp_p(master_pk, master_sk, &u); + + // insert secret key in HashMap + self.storage.insert( + format!("{master_pk} {} {} {identity}", master_sk.0, master_sk.1), + secret_key.clone(), + ); + + secret_key + } + + /// Generates an encryption of `message mod 2` for the provided public key + /// and identity by by calling [`DualRegev::enc()`] on + /// pk = [master_pk | H(id)] which corresponds to to [A | u] in + /// [GPV08 - eprint](https://eprint.iacr.org/2007/432.pdf). + /// Constructing the public key this way yields a identity based public key + /// which secret key can be extracted by the [`PSF`]. + /// + /// Then, `cipher = [u | c]` is output. + /// + /// Parameters: + /// - `master_pk`: specifies the public key, which cis matrix `pk = A` + /// - `identity`: specifies the identity used for encryption + /// - `message`: specifies the message that should be encrypted + /// + /// Returns a cipher of type [`MatZq`] for master_pk an identity. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::{DualRegevIBE, IBEScheme}; + /// let ibe = DualRegevIBE::default(); + /// let (pk, sk) = ibe.setup(); + /// + /// let id = String::from("identity"); + /// let cipher = ibe.enc(&pk, &id, 1); + /// ``` + fn enc( + &self, + master_pk: &Self::MasterPublicKey, + identity: &Self::Identity, + message: impl Into, + ) -> Self::Cipher { + let identity_based_pk = + hash_to_mat_zq_sha256(identity, master_pk.get_num_rows(), 1, master_pk.get_mod()); + self.dual_regev.enc( + &master_pk.concat_horizontal(&identity_based_pk).unwrap(), + message, + ) + } + + /// Decrypts the provided `cipher` using the secret key `sk` by using + /// [`DualRegev::dec()`] + /// + /// Parameters: + /// - `sk_id`: specifies the secret key `sk = s` obtained by extract + /// - `cipher`: specifies the cipher containing `cipher = c` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::identity_based_encryption::{DualRegevIBE, IBEScheme}; + /// use qfall_math::integer::Z; + /// // setup public parameters and key pair + /// let mut ibe = DualRegevIBE::default(); + /// let (pk, sk) = ibe.setup(); + /// + /// // extract a identity based secret key + /// let identity = String::from("identity"); + /// let id_sk = ibe.extract(&pk, &sk, &identity); + /// + /// // encrypt a bit + /// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 + /// let cipher = ibe.enc(&pk, &identity, &msg); + /// + /// // decrypt + /// let m = ibe.dec(&id_sk, &cipher); + /// + /// assert_eq!(msg, m) + /// ``` + fn dec(&self, sk_id: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + self.dual_regev.dec(sk_id, cipher) + } +} + +#[cfg(test)] +mod test_dual_regev_ibe { + use super::DualRegevIBE; + use crate::identity_based_encryption::IBEScheme; + use qfall_math::integer::Z; + + /// Checks whether `new` is available for types implementing [`Into`]. + #[test] + fn new_availability() { + let _ = DualRegevIBE::new(2u8, 2u16, 2u32, 2u64); + let _ = DualRegevIBE::new(2u16, 2u64, 2i32, 2i64); + let _ = DualRegevIBE::new(2i16, 2i64, 2u32, 2u8); + let _ = DualRegevIBE::new(Z::from(2), Z::from(2), 2u8, 2i8); + } + + /// Ensures that `new_from_n` is available for types implementing [`Into`]. + #[test] + #[allow(clippy::needless_borrows_for_generic_args)] + fn availability() { + let _ = DualRegevIBE::new_from_n(4u8); + let _ = DualRegevIBE::new_from_n(4u16); + let _ = DualRegevIBE::new_from_n(4u32); + let _ = DualRegevIBE::new_from_n(4u64); + let _ = DualRegevIBE::new_from_n(4i8); + let _ = DualRegevIBE::new_from_n(4i16); + let _ = DualRegevIBE::new_from_n(4i32); + let _ = DualRegevIBE::new_from_n(4i64); + let _ = DualRegevIBE::new_from_n(Z::from(4)); + let _ = DualRegevIBE::new_from_n(&Z::from(4)); + } + + /// Checks whether `new_from_n` returns an error for invalid input n. + #[test] + #[should_panic] + fn invalid_n() { + DualRegevIBE::new_from_n(1); + } + + /// Checks whether the full-cycle of gen, extract, enc, dec works properly + /// for message 0 and the default. + #[test] + fn cycle_zero_default() { + let msg = Z::ZERO; + let id = String::from("Hello World!"); + let mut cryptosystem = DualRegevIBE::default(); + + let (pk, sk) = cryptosystem.setup(); + let id_sk = cryptosystem.extract(&pk, &sk, &id); + let cipher = cryptosystem.enc(&pk, &id, &msg); + let m = cryptosystem.dec(&id_sk, &cipher); + + assert_eq!(msg, m) + } + + /// Checks whether the full-cycle of gen, extract, enc, dec works properly + /// for message 1 and the default. + #[test] + fn cycle_one_default() { + let msg = Z::ONE; + let id = String::from("Hello World!"); + let mut cryptosystem = DualRegevIBE::default(); + + let (pk, sk) = cryptosystem.setup(); + let id_sk = cryptosystem.extract(&pk, &sk, &id); + let cipher = cryptosystem.enc(&pk, &id, &msg); + let m = cryptosystem.dec(&id_sk, &cipher); + + assert_eq!(msg, m) + } + + /// Checks whether the full-cycle of gen, extract, enc, dec works properly + /// for message 0 and small n. + #[test] + fn cycle_zero_small_n() { + let msg = Z::ZERO; + let id = String::from("Hel213lo World!"); + let mut cryptosystem = DualRegevIBE::new_from_n(5); + + let (pk, sk) = cryptosystem.setup(); + let id_sk = cryptosystem.extract(&pk, &sk, &id); + let cipher = cryptosystem.enc(&pk, &id, &msg); + let m = cryptosystem.dec(&id_sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, extract, enc, dec works properly + /// for message 1 and small n. + #[test] + fn cycle_one_small_n() { + let msg = Z::ONE; + let id = String::from("Hel213lo World!"); + let mut cryptosystem = DualRegevIBE::new_from_n(5); + + let (pk, sk) = cryptosystem.setup(); + let id_sk = cryptosystem.extract(&pk, &sk, &id); + let cipher = cryptosystem.enc(&pk, &id, &msg); + let m = cryptosystem.dec(&id_sk, &cipher); + assert_eq!(msg, m); + } + + /// multi test for different identities, message 1 and small n + #[test] + fn new_from_n() { + for i in 1..=5 { + let msg = Z::ONE; + let id = format!("Hello World!{i}"); + let mut cryptosystem = DualRegevIBE::default(); + + cryptosystem.check_security().unwrap(); + cryptosystem.check_correctness().unwrap(); + + let (pk, sk) = cryptosystem.setup(); + + let id_sk = cryptosystem.extract(&pk, &sk, &id); + for _j in 1..=100 { + let cipher = cryptosystem.enc(&pk, &id, &msg); + let m = cryptosystem.dec(&id_sk, &cipher); + + assert_eq!(msg, m); + } + } + } + + /// checking whether the storage works properly + #[test] + fn extract_storage_same_identity_mk_pk() { + let id = "Hello World!".to_string(); + let mut cryptosystem = DualRegevIBE::default(); + let (pk, sk) = cryptosystem.setup(); + + let id_sk_1 = cryptosystem.extract(&pk, &sk, &id); + let id_sk_2 = cryptosystem.extract(&pk, &sk, &id); + + assert_eq!(id_sk_1, id_sk_2) + } + + /// checking whether the storage works properly for different master secret and public key + /// may fail with small probability + #[test] + fn extract_storage_same_identity_different_mk_pk() { + let id = "Hello World!".to_string(); + let mut cryptosystem = DualRegevIBE::default(); + let (pk_1, sk_1) = cryptosystem.setup(); + let (pk_2, sk_2) = cryptosystem.setup(); + + let id_sk_1 = cryptosystem.extract(&pk_1, &sk_1, &id); + let id_sk_2 = cryptosystem.extract(&pk_2, &sk_2, &id); + + assert_ne!(id_sk_1, id_sk_2) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e5c6264 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,50 @@ +// Copyright © 2023 Niklas Siemer, Marvin Beckmann +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! # What is qFALL-crypto? +//! qFall-crypto provides cryptographic basics such as mathematical primitives, +//! fundamental lattice-based cryptographic constructions, and samplable distributions/ +//! possibilities to sample instances of lattice problems to prototype +//! lattice-based cryptographic constructions and more. +//! +//! Currently qFALL-crypto supports 3 main construction types: +//! - [Identity-Based Encryptions](construction::identity_based_encryption::IBEScheme) +//! - [Public-Key Encryptions](construction::pk_encryption::PKEncryptionScheme) +//! - [Signatures](construction::signature::SignatureScheme) +//! +//! These are identified by traits and then implemented for specific constructions, e.g. +//! [`RingLPR`](construction::pk_encryption::RingLPR). +//! Our library has further primitives useful for prototyping such as +//! [`PSFs`](primitive::psf::PSF) that can be used to implement constructions. +//! +//! qFALL-crypto is free software: you can redistribute it and/or modify it under +//! the terms of the Mozilla Public License Version 2.0 as published by the +//! Mozilla Foundation. See . +//! +//! ## Tutorial + Website +//! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-crypto on our [website](https://qfall.github.io/). +//! The tutorial explains the basic steps starting from installation and +//! continues with basic usage. +//! qFALL-crypto is co-developed together with qFALL-math which provides the basic +//! foundation that is used to implement the cryptographic constructions. +//! +//! This module contains fundamental cryptographic constructions, on which other +//! constructions can be build on. +//! +//! Among others, these include encryption schemes and signature schemes. +//! A construction is always build the same way: +//! +//! 1. A trait that combines the common feature, e.g. +//! [`public key encryption`](pk_encryption::PKEncryptionScheme). +//! 2. Explicit implementations of the trait, e.g. +//! [`RingLPR`](pk_encryption::RingLPR). + +pub mod hash; +pub mod identity_based_encryption; +pub mod pk_encryption; +pub mod signature; diff --git a/src/pk_encryption.rs b/src/pk_encryption.rs new file mode 100644 index 0000000..acb84bc --- /dev/null +++ b/src/pk_encryption.rs @@ -0,0 +1,173 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module provides the trait a struct should implement if it is an +//! instance of a public key encryption scheme. Furthermore, it contains +//! cryptographic schemes implementing the [`PKEncryptionScheme`] or [`PKEncryptionSchemeMut`] trait. +//! +//! The main references are listed in the following +//! and will be further referenced in submodules by these numbers: +//! - \[1\] Peikert, Chris (2016). +//! A decade of lattice cryptography. +//! In: Theoretical Computer Science 10.4. +//! +//! - \[2\] Gentry, Craig and Peikert, Chris and Vaikuntanathan, Vinod (2008). +//! Trapdoors for hard lattices and new cryptographic constructions. +//! In: Proceedings of the fortieth annual ACM symposium on Theory of computing. +//! +//! - \[3\] Regev, Oded (2009). +//! On lattices, learning with errors, random linear codes, and cryptography. +//! In: Journal of the ACM 6. +//! +//! - \[4\] Lindner, R., and C. Peikert (2011). +//! Better key sizes (and attacks) for LWE-based encryption. +//! In: Topics in Cryptology - RSA Conference 2011, Springer. +//! +//! - \[5\] Canetti, R., Halevi, S., and Katz, J. (2004). +//! Chosen-ciphertext security from identity-based encryption. +//! In: Advances in Cryptology - EUROCRYPT 2004. +//! +//! - \[6\] National Institute of Standards and Technology (2024). +//! Module-Lattice-Based Key-Encapsulation Mechanism Standard. +//! Federal Information Processing Standards Publication (FIPS 203). +//! + +mod ccs_from_ibe; +mod dual_regev; +mod dual_regev_discrete_gauss; +mod k_pke; +mod lpr; +mod regev; +mod regev_discrete_gauss; +mod ring_lpr; + +pub use ccs_from_ibe::CCSfromIBE; +pub use dual_regev::DualRegev; +pub use dual_regev_discrete_gauss::DualRegevWithDiscreteGaussianRegularity; +pub use k_pke::KPKE; +pub use lpr::LPR; +use qfall_math::integer::Z; +pub use regev::Regev; +pub use regev_discrete_gauss::RegevWithDiscreteGaussianRegularity; +pub use ring_lpr::RingLPR; + +/// This trait should be implemented by every public key encryption scheme. +/// It offers a simple interface to use and implement PKEs. +pub trait PKEncryptionScheme { + type PublicKey; + type SecretKey; + type Cipher; + + /// Generates a public key pair `(pk, sk)` suitable for the specific scheme. + /// + /// Returns a tuple `(pk, sk)` consisting of [`Self::PublicKey`] and [`Self::SecretKey`]. + fn gen(&self) -> (Self::PublicKey, Self::SecretKey); + + /// Encrypts the provided `message` using the public key `pk`. + /// + /// Parameters: + /// - `pk`: specifies the public key used for encryption + /// - `message`: specifies the message to be encrypted + /// + /// Returns the encryption of `message` as a [`Self::Cipher`] instance. + fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher; + + /// Decrypts the provided `cipher` using the secret key `sk`. + /// + /// Parameters: + /// - `sk`: specifies the secret key used for decryption + /// - `cipher`: specifies the ciphertext to be decrypted + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; +} + +/// This trait just exists s.t. we can pass `self` in as mutable for more advanced constructions, which use a storage. +/// Otherwise, it does exactly the same as [`PKEncryptionScheme`]. +pub trait PKEncryptionSchemeMut { + type PublicKey; + type SecretKey; + type Cipher; + + /// Generates a public key pair `(pk, sk)` suitable for the specific scheme. + /// + /// Returns a tuple `(pk, sk)` consisting of [`Self::PublicKey`] and [`Self::SecretKey`]. + fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey); + + /// Encrypts the provided `message` using the public key `pk`. + /// + /// Parameters: + /// - `pk`: specifies the public key used for encryption + /// - `message`: specifies the message to be encrypted + /// + /// Returns the encryption of `message` as a [`Self::Cipher`] instance. + fn enc(&mut self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher; + + /// Decrypts the provided `cipher` using the secret key `sk`. + /// + /// Parameters: + /// - `sk`: specifies the secret key used for decryption + /// - `cipher`: specifies the ciphertext to be decrypted + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + fn dec(&mut self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; +} + +/// This trait generically implements multi-bit encryption +/// for any scheme implementing the [`PKEncryptionScheme`] trait. +/// +/// It splits the given ciphertext up into its bits and +/// stores the individual encrypted bits as a vector of ciphertexts. +pub trait GenericMultiBitEncryption: PKEncryptionScheme { + /// Encrypts multiple bits by appending several encryptions of single bits. + /// The order of single ciphers is `[c0, c1, ..., cn]`, where `c0` is the least significant bit. + /// Negative values are not allowed. Hence, the absolute value is being encrypted. + /// + /// Parameters: + /// - `pk`: specifies the public key + /// - `message`: specifies the message that should be encryted + /// + /// Returns a cipher of type [`Vec`] containing [`PKEncryptionScheme::Cipher`]. + fn enc_multiple_bits(&self, pk: &Self::PublicKey, message: impl Into) -> Vec { + let message: Z = message.into().abs(); + + let bits = message.to_bits(); + let mut out = vec![]; + for bit in bits { + if bit { + out.push(self.enc(pk, Z::ONE)); + } else { + out.push(self.enc(pk, Z::ZERO)); + } + } + + out + } + + /// Decrypts a multiple bit ciphertext. + /// + /// Parameters: + /// - `sk`: specifies the secret key used for decryption + /// - `cipher`: specifies a slice of ciphers containing several [`PKEncryptionScheme::Cipher`] instances + /// to be decrypted + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + fn dec_multiple_bits(&self, sk: &Self::SecretKey, cipher: &[Self::Cipher]) -> Z { + let mut bits = vec![]; + + for item in cipher { + if self.dec(sk, item) == Z::ZERO { + bits.push(false); + } else { + bits.push(true); + } + } + + Z::from_bits(&bits) + } +} diff --git a/src/pk_encryption/ccs_from_ibe.rs b/src/pk_encryption/ccs_from_ibe.rs new file mode 100644 index 0000000..00d201d --- /dev/null +++ b/src/pk_encryption/ccs_from_ibe.rs @@ -0,0 +1,143 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains a general implementation of an IND-CCA secure +//! public key encryption scheme constructed +//! via an [`IBEScheme`] and a [`SignatureScheme`]. + +use super::PKEncryptionSchemeMut; +use crate::{identity_based_encryption::IBEScheme, signature::SignatureScheme}; +use qfall_math::integer::Z; +use serde::{Deserialize, Serialize}; + +pub mod dual_regev_ibe_pfdh; + +/// This struct manages and stores the public parameters of an [`CCSfromIBE`] +/// public key encryption construction based on [\[5\]](). +/// +/// Attributes: +/// - `ibe`: specifies the IBE scheme used in this construction +/// - `signature`: specifies the signature scheme used in this construction +/// +/// # Examples +/// ``` +/// use qfall_schemes::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; +/// use qfall_math::integer::Z; +/// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); +/// +/// let (pk, sk) = scheme.gen(); +/// let cipher = scheme.enc(&pk, 0); +/// let m = scheme.dec(&sk, &cipher); +/// +/// assert_eq!(Z::ZERO, m); +/// ``` +#[derive(Serialize, Deserialize, Clone)] +pub struct CCSfromIBE +where + IBE::Cipher: ToString, +{ + pub ibe: IBE, + pub signature: Signature, +} + +impl PKEncryptionSchemeMut for CCSfromIBE +where + IBE: IBEScheme, + Signature: SignatureScheme, + IBE::Cipher: ToString, + IBE::MasterPublicKey: Clone, + Signature::PublicKey: Into + Clone, +{ + type Cipher = (Signature::PublicKey, IBE::Cipher, Signature::Signature); + type PublicKey = IBE::MasterPublicKey; + type SecretKey = (IBE::MasterPublicKey, IBE::MasterSecretKey); + + /// Generates a (pk, sk) pair for the CCS construction + /// by following these steps: + /// - (mpk, msk) = ibe.setup() + /// + /// Then, `pk = mpk` and `sk = (mpk, msk)` are returned. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; + /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); + /// + /// let (pk, sk) = scheme.gen(); + /// ``` + fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { + let (pk, sk) = self.ibe.setup(); + (pk.clone(), (pk, sk)) + } + + /// Generates an encryption of `message` for the provided public key by following these steps: + /// - (vrfy_key, sign_key) = signature.gen() + /// - c = ibe.enc(mpk, vrfy_key, message), i.e. encrypt `message` with respect to identity `vrfy_key` + /// - sigma = signature.sign(c, sign_key, vrfy_key), i.e. sign message `c` + /// + /// Then, the ciphertext `(vrfy_key, c, sigma)` is returned. + /// + /// Parameters: + /// - `pk`: specifies the public key `pk = A` + /// - `message`: specifies the message that should be encrypted + /// + /// Returns a cipher consisting of a tuple `cipher = (vrfy_key, c, sigma)`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; + /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); + /// + /// let (pk, sk) = scheme.gen(); + /// let cipher = scheme.enc(&pk, 1); + /// ``` + fn enc(&mut self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { + let (vrfy_key, sign_key) = self.signature.gen(); + + let c = self.ibe.enc(pk, &vrfy_key.clone().into(), message); + let sigma = self.signature.sign(c.to_string(), &sign_key, &vrfy_key); + (vrfy_key, c, sigma) + } + + /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - if signature.vrfy(c, sigma, vrfy_key) is not successful, output -1, otherwise proceed + /// - secret_key = ibe.extract(mpk, msk, vrfy_key), i.e. extract the secret key for identity `vrfy_key` + /// - ibe.dec(secret_key, c) + /// + /// Then, the resulting decryption is returned. + /// + /// Parameters: + /// - `sk`: specifies the secret key `sk = (mpk, msk)` + /// - `cipher`: specifies the cipher containing `cipher = (vrfy_key, c, sigma)` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; + /// use qfall_math::integer::Z; + /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); + /// + /// let (pk, sk) = scheme.gen(); + /// let cipher = scheme.enc(&pk, 1); + /// let m = scheme.dec(&sk, &cipher); + /// + /// assert_eq!(Z::ONE, m); + /// ``` + fn dec(&mut self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + if !self + .signature + .vfy(cipher.1.to_string(), &cipher.2, &cipher.0) + { + return Z::MINUS_ONE; + } + + let secret = self.ibe.extract(&sk.0, &sk.1, &cipher.0.clone().into()); + self.ibe.dec(&secret, &cipher.1) + } +} diff --git a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs new file mode 100644 index 0000000..c607b34 --- /dev/null +++ b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs @@ -0,0 +1,127 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! A classical implementation of the [`CCSfromIBE`] scheme using +//! the [`DualRegevIBE`] and [`PFDH`]. + +use super::CCSfromIBE; +use crate::{identity_based_encryption::DualRegevIBE, signature::pfdh::PFDHGPV}; +use qfall_math::{integer::Z, integer_mod_q::Modulus, rational::Q}; + +impl CCSfromIBE { + /// Initializes a [`CCSfromIBE`] PK encryption scheme from a [`DualRegevIBE`] and + /// a PFDH signature, here [`PFDHGPV`]. + /// + /// Parameters: + /// - `n`: specifies the security parameter + /// - `q`: specifies the modulus + /// - `r`: specifies the Gaussian parameter used for the PSF for the [`PFDHGPV`] + /// - `randomness_length`: specifies the number of bits added to the message before signing + /// - `alpha`: specifies the Gaussian parameter used for encryption in + /// [`DualRegev`](crate::pk_encryption::DualRegev) in the [`DualRegevIBE`] + /// + /// Returns an explicit implementation of an IND-CCA-secure public key + /// encryption scheme. + /// + /// # Example + /// ``` + /// use qfall_schemes::pk_encryption::CCSfromIBE; + /// + /// let mut scheme = CCSfromIBE::init_dr_pfdh(4, 13933, 4, 10.77, 0.0021); + /// ``` + /// + /// # Panics ... + /// - if `q <= 1`. + /// - if `n < 1` or `n` does not fit into an [`i64`]. + pub fn init_dr_pfdh( + n: impl Into, // security parameter + q: impl Into, + randomness_length: impl Into, // added to the message before signing + r: impl Into, // Gaussian parameter for PSF + alpha: impl Into, // Gaussian parameter for Dual Regev Encryption + ) -> Self { + let n = n.into(); + let q = q.into(); + let r = r.into(); + + let dr_ibe = DualRegevIBE::new(&n, &q, &r, alpha); + let pfdh = PFDHGPV::setup(n, q, r, randomness_length); + + Self { + ibe: dr_ibe, + signature: pfdh, + } + } + + /// Initializes a [`CCSfromIBE`] PK encryption scheme from a [`DualRegevIBE`] and + /// a PFDH signature, here [`PFDHGPV`], from a given `n > 0`. + /// + /// Parameters: + /// - `n`: specifies the security parameter + /// + /// Returns an explicit implementation of an IND-CCA-secure public key + /// encryption scheme chosen with appropriate parameters for given `n`.. + /// + /// # Example + /// ``` + /// use qfall_schemes::pk_encryption::CCSfromIBE; + /// + /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); + /// ``` + /// + /// # Panics ... + /// - if `n < 4` or `n` does not fit into an [`i64`]. + pub fn init_dr_pfdh_from_n(n: impl Into) -> Self { + let n = n.into(); + assert!( + n > 3, + "n needs to be chosen larger than 3 for this function to work properly." + ); + + let ibe = DualRegevIBE::new_from_n(&n); + let pfdh = PFDHGPV::setup(&n, &ibe.dual_regev.q, &ibe.psf.s, &n); + + Self { + ibe, + signature: pfdh, + } + } +} + +#[cfg(test)] +mod test_ccs_from_ibe { + use super::CCSfromIBE; + use crate::pk_encryption::PKEncryptionSchemeMut; + use qfall_math::integer::Z; + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and small n. + #[test] + fn cycle_zero() { + let msg = Z::ZERO; + let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc(&pk, &msg); + let m = scheme.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and small n. + #[test] + fn cycle_one() { + let msg = Z::ONE; + let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc(&pk, &msg); + let m = scheme.dec(&sk, &cipher); + assert_eq!(msg, m); + } +} diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs new file mode 100644 index 0000000..9b53d3c --- /dev/null +++ b/src/pk_encryption/dual_regev.rs @@ -0,0 +1,670 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains an implementation of the IND-CPA secure +//! public key Dual Regev encryption scheme. + +use super::{GenericMultiBitEncryption, PKEncryptionScheme}; +use qfall_math::{ + error::MathError, + integer::{MatZ, Z}, + integer_mod_q::{MatZq, Modulus, Zq}, + rational::Q, + traits::{Concatenate, Distance, MatrixGetEntry, MatrixSetEntry, Pow}, +}; +use serde::{Deserialize, Serialize}; + +/// This struct manages and stores the public parameters of a [`DualRegev`] +/// public key encryption instance. +/// +/// Attributes: +/// - `n`: specifies the security parameter, which is not equal to the bit-security level +/// - `m`: defines the dimension of the underlying lattice +/// - `q`: specifies the modulus over which the encryption is computed +/// - `alpha`: specifies the Gaussian parameter used for independent +/// sampling from the discrete Gaussian distribution +/// +/// # Examples +/// ``` +/// use qfall_schemes::pk_encryption::{DualRegev, PKEncryptionScheme}; +/// use qfall_math::integer::Z; +/// // setup public parameters and key pair +/// let dual_regev = DualRegev::default(); +/// let (pk, sk) = dual_regev.gen(); +/// +/// // encrypt a bit +/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 +/// let cipher = dual_regev.enc(&pk, &msg); +/// +/// // decrypt +/// let m = dual_regev.dec(&sk, &cipher); +/// +/// assert_eq!(msg, m); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct DualRegev { + pub(crate) n: Z, // security parameter + pub(crate) m: Z, // number of rows of matrix A + pub(crate) q: Modulus, // modulus + pub(crate) alpha: Q, // Gaussian parameter for sampleZ +} + +impl DualRegev { + /// Instantiates a [`DualRegev`] PK encryption instance with the + /// specified parameters. + /// + /// **WARNING:** The given parameters are not checked for security nor + /// correctness of the scheme. + /// If you want to check your parameters for provable security and correctness, + /// use [`DualRegev::check_correctness`] and [`DualRegev::check_security`]. + /// Or use [`DualRegev::new_from_n`] for generating secure and correct + /// public parameters for [`DualRegev`] according to your choice of `n`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// - `m`: specifies the number of columns of matrix `A` + /// - `q`: specifies the modulus + /// - `alpha`: specifies the Gaussian parameter used for independent + /// sampling from the discrete Gaussian distribution + /// + /// Returns [`DualRegev`] PK encryption instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegev; + /// + /// let dual_regev = DualRegev::new(3, 16, 13, 2); + /// ``` + /// + /// # Panics ... + /// - if the given modulus `q <= 1`. + pub fn new( + n: impl Into, + m: impl Into, + q: impl Into, + alpha: impl Into, + ) -> Self { + let n: Z = n.into(); + let m: Z = m.into(); + let q: Modulus = q.into(); + let alpha: Q = alpha.into(); + + Self { n, m, q, alpha } + } + + /// Generates a new [`DualRegev`] instance, i.e. a new set of suitable + /// (provably secure and correct) public parameters, + /// given the security parameter `n` for `n >= 10`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a correct and secure [`DualRegev`] PK encryption instance or + /// a [`MathError`] if the given `n < 10`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegev; + /// + /// let dual_regev = DualRegev::new_from_n(15); + /// ``` + /// + /// Panics... + /// - if `n < 10`. + /// - if `n` does not fit into an [`i64`]. + pub fn new_from_n(n: impl Into) -> Self { + let n = n.into(); + if n < 10 { + panic!("Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise."); + } + + let mut m: Z; + let mut q: Modulus; + let mut alpha: Q; + (m, q, alpha) = Self::gen_new_public_parameters(&n); + let mut out = Self { + n: n.clone(), + m, + q, + alpha, + }; + while out.check_correctness().is_err() || out.check_security().is_err() { + (m, q, alpha) = Self::gen_new_public_parameters(&n); + out = Self { + n: n.clone(), + m, + q, + alpha, + }; + } + + out + } + + /// Generates new public parameters, which must not be secure or correct + /// depending on the random choice of `q`. At least every fifth execution + /// of this function should output a valid set of public parameters, + /// ensuring a secure and correct PK encryption scheme. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a set of public parameters `(m, q, alpha)` chosen according to + /// the provided `n`. + /// + /// # Examples + /// ```compile_fail + /// use qfall_schemes::pk_encryption::DualRegev; + /// use qfall_math::integer::Z; + /// let n = Z::from(2); + /// + /// let (m, q, alpha) = DualRegev::gen_new_public_parameters(&n); + /// ``` + /// + /// Panics... + /// - if `n` does not fit into an [`i64`]. + fn gen_new_public_parameters(n: &Z) -> (Z, Modulus, Q) { + let n_i64 = i64::try_from(n).unwrap(); + // these powers are chosen according to experience s.t. at least every + // fifth generation of public parameters outputs a valid pair + let power = match n_i64 { + 2..=4 => 5, + 5 => 4, + _ => 3, + }; + + // generate prime q in [n^power / 2, n^power] + let upper_bound: Z = n.pow(power).unwrap(); + let lower_bound = upper_bound.div_ceil(2); + // prime used due to guide from GPV08 after Proposition 8.1 + // on how to choose appropriate parameters, but prime is not + // necessarily needed for this scheme to be correct or secure + let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); + + // choose m = (n+1) log q + let m = (n + Z::ONE) * q.log(2).unwrap().ceil(); + + // α = 1/(2 * sqrt(n) * log^2 n) + let alpha = 1 / (2 * n.sqrt() * n.log(2).unwrap().pow(2).unwrap()); + + let q = Modulus::from(q); + + (m, q, alpha) + } + + /// Checks the public parameters for + /// correctness according to Lemma 5.1 of [\[3\]](). + /// + /// The required properties are: + /// - α = o (1 / ( sqrt(n) * log n ) ) + /// - concentration bound with r=5: r * sqrt(m) * α > q/4 + /// + /// **WARNING:** Some requirements are missing to ensure overwhelming correctness of the scheme. + /// + /// Returns an empty result if the public parameters guarantee correctness + /// with overwhelming probability or a [`MathError`] if the instance would + /// not be correct. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegev; + /// let dual_regev = DualRegev::default(); + /// + /// let is_valid = dual_regev.check_correctness().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// correct Dual Regev public key encryption instance. + pub fn check_correctness(&self) -> Result<(), MathError> { + let q = Z::from(&self.q); + + if self.n <= Z::ONE { + return Err(MathError::InvalidIntegerInput(String::from( + "n must be chosen bigger than 1.", + ))); + } + + // Correctness requirements + // α = o (1 / ( sqrt(n) * log n ) ) + if self.alpha > 1 / (self.n.sqrt() * self.n.log(2).unwrap()) { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log n), but α < 1 / (sqrt(n) * log n) is required." + ))); + } + // concentration bound with r=5 -> r * sqrt(m) * α > q/4 + if 20 * self.m.sqrt() * &self.alpha > q { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as 5 * sqrt(m) * α > q/4, but 5 * sqrt(m) * α <= q/4 is required." + ))); + } + + Ok(()) + } + + /// Checks the public parameters for security according to Theorem 1.1 + /// and Lemma 5.4 of [\[3\]](). + /// + /// The required properties are: + /// - q * α >= 2 sqrt(n) + /// - m > (n + 1) log q + /// + /// Returns an empty result if the public parameters guarantees security w.r.t. `n` + /// or a [`MathError`] if the instance would not be secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegev; + /// let dual_regev = DualRegev::default(); + /// + /// let is_valid = dual_regev.check_security().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// secure Dual Regev public key encryption instance. + pub fn check_security(&self) -> Result<(), MathError> { + let q = Z::from(&self.q); + + // Security requirements + // q * α >= 2 sqrt(n) + if &q * &self.alpha < 2 * self.n.sqrt() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.", + ))); + } + // m > (n + 1) log q + if self.m <= ((&self.n + Z::ONE) * q.log(2).unwrap()).ceil() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as m <= (n + 1) log q, but m > (n + 1) log q is required.", + ))); + } + + Ok(()) + } + + /// This function instantiates a 128-bit secure [`DualRegev`] scheme. + /// + /// The public parameters used for this scheme were generated via `DualRegev::new_from_n(350)` + /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). + pub fn secure128() -> Self { + Self::new(230, 5313, 7764299, 0.0011) + } +} + +impl Default for DualRegev { + /// Initializes a [`DualRegev`] struct with parameters generated by `DualRegev::new_from_n(13)`. + /// This parameter choice is not secure as the dimension of the lattice is too small, + /// but it provides an efficient working example. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegev; + /// + /// let dual_regev = DualRegev::default(); + /// ``` + fn default() -> Self { + let n = Z::from(13); + let m = Z::from(154); + let q = Modulus::from(1427); + let alpha = Q::from(0.01); + + Self { n, m, q, alpha } + } +} + +impl PKEncryptionScheme for DualRegev { + type Cipher = MatZq; + type PublicKey = MatZq; + type SecretKey = MatZ; + + /// Generates a (pk, sk) pair for the Dual Regev public key encryption scheme + /// by following these steps: + /// - A <- Z_q^{n x m} + /// - x <- {0,1}^m + /// - u = A * x + /// - A = [A | u] + /// + /// Then, `pk = A` and `sk = x` of type [`MatZq`] are returned. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegev}; + /// let dual_regev = DualRegev::default(); + /// + /// let (pk, sk) = dual_regev.gen(); + /// ``` + fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + // A <- Z_q^{n x m} + let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); + // x <- Z_2^m + let vec_x = MatZ::sample_uniform(&self.m, 1, 0, 2).unwrap(); + + // u = A * x + let vec_u = &mat_a * &vec_x; + + // A = [A | u] + let mat_a = mat_a.concat_horizontal(&vec_u).unwrap(); + + // pk = A, sk = x + (mat_a, vec_x) + } + + /// Generates an encryption of `message mod 2` for the provided public key by following these steps: + /// - s <- Z_q^n + /// - e <- χ^(m+1) + /// - c^t = s^t * A + e^t + [0^{1xn} | msg * ⌊q/2⌋] + /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. + /// + /// Then, the ciphertext `c` is returned as a vector of type [`MatZq`]. + /// + /// Parameters: + /// - `pk`: specifies the public key `pk = A` + /// - `message`: specifies the message that should be encrypted + /// + /// Returns a cipher `c` of type [`MatZq`]. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegev}; + /// let dual_regev = DualRegev::default(); + /// let (pk, sk) = dual_regev.gen(); + /// + /// let cipher = dual_regev.enc(&pk, 1); + /// ``` + fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { + // generate message = message mod 2 + let message: Z = message.into() % 2; + + // s <- Z_q^n + let vec_s_t = MatZq::sample_uniform(1, &self.n, &self.q); + // e <- χ^(m+1) + let vec_e_t = MatZq::sample_discrete_gauss( + 1, + &(&self.m + 1), + &self.q, + &self.n, + 0, + &self.alpha * Z::from(&self.q), + ) + .unwrap(); + + // c^t = s^t * A + e^t + [0^{1xn} | msg * ⌊q/2⌋] + let mut c = (vec_s_t * pk + vec_e_t).transpose(); + + // hide message in last entry + // compute msg * ⌊q/2⌋ + let msg_q_half = message * Z::from(&self.q).div_floor(2); + // set last entry of c = last_entry + msg * ⌊q/2⌋ + let last_entry: Zq = c.get_entry(-1, 0).unwrap(); + c.set_entry(-1, 0, last_entry + msg_q_half).unwrap(); + + c + } + + /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - x = c^t * [-sk^t | 1]^t + /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. + /// + /// Parameters: + /// - `sk`: specifies the secret key `sk = x` + /// - `cipher`: specifies the cipher containing `cipher = c` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegev}; + /// use qfall_math::integer::Z; + /// let dual_regev = DualRegev::default(); + /// let (pk, sk) = dual_regev.gen(); + /// let cipher = dual_regev.enc(&pk, 1); + /// + /// let m = dual_regev.dec(&sk, &cipher); + /// + /// assert_eq!(Z::ONE, m); + /// ``` + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + let tmp = (Z::MINUS_ONE * sk) + .concat_vertical(&MatZ::identity(1, 1)) + .unwrap(); + let result: Zq = (cipher.transpose() * tmp).get_entry(0, 0).unwrap(); + let result: Z = result.get_representative_least_absolute_residue().abs(); + + let q_half = Z::from(&self.q).div_floor(2); + + if result.distance(Z::ZERO) > result.distance(q_half) { + Z::ONE + } else { + Z::ZERO + } + } +} + +// adds generic multi-bit encryption to this scheme +impl GenericMultiBitEncryption for DualRegev {} + +#[cfg(test)] +mod test_pp_generation { + use super::DualRegev; + use super::Z; + + /// Checks whether `new` is available for types implementing [`Into`]. + #[test] + fn new_availability() { + let _ = DualRegev::new(2u8, 2u16, 2u32, 2u64); + let _ = DualRegev::new(2u16, 2u64, 2i32, 2i64); + let _ = DualRegev::new(2i16, 2i64, 2u32, 2u8); + let _ = DualRegev::new(Z::from(2), Z::from(2), 2u8, 2i8); + } + + /// Checks whether `new_from_n` works properly for different choices of n. + #[test] + fn suitable_security_params() { + let n_choices = [ + 10, 11, 12, 13, 14, 25, 50, 100, 250, 500, 1000, 2500, 5000, 5001, 10000, + ]; + + for n in n_choices { + let _ = DualRegev::new_from_n(n); + } + } + + /// Checks whether the [`Default`] parameter choice is suitable. + #[test] + fn default_suitable() { + let dr = DualRegev::default(); + + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } + + /// Checks whether the generated public parameters from `new_from_n` are + /// valid choices according to security and correctness of the scheme. + #[test] + fn choice_valid() { + let n_choices = [10, 14, 25, 50, 125, 300, 600, 1200, 4000, 6000]; + + for n in n_choices { + let dr = DualRegev::new_from_n(n); + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } + } + + /// Ensures that `new_from_n` is available for types implementing [`Into`]. + #[test] + #[allow(clippy::needless_borrows_for_generic_args)] + fn availability() { + let _ = DualRegev::new_from_n(10u8); + let _ = DualRegev::new_from_n(10u16); + let _ = DualRegev::new_from_n(10u32); + let _ = DualRegev::new_from_n(10u64); + let _ = DualRegev::new_from_n(10i8); + let _ = DualRegev::new_from_n(10i16); + let _ = DualRegev::new_from_n(10i32); + let _ = DualRegev::new_from_n(10i64); + let _ = DualRegev::new_from_n(Z::from(10)); + let _ = DualRegev::new_from_n(&Z::from(10)); + } + + /// Checks whether `new_from_n` returns an error for invalid input n. + #[test] + #[should_panic] + fn invalid_n() { + DualRegev::new_from_n(9); + } + + /// Checks whether `secure128` outputs a new instance with correct and secure + /// parameters. + #[test] + fn secure128_validity() { + let dr = DualRegev::secure128(); + + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } +} + +#[cfg(test)] +mod test_dual_regev { + use super::DualRegev; + use crate::pk_encryption::PKEncryptionScheme; + use qfall_math::integer::Z; + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and small n. + #[test] + fn cycle_zero_small_n() { + let msg = Z::ZERO; + let dr = DualRegev::default(); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and small n. + #[test] + fn cycle_one_small_n() { + let msg = Z::ONE; + let dr = DualRegev::default(); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and larger n. + #[test] + fn cycle_zero_large_n() { + let msg = Z::ZERO; + let dr = DualRegev::new_from_n(50); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and larger n. + #[test] + fn cycle_one_large_n() { + let msg = Z::ONE; + let dr = DualRegev::new_from_n(50); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks that modulus 2 is applied correctly. + #[test] + fn modulus_application() { + let messages = [2, 3, i64::MAX, i64::MIN]; + let dr = DualRegev::default(); + let (pk, sk) = dr.gen(); + + for msg in messages { + let msg_mod = Z::from(msg.rem_euclid(2)); + + let cipher = dr.enc(&pk, msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg_mod, m); + } + } +} + +#[cfg(test)] +mod test_multi_bits { + use super::{DualRegev, GenericMultiBitEncryption, PKEncryptionScheme}; + use qfall_math::integer::Z; + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large positive values. + #[test] + fn positive() { + let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; + + for value in values { + let msg = Z::from(value); + let scheme = DualRegev::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for zero. + #[test] + fn zero() { + let msg = Z::ZERO; + let scheme = DualRegev::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large negative values, which are not encrypted itself, + /// but their absolute value. + #[test] + fn negative() { + let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; + + for value in values { + let msg = Z::from(value); + let scheme = DualRegev::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg.abs(), m); + } + } +} diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs new file mode 100644 index 0000000..0ce5f67 --- /dev/null +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -0,0 +1,697 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains an implementation of the IND-CPA secure +//! public key Dual Regev encryption scheme with an instantiation of the regularity lemma +//! via a discrete Gaussian distribution. + +use super::{GenericMultiBitEncryption, PKEncryptionScheme}; +use qfall_math::{ + error::MathError, + integer::Z, + integer_mod_q::{MatZq, Modulus, Zq}, + rational::Q, + traits::{Distance, Pow}, +}; +use serde::{Deserialize, Serialize}; + +/// This struct manages and stores the public parameters of a [`DualRegevWithDiscreteGaussianRegularity`] +/// public key encryption instance. +/// +/// Attributes: +/// - `n`: specifies the security parameter, which is not equal to the bit-security level +/// - `m`: defines the dimension of the underlying lattice +/// - `q`: specifies the modulus over which the encryption is computed +/// - `r`: specifies the Gaussian parameter used for SampleD, +/// i.e. used for encryption +/// - `alpha`: specifies the Gaussian parameter used for independent +/// sampling from the discrete Gaussian distribution +/// +/// # Examples +/// ``` +/// use qfall_schemes::pk_encryption::{DualRegevWithDiscreteGaussianRegularity, PKEncryptionScheme}; +/// use qfall_math::integer::Z; +/// // setup public parameters and key pair +/// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); +/// let (pk, sk) = dual_regev.gen(); +/// +/// // encrypt a bit +/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 +/// let cipher = dual_regev.enc(&pk, &msg); +/// +/// // decrypt +/// let m = dual_regev.dec(&sk, &cipher); +/// +/// assert_eq!(msg, m); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct DualRegevWithDiscreteGaussianRegularity { + n: Z, // security parameter + m: Z, // number of rows of matrix A + q: Modulus, // modulus + r: Q, // Gaussian parameter for sampleD + alpha: Q, // Gaussian parameter for sampleZ +} + +impl DualRegevWithDiscreteGaussianRegularity { + /// Instantiates a [`DualRegevWithDiscreteGaussianRegularity`] PK encryption instance with the + /// specified parameters. + /// + /// **WARNING:** The given parameters are not checked for security nor + /// correctness of the scheme. + /// If you want to check your parameters for provable security and correctness, + /// use [`DualRegevWithDiscreteGaussianRegularity::check_correctness`] and [`DualRegevWithDiscreteGaussianRegularity::check_security`]. + /// Or use [`DualRegevWithDiscreteGaussianRegularity::new_from_n`] for generating secure and correct + /// public parameters for [`DualRegevWithDiscreteGaussianRegularity`] according to your choice of `n`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// - `m`: specifies the number of columns of matrix `A` + /// - `q`: specifies the modulus + /// - `r`: specifies the Gaussian parameter used for SampleD, + /// i.e. used for encryption + /// - `alpha`: specifies the Gaussian parameter used for independent + /// sampling from the discrete Gaussian distribution + /// + /// Returns a [`DualRegevWithDiscreteGaussianRegularity`] PK encryption instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegevWithDiscreteGaussianRegularity; + /// + /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::new(2, 16, 443, 4, 0.15625); + /// ``` + /// + /// # Panics ... + /// - if the given modulus `q <= 1`. + pub fn new( + n: impl Into, + m: impl Into, + q: impl Into, + r: impl Into, + alpha: impl Into, + ) -> Self { + let n: Z = n.into(); + let m: Z = m.into(); + let q: Modulus = q.into(); + let r: Q = r.into(); + let alpha: Q = alpha.into(); + + Self { n, m, q, r, alpha } + } + + /// Generates a new [`DualRegevWithDiscreteGaussianRegularity`] instance, i.e. a new set of suitable + /// (provably secure and correct) public parameters, + /// given the security parameter `n`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a correct and secure [`DualRegevWithDiscreteGaussianRegularity`] PK encryption instance or + /// a [`MathError`] if the given `n <= 1`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegevWithDiscreteGaussianRegularity; + /// + /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::new_from_n(2); + /// ``` + /// + /// # Panics ... + /// - if `n <= 1`. + pub fn new_from_n(n: impl Into) -> Self { + let n = n.into(); + if n <= Z::ONE { + panic!("n must be chosen bigger than 1."); + } + + let mut m: Z; + let mut q: Modulus; + let mut r: Q; + let mut alpha: Q; + (m, q, r, alpha) = Self::gen_new_public_parameters(&n); + let mut out = Self { + n: n.clone(), + m, + q, + r, + alpha, + }; + while out.check_correctness().is_err() || out.check_security().is_err() { + (m, q, r, alpha) = Self::gen_new_public_parameters(&n); + out = Self { + n: n.clone(), + m, + q, + r, + alpha, + }; + } + + out + } + + /// Generates new public parameters, which must not be secure or correct + /// depending on the random choice of `q`. At least every fifth execution + /// of this function should output a valid set of public parameters, + /// ensuring a secure and correct PK encryption scheme. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a set of public parameters `(m, q, r, alpha)` chosen according to + /// the provided `n`. + /// + /// # Examples + /// ```compile_fail + /// use qfall_schemes::pk_encryption::DualRegevWithDiscreteGaussianRegularity; + /// use qfall_math::integer::Z; + /// let n = Z::from(2); + /// + /// let (m, q, r, alpha) = DualRegevWithDiscreteGaussianRegularity::gen_new_public_parameters(&n); + /// ``` + fn gen_new_public_parameters(n: &Z) -> (Z, Modulus, Q, Q) { + let n_i64 = i64::try_from(n).unwrap(); + // these powers are chosen according to experience s.t. at least every + // fifth generation of public parameters outputs a valid pair + let power = match n_i64 { + 2 => 9, + 3 => 8, + 4..=5 => 7, + 6..=8 => 6, + 9..=12 => 5, + 13..=30 => 4, + _ => 3, + }; + + // generate prime q in [n^power / 2, n^power] + let upper_bound: Z = n.pow(power).unwrap(); + let lower_bound = upper_bound.div_ceil(2); + // prime used due to guide from GPV08 after Proposition 8.1 + // on how to choose appropriate parameters, but prime is not + // necessarily needed for this scheme to be correct or secure + let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); + + // choose m = 2 (n+1) lg q + let m = (Z::from(2) * (n + Z::ONE) * q.log(10).unwrap()).ceil(); + + // choose r = log m + let r = m.log(2).unwrap(); + + // alpha = 1/(sqrt(m) * log^2 m) + let alpha = 1 / (m.sqrt() * m.log(2).unwrap().pow(2).unwrap()); + + let q = Modulus::from(&q); + + (m, q, r, alpha) + } + + /// Checks the public parameters for correctness according to + /// Theorem 7.1 and Lemma 8.2 of [\[2\]](). + /// + /// The required properties are: + /// - n >= 1 + /// - q >= 5 * r * m + /// - α <= 1/(r * sqrt(m) * ω(sqrt(log n)) + /// + /// Returns an empty result if the public parameters guarantee correctness + /// with overwhelming probability or a [`MathError`] if the instance would + /// not be correct. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegevWithDiscreteGaussianRegularity; + /// let dr = DualRegevWithDiscreteGaussianRegularity::default(); + /// + /// let is_valid = dr.check_correctness().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// correct DualRegevWithDiscreteGaussianRegularity public key encryption instance. + pub fn check_correctness(&self) -> Result<(), MathError> { + let q: Z = Z::from(&self.q); + + if self.n <= Z::ONE { + return Err(MathError::InvalidIntegerInput(String::from( + "n must be chosen bigger than 1.", + ))); + } + + // Correctness requirements + // q >= 5 * r * (m+1) + if q < 5 * &self.r * (&self.m + Z::ONE) { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as q < 5rm, but q >= 5rm is required.", + ))); + } + // α <= 1/(r * sqrt(m+1) * ω(sqrt(log n)) + if self.alpha > 1 / (&self.r * (&self.m + Z::ONE).sqrt() * self.n.log(2).unwrap().sqrt()) { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as α > 1/(r*sqrt(m)*ω(sqrt(log n)), but α <= 1/(r*sqrt(m)*ω(sqrt(log n)) is required.", + ))); + } + + Ok(()) + } + + /// Checks the public parameters for security according to + /// Theorem 7.1 and Lemma 8.4 of [\[2\]](). + /// + /// The required properties are: + /// - q * α >= n + /// - m >= 2(n + 1) lg (q) + /// - r >= ω( sqrt( log m ) ) + /// + /// Returns an empty result if the public parameters guarantees security w.r.t. `n` + /// or a [`MathError`] if the instance would not be secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegevWithDiscreteGaussianRegularity; + /// let dr = DualRegevWithDiscreteGaussianRegularity::default(); + /// + /// let is_valid = dr.check_security().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// secure DualRegevWithDiscreteGaussianRegularity public key encryption instance. + pub fn check_security(&self) -> Result<(), MathError> { + let q: Z = Z::from(&self.q); + + // Security requirements + // q * α >= n + if &q * &self.alpha < self.n { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as q * α < n, but q * α >= n is required.", + ))); + } + // m >= 2n lg (q) + if self.m < 2 * &self.n * q.log(10).unwrap() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as m < 2(n + 1) lg (q), but m >= 2(n + 1) lg (q) is required.", + ))); + } + // r >= ω( sqrt( log m ) ) + if self.r < self.m.log(2).unwrap().sqrt() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as r < sqrt( log m ) and r >= ω(sqrt(log m)) is required." + ))); + } + + Ok(()) + } + + /// This function instantiates a 128-bit secure [`DualRegevWithDiscreteGaussianRegularity`] scheme. + /// + /// The public parameters used for this scheme were generated + /// via `DualRegevWithDiscreteGaussianRegularity::new_from_n(350)` + /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). + pub fn secure128() -> Self { + Self::new(350, 5248, 29892991, 12.357, 0.00009) + } +} + +impl Default for DualRegevWithDiscreteGaussianRegularity { + /// Initializes a [`DualRegevWithDiscreteGaussianRegularity`] struct with parameters + /// generated by `DualRegevWithDiscreteGaussianRegularity::new_from_n(2)`. + /// This parameter choice is not secure as the dimension of the lattice is too small, + /// but it provides an efficient working example. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::DualRegevWithDiscreteGaussianRegularity; + /// + /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); + /// ``` + fn default() -> Self { + let n = Z::from(2); + let m = Z::from(16); + let q = Modulus::from(443); + let r = Q::from(4); + let alpha = Q::from((1, 64)); + + Self { n, m, q, r, alpha } + } +} + +impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { + type Cipher = (MatZq, Zq); + type PublicKey = (MatZq, MatZq); + type SecretKey = MatZq; + + /// Generates a (pk, sk) pair for the Dual Regev public key encryption scheme + /// by following these steps: + /// - e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r + /// - A <- Z_q^{n x m} + /// - p = A * e + /// + /// Then, `pk = (A, u)` and `sk = e` are returned. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; + /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); + /// + /// let (pk, sk) = dual_regev.gen(); + /// ``` + fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + // e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r + let vec_e = MatZq::sample_d_common(&self.m, &self.q, &self.n, &self.r).unwrap(); + // A <- Z_q^{n x m} + let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); + + // u = A * e + let vec_u = &mat_a * &vec_e; + + // pk = (A, u), sk = e + ((mat_a, vec_u), vec_e) + } + + /// Generates an encryption of `message mod 2` for the provided public key by following these steps: + /// - vec_x <- χ^m, x <- χ + /// - p = A^t * s + vec_x + /// - c = u^t * s + x + message * ⌊q/2⌋ + /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. + /// + /// Then, `cipher = (p, c)` is returned. + /// + /// Parameters: + /// - `pk`: specifies the public key, which contains two matrices `pk = (A, u)` + /// - `message`: specifies the message that should be encrypted + /// + /// Returns a cipher of the form `cipher = (p, c)` for [`MatZq`] `u` and [`Zq`] `c`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; + /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); + /// let (pk, sk) = dual_regev.gen(); + /// + /// let cipher = dual_regev.enc(&pk, 1); + /// ``` + fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { + // generate message = message mod 2 + let message: Z = message.into() % 2; + + // s <- Z_q^n + let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); + // vec_x <- χ^m + let vec_x = MatZq::sample_discrete_gauss( + &self.m, + 1, + &self.q, + &self.n, + 0, + &(&self.alpha * Z::from(&self.q)), + ) + .unwrap(); + + // x <- χ + let x = Z::sample_discrete_gauss(&self.n, 0, &(&self.alpha * Z::from(&self.q))).unwrap(); + + // p = u^t * s + vec_x + let vec_p = &pk.0.transpose() * &vec_s + vec_x; + // c = u^t * s + x + msg * ⌊q/2⌋ + let q_half = Z::from(&self.q).div_floor(2); + let c = pk.1.dot_product(&vec_s).unwrap() + x + message * q_half; + + (vec_p, c) + } + + /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - x = c - e^t * p + /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. + /// + /// Parameters: + /// - `sk`: specifies the secret key `sk = e` + /// - `cipher`: specifies the cipher containing `cipher = (p, c)` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; + /// use qfall_math::integer::Z; + /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); + /// let (pk, sk) = dual_regev.gen(); + /// let cipher = dual_regev.enc(&pk, 1); + /// + /// let m = dual_regev.dec(&sk, &cipher); + /// + /// assert_eq!(Z::ONE, m); + /// ``` + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + let result = &cipher.1 - sk.dot_product(&cipher.0).unwrap(); + let result: Z = result.get_representative_least_absolute_residue().abs(); + + let q_half = Z::from(&self.q).div_floor(2); + + if result.distance(Z::ZERO) > result.distance(q_half) { + Z::ONE + } else { + Z::ZERO + } + } +} + +// adds generic multi-bit encryption to this scheme +impl GenericMultiBitEncryption for DualRegevWithDiscreteGaussianRegularity {} + +#[cfg(test)] +mod test_pp_generation { + use super::DualRegevWithDiscreteGaussianRegularity; + use super::Z; + + /// Checks whether `new` is available for types implementing [`Into`]. + #[test] + fn new_availability() { + let _ = DualRegevWithDiscreteGaussianRegularity::new(2u8, 2u16, 2u32, 2u64, 2i8); + let _ = DualRegevWithDiscreteGaussianRegularity::new(2u16, 2u64, 2i32, 2i64, 2i16); + let _ = DualRegevWithDiscreteGaussianRegularity::new(2i16, 2i64, 2u32, 2u8, 2u16); + let _ = + DualRegevWithDiscreteGaussianRegularity::new(Z::from(2), Z::from(2), 2u8, 2i8, 2u32); + } + + /// Checks whether `new_from_n` works properly for different choices of n. + #[test] + fn suitable_security_params() { + let n_choices = [ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 5001, + 10000, + ]; + + for n in n_choices { + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(n); + } + } + + /// Checks whether the [`Default`] parameter choice is suitable. + #[test] + fn default_suitable() { + let dr = DualRegevWithDiscreteGaussianRegularity::default(); + + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } + + /// Checks whether the generated public parameters from `new_from_n` are + /// valid choices according to security and correctness of the scheme. + #[test] + fn choice_valid() { + let n_choices = [ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 5001, + 10000, + ]; + + for n in n_choices { + let dr = DualRegevWithDiscreteGaussianRegularity::new_from_n(n); + + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } + } + + /// Ensures that `new_from_n` is available for types implementing [`Into`]. + #[test] + #[allow(clippy::needless_borrows_for_generic_args)] + fn new_from_n_availability() { + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2u8); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2u16); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2u32); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2u64); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2i8); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2i16); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2i32); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2i64); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(Z::from(2)); + let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(&Z::from(2)); + } + + /// Checks whether `new_from_n` returns an error for invalid input n. + #[test] + #[should_panic] + fn invalid_n() { + DualRegevWithDiscreteGaussianRegularity::new_from_n(1); + } + + /// Checks whether `secure128` outputs a new instance with correct and secure + /// parameters. + #[test] + fn secure128_validity() { + let dr = DualRegevWithDiscreteGaussianRegularity::secure128(); + + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } +} + +#[cfg(test)] +mod test_dual_regev { + use super::DualRegevWithDiscreteGaussianRegularity; + use crate::pk_encryption::PKEncryptionScheme; + use qfall_math::integer::Z; + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and small n. + #[test] + fn cycle_zero_small_n() { + let msg = Z::ZERO; + let dr = DualRegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and small n. + #[test] + fn cycle_one_small_n() { + let msg = Z::ONE; + let dr = DualRegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and larger n. + #[test] + fn cycle_zero_large_n() { + let msg = Z::ZERO; + let dr = DualRegevWithDiscreteGaussianRegularity::new_from_n(30); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and larger n. + #[test] + fn cycle_one_large_n() { + let msg = Z::ONE; + let dr = DualRegevWithDiscreteGaussianRegularity::new_from_n(30); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks that modulus 2 is applied correctly. + #[test] + fn modulus_application() { + let messages = [2, 3, i64::MAX, i64::MIN]; + let dr = DualRegevWithDiscreteGaussianRegularity::default(); + let (pk, sk) = dr.gen(); + + for msg in messages { + let msg_mod = Z::from(msg.rem_euclid(2)); + + let cipher = dr.enc(&pk, msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg_mod, m); + } + } +} + +#[cfg(test)] +mod test_multi_bits { + use super::{ + DualRegevWithDiscreteGaussianRegularity, GenericMultiBitEncryption, PKEncryptionScheme, + }; + use qfall_math::integer::Z; + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large positive values. + #[test] + fn positive() { + let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; + + for value in values { + let msg = Z::from(value); + let scheme = DualRegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for zero. + #[test] + fn zero() { + let msg = Z::ZERO; + let scheme = DualRegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large negative values, which are not encrypted itself, + /// but their absolute value. + #[test] + fn negative() { + let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; + + for value in values { + let msg = Z::from(value); + let scheme = DualRegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg.abs(), m); + } + } +} diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs new file mode 100644 index 0000000..586b908 --- /dev/null +++ b/src/pk_encryption/k_pke.rs @@ -0,0 +1,274 @@ +// Copyright © 2025 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains a naive implementation of the K-PKE scheme +//! used as foundation for ML-KEM. +//! +//! **WARNING:** This implementation is a toy implementation of the basics below +//! ML-KEM and mostly supposed to showcase the prototyping capabilities of the `qfall`-library. + +use crate::pk_encryption::PKEncryptionScheme; +use qfall_crypto::utils::{ + common_encodings::{ + decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, + }, + common_moduli::new_anticyclic, +}; +use qfall_math::{ + integer::Z, + integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq}, +}; +use serde::{Deserialize, Serialize}; + +/// This is a naive toy-implementation of the [`PKEncryptionScheme`] used +/// as a basis for ML-KEM. +/// +/// This implementation is not supposed to be an implementation of the FIPS 203 standard in [\[6\]](), but +/// is supposed to showcase the prototyping capabilities of `qfall` and does not cover compression algorithms +/// as specified in the FIPS 203 document or might deviate for the choice of matrix multiplication algorithms. +/// Especially, NTT-representation, sampling and multiplication are not part of this prototype. +/// +/// Attributes: +/// - `q`: defines the modulus polynomial `(X^n + 1) mod p` +/// - `k`: defines the width and height of matrix `A` +/// - `eta_1`: defines that vectors `s`, `e`, and `y` are sampled according to Bin(eta_1, 1/2) centered around 0 +/// - `eta_2`: defines that vector `e_1` and `e_2` are sampled according to Bin(eta_2, 1/2) centered around 0 +/// +/// # Examples +/// ``` +/// use qfall_schemes::pk_encryption::{KPKE, PKEncryptionScheme}; +/// +/// // setup public parameters +/// let k_pke = KPKE::ml_kem_512(); +/// +/// // generate (pk, sk) pair +/// let (pk, sk) = k_pke.gen(); +/// +/// // encrypt a message +/// let msg = 250; +/// let cipher = k_pke.enc(&pk, &msg); +/// +/// // decrypt the ciphertext +/// let m = k_pke.dec(&sk, &cipher); +/// +/// assert_eq!(msg, m); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct KPKE { + q: ModulusPolynomialRingZq, // modulus (X^n + 1) mod p + k: i64, // defines both dimensions of matrix A + eta_1: i64, // defines the binomial distribution of the secret and error drawn in `gen` + eta_2: i64, // defines the binomial distribution of the error drawn in `enc` +} + +impl KPKE { + /// Returns a [`KPKE`] instance with public parameters according to the ML-KEM-512 specification. + pub fn ml_kem_512() -> Self { + let q = new_anticyclic(256, 3329).unwrap(); + Self { + q, + k: 2, + eta_1: 3, + eta_2: 2, + } + } + + /// Returns a [`KPKE`] instance with public parameters according to the ML-KEM-768 specification. + pub fn ml_kem_768() -> Self { + let q = new_anticyclic(256, 3329).unwrap(); + Self { + q, + k: 3, + eta_1: 2, + eta_2: 2, + } + } + + /// Returns a [`KPKE`] instance with public parameters according to the ML-KEM-1024 specification. + pub fn ml_kem_1024() -> Self { + let q = new_anticyclic(256, 3329).unwrap(); + Self { + q, + k: 4, + eta_1: 2, + eta_2: 2, + } + } +} + +impl PKEncryptionScheme for KPKE { + type PublicKey = (MatPolynomialRingZq, MatPolynomialRingZq); + type SecretKey = MatPolynomialRingZq; + type Cipher = (MatPolynomialRingZq, PolynomialRingZq); + + /// Generates a `(pk, sk)` pair by following these steps: + /// - A <- R_q^{k x k} + /// - s <- Bin(eta_1, 0.5)^k centered around 0 + /// - e <- Bin(eta_1, 0.5)^k centered around 0 + /// - t = A * s + e + /// + /// Then, `pk = (A^T, t)` and `sk = s` are returned. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, KPKE}; + /// let k_pke = KPKE::ml_kem_512(); + /// + /// let (pk, sk) = k_pke.gen(); + /// ``` + fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + // 5 𝐀[𝑖,𝑗] ← SampleNTT(𝜌‖𝑗‖𝑖) + // Reminder: NTT-representation, sampling and multiplication are not part of this prototype + let mat_a = MatPolynomialRingZq::sample_uniform(self.k, self.k, &self.q); + // 9 𝐬[𝑖] ← SamplePolyCBD_𝜂_1(PRF_𝜂_1 (𝜎, 𝑁)) + let vec_s = MatPolynomialRingZq::sample_binomial_with_offset( + self.k, + 1, + &self.q, + -self.eta_1, + 2 * self.eta_1, + 0.5, + ) + .unwrap(); + // 13 𝐞[𝑖] ← SamplePolyCBD_𝜂_1(PRF_𝜂_1 (𝜎, 𝑁)) + let vec_e = MatPolynomialRingZq::sample_binomial_with_offset( + self.k, + 1, + &self.q, + -self.eta_1, + 2 * self.eta_1, + 0.5, + ) + .unwrap(); + + // 18 𝐭 ← 𝐀 ∘ 𝐬 + 𝐞 + let vec_t = &mat_a * &vec_s + vec_e; + + let pk = (mat_a.transpose(), vec_t); + let sk = vec_s; + (pk, sk) + } + + /// Encrypts a `message` with the provided public key by following these steps: + /// - y <- Bin(eta_1, 0.5)^k centered around 0 + /// - e_1 <- Bin(eta_2, 0.5)^k centered around 0 + /// - e_2 <- Bin(eta_2, 0.5) centered around 0 + /// - u = A^T * y + e_1 + /// - v = t^T * y + e_2 + 𝜇, where 𝜇 is the {q/2, 0} encoding of the bits of `message` + /// + /// Then, ciphertext `(u, v)` is returned. + /// + /// Parameters: + /// - `pk`: specifies the public key `pk = (A, t)` + /// - `message`: specifies the message that should be encrypted, which should not extend 256 bits (and be positive) + /// + /// Returns a ciphertext `(u, v)` of type [`MatPolynomialRingZq`] and [`PolynomialRingZq`]. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, KPKE}; + /// let k_pke = KPKE::ml_kem_512(); + /// let (pk, sk) = k_pke.gen(); + /// + /// let c = k_pke.enc(&pk, 1); + /// ``` + fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { + // 10 𝐲[𝑖] ← SamplePolyCBD_𝜂_1(PRF_𝜂_1 (𝑟, 𝑁)) + let vec_y = MatPolynomialRingZq::sample_binomial_with_offset( + self.k, + 1, + &self.q, + -self.eta_1, + 2 * self.eta_1, + 0.5, + ) + .unwrap(); + // 𝐞_𝟏[𝑖] ← SamplePolyCBD_𝜂_2(PRF_𝜂_2 (𝑟, 𝑁)) + let vec_e_1 = MatPolynomialRingZq::sample_binomial_with_offset( + self.k, + 1, + &self.q, + -self.eta_2, + 2 * self.eta_2, + 0.5, + ) + .unwrap(); + // 𝑒_2 ← SamplePolyCBD_𝜂_2(PRF_𝜂_2 (𝑟, 𝑁)) + let e_2 = PolynomialRingZq::sample_binomial_with_offset( + &self.q, + -self.eta_2, + 2 * self.eta_2, + 0.5, + ) + .unwrap(); + + // 19 𝐮 ← NTT^−1(𝐀^⊺ ∘ 𝐲) + 𝐞_𝟏 + let vec_u = &pk.0 * &vec_y + vec_e_1; + + // 20 𝜇 ← Decompress_1(ByteDecode_1(𝑚)) + let mu = encode_z_bitwise_in_polynomialringzq(&self.q, &message.into()); + + // 21 𝑣 ← NTT^−1(𝐭^⊺ ∘ 𝐲) + 𝑒_2 + 𝜇 + let v = pk.1.dot_product(&vec_y).unwrap() + e_2 + mu; + + (vec_u, v) + } + + /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - w = v - s^T * u + /// - returns the decoding of `w` with 1 and 0 set in the returned [`Z`] instance + /// if the corresponding coefficient was closer to q/2 or 0 respectively + /// + /// Parameters: + /// - `sk`: specifies the secret key `sk = s` + /// - `cipher`: specifies the ciphertext containing `cipher = (u, v)` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, KPKE}; + /// let k_pke = KPKE::ml_kem_512(); + /// let (pk, sk) = k_pke.gen(); + /// let c = k_pke.enc(&pk, 1); + /// + /// let m = k_pke.dec(&sk, &c); + /// + /// assert_eq!(1, m); + /// ``` + fn dec(&self, sk: &Self::SecretKey, (u, v): &Self::Cipher) -> Z { + // 6 𝑤 ← 𝑣 − NTT^−1(𝐬^⊺ ∘ NTT(𝐮)) + let w = v - sk.dot_product(u).unwrap(); + + // 7 𝑚 ← ByteEncode_1(Compress_1(𝑤)) + decode_z_bitwise_from_polynomialringzq(self.q.get_q(), &w) + } +} + +#[cfg(test)] +mod test_kpke { + use crate::pk_encryption::{k_pke::KPKE, PKEncryptionScheme}; + + /// Ensures that [`KPKE`] works for all ML-KEM specifications by + /// performing a round trip of several messages. + #[test] + fn correctness() { + let k_pkes = [KPKE::ml_kem_512(), KPKE::ml_kem_768(), KPKE::ml_kem_1024()]; + for k_pke in k_pkes { + let messages = [0, 1, 13, 255, 2047, 4294967295_u32]; + + for message in messages { + let (pk, sk) = k_pke.gen(); + let c = k_pke.enc(&pk, message); + let m = k_pke.dec(&sk, &c); + + assert_eq!(message, m); + } + } + } +} diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs new file mode 100644 index 0000000..178dfeb --- /dev/null +++ b/src/pk_encryption/lpr.rs @@ -0,0 +1,693 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains an implementation of the IND-CPA secure +//! public key LPR encryption scheme. + +use super::{GenericMultiBitEncryption, PKEncryptionScheme}; +use qfall_math::{ + error::MathError, + integer::Z, + integer_mod_q::{MatZq, Modulus, Zq}, + rational::Q, + traits::{Concatenate, Distance, MatrixGetEntry, MatrixSetEntry, Pow}, +}; +use serde::{Deserialize, Serialize}; + +/// This struct manages and stores the public parameters of a [`LPR`] +/// public key encryption instance. +/// +/// Attributes: +/// - `n`: specifies the security parameter, which is not equal to the bit-security level +/// - `q`: specifies the modulus over which the encryption is computed +/// - `alpha`: specifies the Gaussian parameter used for independent +/// sampling from the discrete Gaussian distribution +/// +/// # Examples +/// ``` +/// use qfall_schemes::pk_encryption::{LPR, PKEncryptionScheme}; +/// use qfall_math::integer::Z; +/// // setup public parameters and key pair +/// let lpr = LPR::default(); +/// let (pk, sk) = lpr.gen(); +/// +/// // encrypt a bit +/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 +/// let cipher = lpr.enc(&pk, &msg); +/// +/// // decrypt +/// let m = lpr.dec(&sk, &cipher); +/// +/// assert_eq!(msg, m); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct LPR { + n: Z, // security parameter + q: Modulus, // modulus + alpha: Q, // Gaussian parameter for sampleZ +} + +impl LPR { + /// Instantiates a [`LPR`] PK encryption instance with the + /// specified parameters. + /// + /// **WARNING:** The given parameters are not checked for security nor + /// correctness of the scheme. + /// If you want to check your parameters for provable security and correctness, + /// use [`LPR::check_correctness`] and [`LPR::check_security`]. + /// Or use [`LPR::new_from_n`] for generating secure and correct + /// public parameters for [`LPR`] according to your choice of `n`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// - `q`: specifies the modulus + /// - `alpha`: specifies the Gaussian parameter used for independent + /// sampling from the discrete Gaussian distribution + /// + /// Returns a correct and secure [`LPR`] PK encryption instance or + /// a [`MathError`] if the instance would not be correct or secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::LPR; + /// + /// let lpr = LPR::new(3, 13, 2); + /// ``` + /// + /// # Panics ... + /// - if the given modulus `q <= 1`. + pub fn new(n: impl Into, q: impl Into, alpha: impl Into) -> Self { + let n: Z = n.into(); + let q: Modulus = q.into(); + let alpha: Q = alpha.into(); + + Self { n, q, alpha } + } + + /// Generates a new [`LPR`] instance, i.e. a new set of suitable + /// (provably secure and correct) public parameters, + /// given the security parameter `n` for `n >= 10`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a correct and secure [`LPR`] PK encryption instance or + /// a [`MathError`] if the given `n < 10`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::LPR; + /// + /// let lpr = LPR::new_from_n(15); + /// ``` + /// + /// Panics... + /// - if `n < 10` + /// - if `n` does not fit into an [`i64`]. + pub fn new_from_n(n: impl Into) -> Self { + let n = n.into(); + assert!( + n >= 10, + "Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise." + ); + + let mut q: Modulus; + let mut alpha: Q; + (q, alpha) = Self::gen_new_public_parameters(&n); + let mut out = Self { + n: n.clone(), + q, + alpha, + }; + while out.check_correctness().is_err() || out.check_security().is_err() { + (q, alpha) = Self::gen_new_public_parameters(&n); + out = Self { + n: n.clone(), + q, + alpha, + }; + } + + out + } + + /// Generates new public parameters, which must not be secure or correct + /// depending on the random choice of `q`. At least every fifth execution + /// of this function should output a valid set of public parameters, + /// ensuring a secure and correct PK encryption scheme. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a set of public parameters `(q, alpha)` chosen according to + /// the provided `n`. + /// + /// # Examples + /// ```compile_fail + /// use qfall_schemes::pk_encryption::LPR; + /// use qfall_math::integer::Z; + /// let n = Z::from(2); + /// + /// let (q, alpha) = LPR::gen_new_public_parameters(&n); + /// ``` + /// + /// Panics... + /// - if `n` does not fit into an [`i64`]. + fn gen_new_public_parameters(n: &Z) -> (Modulus, Q) { + let n_i64 = i64::try_from(n).unwrap(); + + // generate prime q in [n^3 / 2, n^3] + let upper_bound: Z = n.pow(3).unwrap(); + let lower_bound = upper_bound.div_ceil(2); + // prime used due to guide from GPV08 after Proposition 8.1 + // on how to choose appropriate parameters, but prime is not + // necessarily needed for this scheme to be correct or secure + let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); + + // Found out by experience as the bound is not tight enough to ensure correctness for large n. + // Hence, a small factor roughly of max(log n - 4, 1) has to be applied. + // Checked for 100 parameter sets with 10 cycles each and no mismatching decryptions occurred. + let factor = match n_i64 { + 1..=20 => 1, + 21..=40 => 2, + 41..=80 => 3, + 81..=160 => 4, + _ => 5, + }; + // α = 1/(sqrt(n) * log^2 n) + let alpha = 1 / (factor * n.sqrt() * n.log(2).unwrap().pow(3).unwrap()); + + let q = Modulus::from(q); + + (q, alpha) + } + + /// Checks the public parameters for + /// correctness according to Lemma 3.1 of [\[4\]](). + /// + /// The required properties are: + /// - α = o (1 / (sqrt(n) * log^3 n)) + /// + /// **WARNING**: This bound is not tight. Hence, we added a small factor + /// loosely corresponding to max(log n - 4, 1) below to ensure correctness + /// with overwhelming probability. + /// + /// Returns an empty result if the public parameters guarantee correctness + /// with overwhelming probability or a [`MathError`] if the instance would + /// not be correct. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::LPR; + /// let lpr = LPR::default(); + /// + /// let is_valid = lpr.check_correctness().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// correct LPR public key encryption instance. + /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError) + /// if the value does not fit into an [`i64`] + pub fn check_correctness(&self) -> Result<(), MathError> { + let n_i64 = i64::try_from(&self.n)?; + + if self.n <= Z::ONE { + return Err(MathError::InvalidIntegerInput(String::from( + "n must be chosen bigger than 1.", + ))); + } + + // Found out by experience as the bound is not tight enough to ensure correctness for large n. + // Hence, a small factor roughly of max(log n - 4, 1) has to be applied. + // Checked for 100 parameter sets with 10 cycles each and no mismatching decryptions occurred. + let factor = match n_i64 { + 1..=20 => 1, + 21..=40 => 2, + 41..=80 => 3, + 81..=160 => 4, + _ => 5, + }; + // α = o (1 / sqrt(n) * log^3 n )) + if self.alpha > 1 / (factor * self.n.sqrt() * self.n.log(2).unwrap().pow(3).unwrap()) { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log^3 n), but α < 1 / (sqrt(n) * log^3 n) is required. Please check the documentation!" + ))); + } + + Ok(()) + } + + /// Checks the public parameters for security according to Section 2.2 + /// and Lemma 3.2 of [\[4\]](). + /// + /// The required properties are: + /// - q * α >= 2 sqrt(n) + /// + /// Returns an empty result if the public parameters guarantee security + /// w.r.t. `n` or a [`MathError`] if the instance would + /// not be secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::LPR; + /// let lpr = LPR::default(); + /// + /// let is_valid = lpr.check_security().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// secure LPR public key encryption instance. + pub fn check_security(&self) -> Result<(), MathError> { + let q = Z::from(&self.q); + + // Security requirements + // q * α >= 2 sqrt(n) + if &q * &self.alpha < 2 * self.n.sqrt() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.", + ))); + } + + Ok(()) + } + + /// This function instantiates a 128-bit secure [`LPR`] scheme. + /// + /// The public parameters used for this scheme were generated via `LPR::new_from_n(350)` + /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). + pub fn secure128() -> Self { + Self::new(500, 76859609, 0.000005) + } +} + +impl Default for LPR { + /// Initializes a [`LPR`] struct with parameters generated by `LPR::new_from_n(3)`. + /// This parameter choice is not secure as the dimension of the lattice is too small, + /// but it provides an efficient working example. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::LPR; + /// + /// let lpr = LPR::default(); + /// ``` + fn default() -> Self { + let n = Z::from(10); + let q = Modulus::from(983); + let alpha = Q::from(0.0072); + + Self { n, q, alpha } + } +} + +impl PKEncryptionScheme for LPR { + type Cipher = MatZq; + type PublicKey = MatZq; + type SecretKey = MatZq; + + /// Generates a (pk, sk) pair for the LPR public key encryption scheme + /// by following these steps: + /// - A <- Z_q^{n x n} + /// - s <- χ^n + /// - e <- χ^n + /// - b^t = s^t * A + e^t + /// - A = [A^t | b]^t + /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. + /// + /// Then, `pk = A` and `sk = s` are returned. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, LPR}; + /// let lpr = LPR::default(); + /// + /// let (pk, sk) = lpr.gen(); + /// ``` + fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + // A <- Z_q^{n x n} + let mat_a = MatZq::sample_uniform(&self.n, &self.n, &self.q); + // s <- χ^n + let vec_s = MatZq::sample_discrete_gauss( + &self.n, + 1, + &self.q, + &self.n, + 0, + &self.alpha * Z::from(&self.q), + ) + .unwrap(); + // e <- χ^n + let vec_e_t = MatZq::sample_discrete_gauss( + 1, + &self.n, + &self.q, + &self.n, + 0, + &self.alpha * Z::from(&self.q), + ) + .unwrap(); + + // b^t = s^t * A + e^t + let vec_b_t = vec_s.transpose() * &mat_a + vec_e_t; + + // A = [A^t | b]^t + let mat_a = mat_a.concat_vertical(&vec_b_t).unwrap(); + + // pk = A, sk = s + (mat_a, vec_s) + } + + /// Generates an encryption of `message mod 2` for the provided public key by following these steps: + /// - r <- χ^n + /// - e <- χ^{n+1} + /// - c = A * r + e + [0^{1 x n} | msg * ⌊q/2⌋]^t + /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. + /// + /// Then, cipher `c` as a vector of type [`MatZq`] is returned. + /// + /// Parameters: + /// - `pk`: specifies the public key `pk = A` + /// - `message`: specifies the message that should be encrypted + /// + /// Returns a cipher `c` of type [`MatZq`]. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, LPR}; + /// let lpr = LPR::default(); + /// let (pk, sk) = lpr.gen(); + /// + /// let cipher = lpr.enc(&pk, 1); + /// ``` + fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { + // generate message = message mod 2 + let message: Z = message.into() % 2; + + // x <- χ^n + let vec_r = MatZq::sample_discrete_gauss( + &self.n, + 1, + &self.q, + &self.n, + 0, + &self.alpha * Z::from(&self.q), + ) + .unwrap(); + // e <- χ^{n+1} + let vec_e = MatZq::sample_discrete_gauss( + &(&self.n + 1), + 1, + &self.q, + &self.n, + 0, + &self.alpha * Z::from(&self.q), + ) + .unwrap(); + + // c = A * r + e + [0^{1xn} | msg * ⌊q/2⌋]^t + let mut c = pk * vec_r + vec_e; + + // hide message in last entry + // compute msg * ⌊q/2⌋ + let msg_q_half = message * Z::from(&self.q).div_floor(2); + // set last entry of c = last_entry + msg * ⌊q/2⌋ + let last_entry: Zq = c.get_entry(-1, 0).unwrap(); + c.set_entry(-1, 0, last_entry + msg_q_half).unwrap(); + + c + } + + /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - x = [-sk^t | 1] * c + /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. + /// + /// Parameters: + /// - `sk`: specifies the secret key `sk = s` + /// - `cipher`: specifies the cipher containing `cipher = c` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, LPR}; + /// use qfall_math::integer::Z; + /// let lpr = LPR::default(); + /// let (pk, sk) = lpr.gen(); + /// let cipher = lpr.enc(&pk, 1); + /// + /// let m = lpr.dec(&sk, &cipher); + /// + /// assert_eq!(Z::ONE, m); + /// ``` + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + let result = (Z::MINUS_ONE * sk.transpose()) + .concat_horizontal(&MatZq::identity(1, 1, &self.q)) + .unwrap() + .dot_product(cipher) + .unwrap(); + let result: Z = result.get_representative_least_absolute_residue().abs(); + + let q_half = Z::from(&self.q).div_floor(2); + + if result.distance(Z::ZERO) > result.distance(q_half) { + Z::ONE + } else { + Z::ZERO + } + } +} + +// adds generic multi-bit encryption to this scheme +impl GenericMultiBitEncryption for LPR {} + +#[cfg(test)] +mod test_pp_generation { + use super::LPR; + use super::Z; + + /// Checks whether `new` is available for types implementing [`Into`]. + #[test] + fn new_availability() { + let _ = LPR::new(2u8, 2u32, 2u64); + let _ = LPR::new(2u16, 2i32, 2i64); + let _ = LPR::new(2i16, 2u32, 2u8); + let _ = LPR::new(Z::from(2), 2u8, 2i8); + } + + /// Checks whether `new_from_n` works properly for different choices of n. + #[test] + fn suitable_security_params() { + let n_choices = [ + 10, 11, 12, 13, 14, 25, 50, 100, 250, 500, 1000, 2500, 5000, 5001, 10000, + ]; + + for n in n_choices { + let _ = LPR::new_from_n(n); + } + } + + /// Checks whether the [`Default`] parameter choice is suitable. + #[test] + fn default_suitable() { + let lpr = LPR::default(); + + assert!(lpr.check_correctness().is_ok()); + assert!(lpr.check_security().is_ok()); + } + + /// Checks whether the generated public parameters from `new_from_n` are + /// valid choices according to security and correctness of the scheme. + #[test] + fn choice_valid() { + let n_choices = [10, 14, 25, 50, 125, 300, 600, 1200, 4000, 6000]; + + for n in n_choices { + let lpr = LPR::new_from_n(n); + + assert!(lpr.check_correctness().is_ok()); + assert!(lpr.check_security().is_ok()); + } + } + + /// Ensures that `new_from_n` is available for types implementing [`Into`]. + #[test] + #[allow(clippy::needless_borrows_for_generic_args)] + fn availability() { + let _ = LPR::new_from_n(10u8); + let _ = LPR::new_from_n(10u16); + let _ = LPR::new_from_n(10u32); + let _ = LPR::new_from_n(10u64); + let _ = LPR::new_from_n(10i8); + let _ = LPR::new_from_n(10i16); + let _ = LPR::new_from_n(10i32); + let _ = LPR::new_from_n(10i64); + let _ = LPR::new_from_n(Z::from(10)); + let _ = LPR::new_from_n(&Z::from(10)); + } + + /// Checks whether `new_from_n` returns an error for invalid input n. + #[test] + #[should_panic] + fn invalid_n() { + LPR::new_from_n(9); + } + + /// Checks whether `secure128` outputs a new instance with correct and secure + /// parameters. + #[test] + fn secure128_validity() { + let lpr = LPR::secure128(); + + assert!(lpr.check_correctness().is_ok()); + assert!(lpr.check_security().is_ok()); + } +} + +#[cfg(test)] +mod test_lpr { + use super::LPR; + use crate::pk_encryption::PKEncryptionScheme; + use qfall_math::integer::Z; + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and small n. + #[test] + fn cycle_zero_small_n() { + let msg = Z::ZERO; + let lpr = LPR::default(); + + let (pk, sk) = lpr.gen(); + let cipher = lpr.enc(&pk, &msg); + let m = lpr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and small n. + #[test] + fn cycle_one_small_n() { + let msg = Z::ONE; + let lpr = LPR::default(); + + let (pk, sk) = lpr.gen(); + let cipher = lpr.enc(&pk, &msg); + let m = lpr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and larger n. + #[test] + fn cycle_zero_large_n() { + let msg = Z::ZERO; + let lpr = LPR::new_from_n(50); + + let (pk, sk) = lpr.gen(); + let cipher = lpr.enc(&pk, &msg); + let m = lpr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and larger n. + #[test] + fn cycle_one_large_n() { + let msg = Z::ONE; + let lpr = LPR::new_from_n(50); + + let (pk, sk) = lpr.gen(); + let cipher = lpr.enc(&pk, &msg); + let m = lpr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks that modulus 2 is applied correctly. + #[test] + fn modulus_application() { + let messages = [2, 3, i64::MAX, i64::MIN]; + let dr = LPR::default(); + let (pk, sk) = dr.gen(); + + for msg in messages { + let msg_mod = Z::from(msg.rem_euclid(2)); + + let cipher = dr.enc(&pk, msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg_mod, m); + } + } +} + +#[cfg(test)] +mod test_multi_bits { + use super::{GenericMultiBitEncryption, PKEncryptionScheme, LPR}; + use qfall_math::integer::Z; + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large positive values. + #[test] + fn positive() { + let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; + + for value in values { + let msg = Z::from(value); + let scheme = LPR::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for zero. + #[test] + fn zero() { + let msg = Z::ZERO; + let scheme = LPR::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large negative values, which are not encrypted itself, + /// but their absolute value. + #[test] + fn negative() { + let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; + + for value in values { + let msg = Z::from(value); + let scheme = LPR::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg.abs(), m); + } + } +} diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs new file mode 100644 index 0000000..1efdd5c --- /dev/null +++ b/src/pk_encryption/regev.rs @@ -0,0 +1,672 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains an implementation of the IND-CPA secure +//! public key Regev encryption scheme. + +use super::{GenericMultiBitEncryption, PKEncryptionScheme}; +use qfall_math::{ + error::MathError, + integer::{MatZ, Z}, + integer_mod_q::{MatZq, Modulus, Zq}, + rational::Q, + traits::{Concatenate, Distance, MatrixGetEntry, MatrixSetEntry, Pow}, +}; +use serde::{Deserialize, Serialize}; + +/// This struct manages and stores the public parameters of a [`Regev`] +/// public key encryption instance. +/// +/// Attributes: +/// - `n`: specifies the security parameter, which is not equal to the bit-security level +/// - `m`: defines the dimension of the underlying lattice +/// - `q`: specifies the modulus over which the encryption is computed +/// - `alpha`: specifies the Gaussian parameter used for independent +/// sampling from the discrete Gaussian distribution +/// +/// # Examples +/// ``` +/// use qfall_schemes::pk_encryption::{Regev, PKEncryptionScheme}; +/// use qfall_math::integer::Z; +/// // setup public parameters and key pair +/// let regev = Regev::default(); +/// let (pk, sk) = regev.gen(); +/// +/// // encrypt a bit +/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 +/// let cipher = regev.enc(&pk, &msg); +/// +/// // decrypt +/// let m = regev.dec(&sk, &cipher); +/// +/// assert_eq!(msg, m); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct Regev { + n: Z, // security parameter + m: Z, // number of rows of matrix A + q: Modulus, // modulus + alpha: Q, // Gaussian parameter for sampleZ +} + +impl Regev { + /// Instantiates a [`Regev`] PK encryption instance with the + /// specified parameters. + /// + /// **WARNING:** The given parameters are not checked for security nor + /// correctness of the scheme. + /// If you want to check your parameters for provable security and correctness, + /// use [`Regev::check_correctness`] and [`Regev::check_security`]. + /// Or use [`Regev::new_from_n`] for generating secure and correct + /// public parameters for [`Regev`] according to your choice of `n`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// - `m`: specifies the number of columns of matrix `A` + /// - `q`: specifies the modulus + /// - `alpha`: specifies the Gaussian parameter used for independent + /// sampling from the discrete Gaussian distribution + /// + /// Returns a [`Regev`] PK encryption instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::Regev; + /// + /// let regev = Regev::new(3, 16, 13, 2); + /// ``` + /// + /// # Panics ... + /// - if the given modulus `q <= 1`. + pub fn new( + n: impl Into, + m: impl Into, + q: impl Into, + alpha: impl Into, + ) -> Self { + let n: Z = n.into(); + let m: Z = m.into(); + let q: Modulus = q.into(); + let alpha: Q = alpha.into(); + + Self { n, m, q, alpha } + } + + /// Generates a new [`Regev`] instance, i.e. a new set of suitable + /// (provably secure and correct) public parameters, + /// given the security parameter `n` for `n >= 10`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a correct and secure [`Regev`] PK encryption instance or + /// a [`MathError`] if the given `n < 10`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::Regev; + /// + /// let regev = Regev::new_from_n(15); + /// ``` + /// + /// Panics... + /// - if `n < 10`. + /// - if `n` does not fit into an [`i64`]. + pub fn new_from_n(n: impl Into) -> Self { + let n = n.into(); + if n < 10 { + panic!("Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise."); + } + + let mut m: Z; + let mut q: Modulus; + let mut alpha: Q; + (m, q, alpha) = Self::gen_new_public_parameters(&n); + let mut out = Self { + n: n.clone(), + m, + q, + alpha, + }; + while out.check_correctness().is_err() || out.check_security().is_err() { + (m, q, alpha) = Self::gen_new_public_parameters(&n); + out = Self { + n: n.clone(), + m, + q, + alpha, + }; + } + + out + } + + /// Generates new public parameters, which must not be secure or correct + /// depending on the random choice of `q`. At least every fifth execution + /// of this function should output a valid set of public parameters, + /// ensuring a secure and correct PK encryption scheme. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a set of public parameters `(m, q, alpha)` chosen according to + /// the provided `n`. + /// + /// # Examples + /// ```compile_fail + /// use qfall_schemes::pk_encryption::Regev; + /// use qfall_math::integer::Z; + /// let n = Z::from(2); + /// + /// let (m, q, alpha) = Regev::gen_new_public_parameters(&n); + /// ``` + /// + /// Panics... + /// - if `n` does not fit into an [`i64`]. + fn gen_new_public_parameters(n: &Z) -> (Z, Modulus, Q) { + let n_i64 = i64::try_from(n).unwrap(); + // these powers are chosen according to experience s.t. at least every + // fifth generation of public parameters outputs a valid pair + let power = match n_i64 { + 2..=4 => 5, + 5 => 4, + _ => 3, + }; + + // generate prime q in [n^power / 2, n^power] + let upper_bound: Z = n.pow(power).unwrap(); + let lower_bound = upper_bound.div_ceil(2); + // prime used due to guide from GPV08 after Proposition 8.1 + // on how to choose appropriate parameters, but prime is not + // necessarily needed for this scheme to be correct or secure + let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); + + // choose m = (n+1) log q + let m = (n + Z::ONE) * q.log(2).unwrap().ceil(); + + // α = 1/(2 * sqrt(n) * log^2 n) + let alpha = 1 / (2 * n.sqrt() * n.log(2).unwrap().pow(2).unwrap()); + + let q = Modulus::from(q); + + (m, q, alpha) + } + + /// Checks the public parameters for + /// correctness according to Lemma 5.1 of [\[3\]](). + /// + /// The required properties are: + /// - α = o (1 / ( sqrt(n) * log n ) ) + /// - concentration bound with r=5: r * sqrt(m) * α > q/4 + /// + /// **WARNING:** Some requirements are missing to ensure + /// overwhelming correctness of the scheme for small `n`. + /// + /// Returns an empty result if the public parameters guarantee correctness + /// with overwhelming probability or a [`MathError`] if the instance would + /// not be correct. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::Regev; + /// let regev = Regev::default(); + /// + /// let is_valid = regev.check_correctness().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// correct Regev public key encryption instance. + pub fn check_correctness(&self) -> Result<(), MathError> { + let q = Z::from(&self.q); + + if self.n <= Z::ONE { + return Err(MathError::InvalidIntegerInput(String::from( + "n must be chosen bigger than 1.", + ))); + } + + // Correctness requirements + // α = o (1 / ( sqrt(n) * log n ) ) + if self.alpha > 1 / (self.n.sqrt() * self.n.log(2).unwrap()) { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log n), but α < 1 / (sqrt(n) * log n) is required." + ))); + } + // concentration bound with r=5 -> r * sqrt(m) * α > q/4 + if 20 * self.m.sqrt() * &self.alpha > q { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as 5 * sqrt(m) * α > q/4, but 5 * sqrt(m) * α <= q/4 is required." + ))); + } + + Ok(()) + } + + /// Checks the public parameters for security according to Theorem 1.1 + /// and Lemma 5.4 of [\[3\]](). + /// + /// The required properties are: + /// - q * α >= 2 sqrt(n) + /// - m > (n + 1) log q + /// + /// Returns an empty result if the public parameters guarantees security w.r.t. `n` + /// or a [`MathError`] if the instance would not be secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::Regev; + /// let regev = Regev::default(); + /// + /// let is_valid = regev.check_security().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// secure Regev public key encryption instance. + pub fn check_security(&self) -> Result<(), MathError> { + let q = Z::from(&self.q); + + // Security requirements + // q * α >= 2 sqrt(n) + if &q * &self.alpha < 2 * self.n.sqrt() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.", + ))); + } + // m > (n + 1) log q + if self.m <= ((&self.n + Z::ONE) * q.log(2).unwrap()).ceil() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as m <= (n + 1) log q, but m > (n + 1) log q is required.", + ))); + } + + Ok(()) + } + + /// This function instantiates a 128-bit secure [`Regev`] scheme. + /// + /// The public parameters used for this scheme were generated via `Regev::new_from_n(350)` + /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). + pub fn secure128() -> Self { + Self::new(230, 5313, 7764299, 0.0011) + } +} + +impl Default for Regev { + /// Initializes a [`Regev`] struct with parameters generated by `Regev::new_from_n(13)`. + /// This parameter choice is not secure as the dimension of the lattice is too small, + /// but it provides an efficient working example. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::Regev; + /// + /// let regev = Regev::default(); + /// ``` + fn default() -> Self { + let n = Z::from(13); + let m = Z::from(154); + let q = Modulus::from(1427); + let alpha = Q::from(0.01); + + Self { n, m, q, alpha } + } +} + +impl PKEncryptionScheme for Regev { + type Cipher = MatZq; + type PublicKey = MatZq; + type SecretKey = MatZq; + + /// Generates a (pk, sk) pair for the Regev public key encryption scheme + /// by following these steps: + /// - A <- Z_q^{n x m} + /// - s <- Z_q^n + /// - e^t <- χ^m + /// - b^t = s^t * A + e^t + /// - A = [A^t | b]^t + /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. + /// + /// Then, `pk = A` and `sk = s` of type [`MatZq`] are returned. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, Regev}; + /// let regev = Regev::default(); + /// + /// let (pk, sk) = regev.gen(); + /// ``` + fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + // A <- Z_q^{n x m} + let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); + // s <- Z_q^n + let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); + // e^t <- χ^m + let vec_e_t = MatZq::sample_discrete_gauss( + 1, + &self.m, + &self.q, + &self.n, + 0, + &self.alpha * Z::from(&self.q), + ) + .unwrap(); + + // b^t = s^t * A + e^t + let vec_b_t = vec_s.transpose() * &mat_a + vec_e_t; + + // A = [A^t | b]^t + let mat_a = mat_a.concat_vertical(&vec_b_t).unwrap(); + + // pk = A, sk = s + (mat_a, vec_s) + } + + /// Generates an encryption of `message mod 2` for the provided public key by following these steps: + /// - x <- Z_2^m + /// - c = A * x + [0^{1 x n} | msg * ⌊q/2⌋]^t + /// + /// Then, the ciphertext `c` is returned as a vector of type [`MatZq`]. + /// + /// Parameters: + /// - `pk`: specifies the public key `pk = A` + /// - `message`: specifies the message that should be encrypted + /// + /// Returns a cipher `c` of type [`MatZq`]. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, Regev}; + /// let regev = Regev::default(); + /// let (pk, sk) = regev.gen(); + /// + /// let cipher = regev.enc(&pk, 1); + /// ``` + fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { + // generate message = message mod 2 + let message: Z = message.into() % 2; + + // x <- Z_2^m + let vec_x = MatZ::sample_uniform(&self.m, 1, 0, 2).unwrap(); + + // c = A * x + [0^{1xn} | msg * ⌊q/2⌋]^t + let mut c = pk * vec_x; + + // hide message in last entry + // compute msg * ⌊q/2⌋ + let msg_q_half = message * Z::from(&self.q).div_floor(2); + // set last entry of c = last_entry + msg * ⌊q/2⌋ + let last_entry: Zq = c.get_entry(-1, 0).unwrap(); + c.set_entry(-1, 0, last_entry + msg_q_half).unwrap(); + + c + } + + /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - x = [-sk^t | 1] * c + /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. + /// + /// Parameters: + /// - `sk`: specifies the secret key `sk = s` + /// - `cipher`: specifies the cipher containing `cipher = c` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, Regev}; + /// use qfall_math::integer::Z; + /// let regev = Regev::default(); + /// let (pk, sk) = regev.gen(); + /// let cipher = regev.enc(&pk, 1); + /// + /// let m = regev.dec(&sk, &cipher); + /// + /// assert_eq!(Z::ONE, m); + /// ``` + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + let result = (Z::MINUS_ONE * sk) + .concat_vertical(&MatZq::identity(1, 1, &self.q)) + .unwrap() + .dot_product(cipher) + .unwrap(); + let result: Z = result.get_representative_least_absolute_residue().abs(); + + let q_half = Z::from(&self.q).div_floor(2); + + if result.distance(Z::ZERO) > result.distance(q_half) { + Z::ONE + } else { + Z::ZERO + } + } +} + +// adds generic multi-bit encryption to this scheme +impl GenericMultiBitEncryption for Regev {} + +#[cfg(test)] +mod test_pp_generation { + use super::Regev; + use super::Z; + + /// Checks whether `new` is available for types implementing [`Into`]. + #[test] + fn new_availability() { + let _ = Regev::new(2u8, 2u16, 2u32, 2u64); + let _ = Regev::new(2u16, 2u64, 2i32, 2i64); + let _ = Regev::new(2i16, 2i64, 2u32, 2u8); + let _ = Regev::new(Z::from(2), Z::from(2), 2u8, 2i8); + } + + /// Checks whether `new_from_n` works properly for different choices of n. + #[test] + fn suitable_security_params() { + let n_choices = [ + 10, 11, 12, 13, 14, 25, 50, 100, 250, 500, 1000, 2500, 5000, 5001, 10000, + ]; + + for n in n_choices { + let _ = Regev::new_from_n(n); + } + } + + /// Checks whether the [`Default`] parameter choice is suitable. + #[test] + fn default_suitable() { + let regev = Regev::default(); + + assert!(regev.check_correctness().is_ok()); + assert!(regev.check_security().is_ok()); + } + + /// Checks whether the generated public parameters from `new_from_n` are + /// valid choices according to security and correctness of the scheme. + #[test] + fn choice_valid() { + let n_choices = [10, 14, 25, 50, 125, 300, 600, 1200, 4000, 6000]; + + for n in n_choices { + let regev = Regev::new_from_n(n); + assert!(regev.check_correctness().is_ok()); + assert!(regev.check_security().is_ok()); + } + } + + /// Ensures that `new_from_n` is available for types implementing [`Into`]. + #[test] + #[allow(clippy::needless_borrows_for_generic_args)] + fn availability() { + let _ = Regev::new_from_n(10u8); + let _ = Regev::new_from_n(10u16); + let _ = Regev::new_from_n(10u32); + let _ = Regev::new_from_n(10u64); + let _ = Regev::new_from_n(10i8); + let _ = Regev::new_from_n(10i16); + let _ = Regev::new_from_n(10i32); + let _ = Regev::new_from_n(10i64); + let _ = Regev::new_from_n(Z::from(10)); + let _ = Regev::new_from_n(&Z::from(10)); + } + + /// Checks whether `new_from_n` returns an error for invalid input n. + #[test] + #[should_panic] + fn invalid_n() { + Regev::new_from_n(9); + } + + /// Checks whether `secure128` outputs a new instance with correct and secure + /// parameters. + #[test] + fn secure128_validity() { + let regev = Regev::secure128(); + + assert!(regev.check_correctness().is_ok()); + assert!(regev.check_security().is_ok()); + } +} + +#[cfg(test)] +mod test_regev { + use super::Regev; + use crate::pk_encryption::PKEncryptionScheme; + use qfall_math::integer::Z; + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and small n. + #[test] + fn cycle_zero_small_n() { + let msg = Z::ZERO; + let regev = Regev::default(); + + let (pk, sk) = regev.gen(); + let cipher = regev.enc(&pk, &msg); + let m = regev.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and small n. + #[test] + fn cycle_one_small_n() { + let msg = Z::ONE; + let regev = Regev::default(); + + let (pk, sk) = regev.gen(); + let cipher = regev.enc(&pk, &msg); + let m = regev.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and larger n. + #[test] + fn cycle_zero_large_n() { + let msg = Z::ZERO; + let regev = Regev::new_from_n(50); + + let (pk, sk) = regev.gen(); + let cipher = regev.enc(&pk, &msg); + let m = regev.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and larger n. + #[test] + fn cycle_one_large_n() { + let msg = Z::ONE; + let regev = Regev::new_from_n(50); + + let (pk, sk) = regev.gen(); + let cipher = regev.enc(&pk, &msg); + let m = regev.dec(&sk, &cipher); + assert_eq!(msg, m); + } + + /// Checks that modulus 2 is applied correctly. + #[test] + fn modulus_application() { + let messages = [2, 3, i64::MAX, i64::MIN]; + let regev = Regev::default(); + let (pk, sk) = regev.gen(); + + for msg in messages { + let msg_mod = Z::from(msg.rem_euclid(2)); + + let cipher = regev.enc(&pk, msg); + let m = regev.dec(&sk, &cipher); + + assert_eq!(msg_mod, m); + } + } +} + +#[cfg(test)] +mod test_multi_bits { + use super::{GenericMultiBitEncryption, PKEncryptionScheme, Regev}; + use qfall_math::integer::Z; + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large positive values. + #[test] + fn positive() { + let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; + + for value in values { + let msg = Z::from(value); + let scheme = Regev::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for zero. + #[test] + fn zero() { + let msg = Z::ZERO; + let scheme = Regev::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large negative values, which are not encrypted itself, + /// but their absolute value. + #[test] + fn negative() { + let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; + + for value in values { + let msg = Z::from(value); + let scheme = Regev::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg.abs(), m); + } + } +} diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs new file mode 100644 index 0000000..505f616 --- /dev/null +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -0,0 +1,692 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains an implementation of the IND-CPA secure +//! public key Regev encryption scheme with an instantiation of the regularity lemma +//! via a discrete Gaussian distribution. + +use super::{GenericMultiBitEncryption, PKEncryptionScheme}; +use qfall_math::{ + error::MathError, + integer::Z, + integer_mod_q::{MatZq, Modulus, Zq}, + rational::Q, + traits::{Distance, Pow}, +}; +use serde::{Deserialize, Serialize}; + +/// This struct manages and stores the public parameters of a [`RegevWithDiscreteGaussianRegularity`] +/// public key encryption instance. +/// +/// Attributes: +/// - `n`: specifies the security parameter, which is not equal to the bit-security level +/// - `m`: defines the dimension of the underlying lattice +/// - `q`: specifies the modulus over which the encryption is computed +/// - `r`: specifies the Gaussian parameter used for SampleD, +/// i.e. used for encryption +/// - `alpha`: specifies the Gaussian parameter used for independent +/// sampling from the discrete Gaussian distribution +/// +/// # Examples +/// ``` +/// use qfall_schemes::pk_encryption::{RegevWithDiscreteGaussianRegularity, PKEncryptionScheme}; +/// use qfall_math::integer::Z; +/// // setup public parameters and key pair +/// let regev = RegevWithDiscreteGaussianRegularity::default(); +/// let (pk, sk) = regev.gen(); +/// +/// // encrypt a bit +/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 +/// let cipher = regev.enc(&pk, &msg); +/// +/// // decrypt +/// let m = regev.dec(&sk, &cipher); +/// +/// assert_eq!(msg, m); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct RegevWithDiscreteGaussianRegularity { + n: Z, // security parameter + m: Z, // number of rows of matrix A + q: Modulus, // modulus + r: Q, // Gaussian parameter for sampleD + alpha: Q, // Gaussian parameter for sampleZ +} + +impl RegevWithDiscreteGaussianRegularity { + /// Instantiates a [`RegevWithDiscreteGaussianRegularity`] PK encryption instance with the + /// specified parameters. + /// + /// **WARNING:** The given parameters are not checked for security nor + /// correctness of the scheme. + /// If you want to check your parameters for provable security and correctness, + /// use [`RegevWithDiscreteGaussianRegularity::check_correctness`] and [`RegevWithDiscreteGaussianRegularity::check_security`]. + /// Or use [`RegevWithDiscreteGaussianRegularity::new_from_n`] for generating secure and correct + /// public parameters for [`RegevWithDiscreteGaussianRegularity`] according to your choice of `n`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// - `m`: specifies the number of columns of matrix `A` + /// - `q`: specifies the modulus + /// - `r`: specifies the Gaussian parameter used for SampleD, + /// i.e. used for encryption + /// - `alpha`: specifies the Gaussian parameter used for independent + /// sampling from the discrete Gaussian distribution + /// + /// Returns a [`RegevWithDiscreteGaussianRegularity`] PK encryption instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RegevWithDiscreteGaussianRegularity; + /// + /// let regev = RegevWithDiscreteGaussianRegularity::new(2, 16, 443, 4, 0.15625); + /// ``` + /// + /// # Panics ... + /// - if the given modulus `q <= 1`. + pub fn new( + n: impl Into, + m: impl Into, + q: impl Into, + r: impl Into, + alpha: impl Into, + ) -> Self { + let n: Z = n.into(); + let m: Z = m.into(); + let q: Modulus = q.into(); + let r: Q = r.into(); + let alpha: Q = alpha.into(); + + Self { n, m, q, r, alpha } + } + + /// Generates a new [`RegevWithDiscreteGaussianRegularity`] instance, i.e. a new set of suitable + /// (provably secure and correct) public parameters, + /// given the security parameter `n`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a correct and secure [`RegevWithDiscreteGaussianRegularity`] PK encryption instance or + /// a [`MathError`] if the given `n <= 1`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RegevWithDiscreteGaussianRegularity; + /// + /// let regev = RegevWithDiscreteGaussianRegularity::new_from_n(2); + /// ``` + /// + /// # Panics ... + /// - if `n <= 1`. + pub fn new_from_n(n: impl Into) -> Self { + let n = n.into(); + if n <= Z::ONE { + panic!("n must be chosen bigger than 1."); + } + + let mut m: Z; + let mut q: Modulus; + let mut r: Q; + let mut alpha: Q; + (m, q, r, alpha) = Self::gen_new_public_parameters(&n); + let mut out = Self { + n: n.clone(), + m, + q, + r, + alpha, + }; + while out.check_correctness().is_err() || out.check_security().is_err() { + (m, q, r, alpha) = Self::gen_new_public_parameters(&n); + out = Self { + n: n.clone(), + m, + q, + r, + alpha, + }; + } + + out + } + + /// Generates new public parameters, which must not be secure or correct + /// depending on the random choice of `q`. At least every fifth execution + /// of this function should output a valid set of public parameters, + /// ensuring a secure and correct PK encryption scheme. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a set of public parameters `(m, q, r, alpha)` chosen according to + /// the provided `n`. + /// + /// # Examples + /// ```compile_fail + /// use qfall_schemes::pk_encryption::RegevWithDiscreteGaussianRegularity; + /// use qfall_math::integer::Z; + /// let n = Z::from(2); + /// + /// let (m, q, r, alpha) = RegevWithDiscreteGaussianRegularity::gen_new_public_parameters(&n); + /// ``` + fn gen_new_public_parameters(n: &Z) -> (Z, Modulus, Q, Q) { + let n_i64 = i64::try_from(n).unwrap(); + // these powers are chosen according to experience s.t. at least every + // fifth generation of public parameters outputs a valid pair + let power = match n_i64 { + 2 => 9, + 3 => 8, + 4..=5 => 7, + 6..=8 => 6, + 9..=12 => 5, + 13..=30 => 4, + _ => 3, + }; + + // generate prime q in [n^power / 2, n^power] + let upper_bound: Z = n.pow(power).unwrap(); + let lower_bound = upper_bound.div_ceil(2); + // prime used due to guide from GPV08 after Proposition 8.1 + // on how to choose appropriate parameters, but prime is not + // necessarily needed for this scheme to be correct or secure + let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); + + // choose m = 2 (n+1) lg q + let m = (Z::from(2) * (n + Z::ONE) * q.log(10).unwrap()).ceil(); + + // choose r = log m + let r = m.log(2).unwrap(); + + // alpha = 1/(sqrt(m) * log^2 m) + let alpha = 1 / (m.sqrt() * m.log(2).unwrap().pow(2).unwrap()); + + let q = Modulus::from(&q); + + (m, q, r, alpha) + } + + /// Checks the public parameters for correctness according to + /// Lemma 8.2 of [\[2\]](). + /// + /// The required properties are: + /// - n >= 1 + /// - q >= 5 * r * m + /// - α <= 1/(r * sqrt(m) * ω(sqrt(log n)) + /// + /// Returns an empty result if the public parameters guarantee correctness + /// with overwhelming probability or a [`MathError`] if the instance would + /// not be correct. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RegevWithDiscreteGaussianRegularity; + /// let dr = RegevWithDiscreteGaussianRegularity::default(); + /// + /// let is_valid = dr.check_correctness().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// correct RegevWithDiscreteGaussianRegularity public key encryption instance. + pub fn check_correctness(&self) -> Result<(), MathError> { + let q: Z = Z::from(&self.q); + + if self.n <= Z::ONE { + return Err(MathError::InvalidIntegerInput(String::from( + "n must be chosen bigger than 1.", + ))); + } + + // Correctness requirements + // q >= 5 * r * m + if q < 5 * &self.r * &self.m { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as q < 5rm, but q >= 5rm is required.", + ))); + } + // α <= 1/(r * sqrt(m) * ω(sqrt(log n)) + if self.alpha > 1 / (&self.r * self.m.sqrt() * self.n.log(2).unwrap().sqrt()) { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as α > 1/(r*sqrt(m)*ω(sqrt(log n)), but α <= 1/(r*sqrt(m)*ω(sqrt(log n)) is required.", + ))); + } + + Ok(()) + } + + /// Checks the public parameters for security according to + /// Lemma 8.4 of [\[2\]](). + /// + /// The required properties are: + /// - q * α >= n + /// - m >= 2(n + 1) lg (q) + /// - r >= ω( sqrt( log m ) ) + /// + /// Returns an empty result if the public parameters guarantee security w.r.t. `n` + /// or a [`MathError`] if the instance would not be secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RegevWithDiscreteGaussianRegularity; + /// let dr = RegevWithDiscreteGaussianRegularity::default(); + /// + /// let is_valid = dr.check_security().is_ok(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// secure RegevWithDiscreteGaussianRegularity public key encryption instance. + pub fn check_security(&self) -> Result<(), MathError> { + let q: Z = Z::from(&self.q); + + // Security requirements + // q * α >= n + if &q * &self.alpha < self.n { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as q * α < n, but q * α >= n is required.", + ))); + } + // m >= 2(n + 1) lg (q) + if self.m < 2 * (&self.n + 1) * q.log(10).unwrap() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as m < 2(n + 1) lg (q), but m >= 2(n + 1) lg (q) is required.", + ))); + } + // r >= ω( sqrt( log m ) ) + if self.r < self.m.log(2).unwrap().sqrt() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as r < sqrt( log m ) and r >= ω(sqrt(log m)) is required." + ))); + } + + Ok(()) + } + + /// This function instantiates a 128-bit secure [`RegevWithDiscreteGaussianRegularity`] scheme. + /// + /// The public parameters used for this scheme were generated via `RegevWithDiscreteGaussianRegularity::new_from_n(350)` + /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). + pub fn secure128() -> Self { + Self::new(350, 5248, 29892991, 12.357, 0.00009) + } +} + +impl Default for RegevWithDiscreteGaussianRegularity { + /// Initializes a [`RegevWithDiscreteGaussianRegularity`] struct with parameters generated by `RegevWithDiscreteGaussianRegularity::new_from_n(2)`. + /// This parameter choice is not secure as the dimension of the lattice is too small, + /// but it provides an efficient working example. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RegevWithDiscreteGaussianRegularity; + /// + /// let regev = RegevWithDiscreteGaussianRegularity::default(); + /// ``` + fn default() -> Self { + let n = Z::from(2); + let m = Z::from(16); + let q = Modulus::from(443); + let r = Q::from(4); + let alpha = Q::from((1, 64)); + + Self { n, m, q, r, alpha } + } +} + +impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { + type Cipher = (MatZq, Zq); + type PublicKey = (MatZq, MatZq); + type SecretKey = MatZq; + + /// Generates a (pk, sk) pair for the Regev public key encryption scheme + /// by following these steps: + /// - s <- Z_q^n + /// - A <- Z_q^{n x m} + /// - x <- χ^m + /// - p = A^t * s + x + /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. + /// + /// Then, `pk = (A, p)` and `sk = s` are returned. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; + /// let regev = RegevWithDiscreteGaussianRegularity::default(); + /// + /// let (pk, sk) = regev.gen(); + /// ``` + fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + // s <- Z_q^n + let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); + + // A <- Z_q^{n x m} + let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); + // x <- χ^m + let vec_x = MatZq::sample_discrete_gauss( + &self.m, + 1, + &self.q, + &self.n, + 0, + &(&self.alpha * Z::from(&self.q)), + ) + .unwrap(); + // p = A^t * s + x + let vec_p = mat_a.transpose() * &vec_s + vec_x; + + // pk = (A, p), sk = s + ((mat_a, vec_p), vec_s) + } + + /// Generates an encryption of `message mod 2` for the provided public key by following these steps: + /// e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r + /// - u = A * e + /// - c = p^t * e + message * ⌊q/2⌋ + /// + /// Then, `cipher = (u, c)` is returned. + /// + /// Parameters: + /// - `pk`: specifies the public key, which contains two matrices `pk = (A, p)` + /// - `message`: specifies the message that should be encrypted + /// + /// Returns a cipher of the form `cipher = (u, c)` for [`MatZq`] `u` and [`Zq`] `c`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; + /// let regev = RegevWithDiscreteGaussianRegularity::default(); + /// let (pk, sk) = regev.gen(); + /// + /// let cipher = regev.enc(&pk, 1); + /// ``` + fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { + // generate message = message mod 2 + let message: Z = message.into() % 2; + + // e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r + let vec_e = MatZq::sample_d_common(&self.m, &self.q, &self.n, &self.r).unwrap(); + + // u = A * e + let vec_u = &pk.0 * &vec_e; + // c = p^t * e + msg * ⌊q/2⌋ + let q_half = Z::from(&self.q).div_floor(2); + let c = pk.1.dot_product(&vec_e).unwrap() + message * q_half; + + (vec_u, c) + } + + /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - x = c - s^t * u + /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. + /// + /// Parameters: + /// - `sk`: specifies the secret key `sk = s` + /// - `cipher`: specifies the cipher containing `cipher = (u, c)` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; + /// use qfall_math::integer::Z; + /// let regev = RegevWithDiscreteGaussianRegularity::default(); + /// let (pk, sk) = regev.gen(); + /// let cipher = regev.enc(&pk, 1); + /// + /// let m = regev.dec(&sk, &cipher); + /// + /// assert_eq!(Z::ONE, m); + /// ``` + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + let result = &cipher.1 - sk.dot_product(&cipher.0).unwrap(); + let result: Z = result.get_representative_least_absolute_residue().abs(); + + let q_half = Z::from(&self.q).div_floor(2); + + if result.distance(Z::ZERO) > result.distance(q_half) { + Z::ONE + } else { + Z::ZERO + } + } +} + +// adds generic multi-bit encryption to this scheme +impl GenericMultiBitEncryption for RegevWithDiscreteGaussianRegularity {} + +#[cfg(test)] +mod test_pp_generation { + use super::RegevWithDiscreteGaussianRegularity; + use super::Z; + + /// Checks whether `new` is available for types implementing [`Into`]. + #[test] + fn new_availability() { + let _ = RegevWithDiscreteGaussianRegularity::new(2u8, 2u16, 2u32, 2u64, 2i8); + let _ = RegevWithDiscreteGaussianRegularity::new(2u16, 2u64, 2i32, 2i64, 2i16); + let _ = RegevWithDiscreteGaussianRegularity::new(2i16, 2i64, 2u32, 2u8, 2u16); + let _ = RegevWithDiscreteGaussianRegularity::new(Z::from(2), Z::from(2), 2u8, 2i8, 2u32); + } + + /// Checks whether `new_from_n` works properly for different choices of n. + #[test] + fn suitable_security_params() { + let n_choices = [ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 5001, + 10000, + ]; + + for n in n_choices { + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(n); + } + } + + /// Checks whether the [`Default`] parameter choice is suitable. + #[test] + fn default_suitable() { + let dr = RegevWithDiscreteGaussianRegularity::default(); + + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } + + /// Checks whether the generated public parameters from `new_from_n` are + /// valid choices according to security and correctness of the scheme. + #[test] + fn choice_valid() { + let n_choices = [ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 5001, + 10000, + ]; + + for n in n_choices { + let dr = RegevWithDiscreteGaussianRegularity::new_from_n(n); + + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } + } + + /// Ensures that `new_from_n` is available for types implementing [`Into`]. + #[test] + #[allow(clippy::needless_borrows_for_generic_args)] + fn new_from_n_availability() { + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2u8); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2u16); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2u32); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2u64); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2i8); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2i16); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2i32); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2i64); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(Z::from(2)); + let _ = RegevWithDiscreteGaussianRegularity::new_from_n(&Z::from(2)); + } + + /// Checks whether `new_from_n` returns an error for invalid input n. + #[test] + #[should_panic] + fn invalid_n() { + RegevWithDiscreteGaussianRegularity::new_from_n(1); + } + + /// Checks whether `secure128` outputs a new instance with correct and secure + /// parameters. + #[test] + fn secure128_validity() { + let dr = RegevWithDiscreteGaussianRegularity::secure128(); + + assert!(dr.check_correctness().is_ok()); + assert!(dr.check_security().is_ok()); + } +} + +#[cfg(test)] +mod test_regev { + use super::RegevWithDiscreteGaussianRegularity; + use crate::pk_encryption::PKEncryptionScheme; + use qfall_math::integer::Z; + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and small n. + #[test] + fn cycle_zero_small_n() { + let msg = Z::ZERO; + let dr = RegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and small n. + #[test] + fn cycle_one_small_n() { + let msg = Z::ONE; + let dr = RegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 0 and larger n. + #[test] + fn cycle_zero_large_n() { + let msg = Z::ZERO; + let dr = RegevWithDiscreteGaussianRegularity::new_from_n(30); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for message 1 and larger n. + #[test] + fn cycle_one_large_n() { + let msg = Z::ONE; + let dr = RegevWithDiscreteGaussianRegularity::new_from_n(30); + + let (pk, sk) = dr.gen(); + let cipher = dr.enc(&pk, &msg); + let m = dr.dec(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks that modulus 2 is applied correctly. + #[test] + fn modulus_application() { + let messages = [2, 3, i64::MAX, i64::MIN]; + let regev = RegevWithDiscreteGaussianRegularity::default(); + let (pk, sk) = regev.gen(); + + for msg in messages { + let msg_mod = Z::from(msg.rem_euclid(2)); + + let cipher = regev.enc(&pk, msg); + let m = regev.dec(&sk, &cipher); + + assert_eq!(msg_mod, m); + } + } +} + +#[cfg(test)] +mod test_multi_bits { + use super::{ + GenericMultiBitEncryption, PKEncryptionScheme, RegevWithDiscreteGaussianRegularity, + }; + use qfall_math::integer::Z; + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large positive values. + #[test] + fn positive() { + let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; + + for value in values { + let msg = Z::from(value); + let scheme = RegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for zero. + #[test] + fn zero() { + let msg = Z::ZERO; + let scheme = RegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg, m); + } + + /// Checks whether the multi-bit encryption cycle works properly + /// for small and large negative values, which are not encrypted itself, + /// but their absolute value. + #[test] + fn negative() { + let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; + + for value in values { + let msg = Z::from(value); + let scheme = RegevWithDiscreteGaussianRegularity::default(); + + let (pk, sk) = scheme.gen(); + let cipher = scheme.enc_multiple_bits(&pk, &msg); + let m = scheme.dec_multiple_bits(&sk, &cipher); + + assert_eq!(msg.abs(), m); + } + } +} diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs new file mode 100644 index 0000000..3ac8f5f --- /dev/null +++ b/src/pk_encryption/ring_lpr.rs @@ -0,0 +1,645 @@ +// Copyright © 2023 Niklas Siemer +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module contains an implementation of the IND-CPA secure +//! public key Ring-LPR encryption scheme. + +use super::PKEncryptionScheme; +use qfall_crypto::utils::{ + common_encodings::{ + decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, + }, + common_moduli::new_anticyclic, +}; +use qfall_math::{ + error::MathError, + integer::Z, + integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolynomialRingZq}, + rational::Q, + traits::Pow, +}; +use serde::{Deserialize, Serialize}; + +/// This struct manages and stores the public parameters of a [`RingLPR`] +/// public key encryption instance. +/// +/// This encryption scheme is implemented according to the description in [\[1\]](). +/// +/// Attributes: +/// - `n`: specifies the security parameter, which is not equal to the bit-security level +/// - `q`: specifies the modulus over which the encryption is computed +/// - `alpha`: specifies the Gaussian parameter used for independent +/// sampling from the discrete Gaussian distribution +/// +/// # Examples +/// ``` +/// use qfall_schemes::pk_encryption::{RingLPR, PKEncryptionScheme}; +/// use qfall_math::integer::Z; +/// // setup public parameters and key pair +/// let lpr = RingLPR::default(); +/// let (pk, sk) = lpr.gen(); +/// +/// // encrypt a bit +/// let msg = Z::from(15); // must be at most n bits, i.e. for default 2^16 - 1 +/// let cipher = lpr.enc(&pk, &msg); +/// +/// // decrypt +/// let m = lpr.dec(&sk, &cipher); +/// +/// assert_eq!(msg, m); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct RingLPR { + n: Z, // security parameter + q: ModulusPolynomialRingZq, // modulus + alpha: Q, // Gaussian parameter for sampleZ +} + +impl RingLPR { + /// Instantiates a [`RingLPR`] PK encryption instance with the + /// specified parameters. + /// + /// **WARNING:** The given parameters are not checked for security nor + /// correctness of the scheme. + /// If you want to check your parameters for provable security and correctness, + /// use [`RingLPR::check_correctness`] and [`RingLPR::check_security`]. + /// Or use [`RingLPR::new_from_n`] for generating secure and correct + /// public parameters for [`RingLPR`] according to your choice of `n`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// - `q`: specifies the modulus + /// - `alpha`: specifies the Gaussian parameter used for independent + /// sampling from the discrete Gaussian distribution + /// + /// Returns a correct and secure [`RingLPR`] PK encryption instance or + /// a [`MathError`] if the instance would not be correct or secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RingLPR; + /// + /// let lpr = RingLPR::new(3, 13, 2); + /// ``` + /// + /// # Panics ... + /// - if the given modulus `q <= 1`. + /// - if `n < 0`. + pub fn new(n: impl Into, q: impl Into, alpha: impl Into) -> Self { + let n: Z = n.into(); + + // mod = (X^n + 1) mod q + let q = new_anticyclic(&n, q).unwrap(); + + let alpha: Q = alpha.into(); + + Self { n, q, alpha } + } + + /// Generates a new [`RingLPR`] instance, i.e. a new set of suitable + /// (provably secure and correct) public parameters, + /// given the security parameter `n` for `n >= 10`. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a correct and secure [`RingLPR`] PK encryption instance or + /// a [`MathError`] if the given `n < 10`. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RingLPR; + /// + /// let lpr = RingLPR::new_from_n(16); + /// ``` + /// + /// Panics... + /// - if `n < 10` + /// - if `n` does not fit into an [`i64`]. + pub fn new_from_n(n: impl Into) -> Self { + let n = n.into(); + assert!( + n >= 10, + "Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise." + ); + + let (mut q, mut alpha) = Self::gen_new_public_parameters(&n); + let mut out = Self { + n: n.clone(), + q, + alpha, + }; + while out.check_correctness().is_err() || out.check_security().is_err() { + (q, alpha) = Self::gen_new_public_parameters(&n); + out = Self { + n: n.clone(), + q, + alpha, + }; + } + + out + } + + /// Generates new public parameters, which must not be secure or correct + /// depending on the random choice of `q`. At least every fifth execution + /// of this function should output a valid set of public parameters, + /// ensuring a secure and correct PK encryption scheme. + /// + /// Parameters: + /// - `n`: specifies the security parameter and number of rows + /// of the uniform at random instantiated matrix `A` + /// + /// Returns a set of public parameters `(q, alpha)` chosen according to + /// the provided `n`. + /// + /// # Examples + /// ```compile_fail + /// use qfall_schemes::pk_encryption::RingLPR; + /// use qfall_math::integer::Z; + /// let n = Z::from(2); + /// + /// let (q, alpha) = RingLPR::gen_new_public_parameters(&n); + /// ``` + /// + /// Panics... + /// - if `n` does not fit into an [`i64`]. + fn gen_new_public_parameters(n: &Z) -> (ModulusPolynomialRingZq, Q) { + let n_i64 = i64::try_from(n).unwrap(); + + // generate prime q in [n^3 / 2, n^3] + let upper_bound: Z = n.pow(3).unwrap(); + let lower_bound = upper_bound.div_ceil(2); + // prime used due to guide from GPV08 after Proposition 8.1 + // on how to choose appropriate parameters, but prime is not + // necessarily needed for this scheme to be correct or secure + let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); + + // Found out by experience as the bound is not tight enough to ensure correctness for large n. + // Hence, a small factor roughly of max(log n - 4, 1) has to be applied. + // Checked for 100 parameter sets with 10 cycles each and no mismatching decryptions occurred. + let factor = match n_i64 { + 1..=20 => 1, + 21..=40 => 2, + 41..=80 => 3, + 81..=160 => 4, + _ => 5, + }; + // α = 1/(sqrt(n) * log^2 n) + let alpha = 1 / (factor * n.sqrt() * n.log(2).unwrap().pow(3).unwrap()); + + // mod = (X^n + 1) mod q + let q = new_anticyclic(n, q).unwrap(); + + (q, alpha) + } + + /// Checks the public parameters for + /// correctness according to Lemma 3.1 of [\[4\]](). + /// + /// The required properties are: + /// - α = o (1 / (sqrt(n) * log^3 n)) + /// - n = 2^d for some d ∈ N_0 + /// + /// **WARNING**: This bound is not tight. Hence, we added a small factor + /// loosely corresponding to max(log n - 4, 1) below to ensure correctness + /// with overwhelming probability. + /// + /// Returns an empty result if the public parameters guarantee correctness + /// with overwhelming probability or a [`MathError`] if the instance would + /// not be correct. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RingLPR; + /// let lpr = RingLPR::default(); + /// + /// assert!(lpr.check_correctness().is_ok()); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// correct RingLPR public key encryption instance. + /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError) + /// if the value does not fit into an [`i64`]. + pub fn check_correctness(&self) -> Result<(), MathError> { + let n_i64 = i64::try_from(&self.n)?; + + if self.n <= Z::ONE { + return Err(MathError::InvalidIntegerInput(String::from( + "n must be chosen bigger than 1.", + ))); + } + + // ensure n = 2^d for some d ∈ N_0 + let result = self.n.is_perfect_power(); + let err_msg = String::from( + "n is not a perfect power of 2, \ + which is required for the correctness of this scheme.", + ); + if let Some((root, _)) = result { + if root != 2 { + return Err(MathError::InvalidIntegerInput(err_msg)); + } + } else { + return Err(MathError::InvalidIntegerInput(err_msg)); + } + + // Found out by experience as the bound is not tight enough to ensure correctness for large n. + // Hence, a small factor roughly of max(log n - 4, 1) has to be applied. + // Checked for 100 parameter sets with 10 cycles each and no mismatching decryptions occurred. + let factor = match n_i64 { + 1..=20 => 1, + 21..=40 => 2, + 41..=80 => 3, + 81..=160 => 4, + _ => 5, + }; + // α = o (1 / sqrt(n) * log^3 n )) + if self.alpha > 1 / (factor * self.n.sqrt() * self.n.log(2).unwrap().pow(3).unwrap()) { + return Err(MathError::InvalidIntegerInput(String::from( + "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log^3 n), \ + but α < 1 / (sqrt(n) * log^3 n) is required. Please check the documentation!", + ))); + } + + Ok(()) + } + + /// Checks the public parameters for security according to Section 2.2 + /// and Lemma 3.2 of [\[4\]](). + /// + /// The required properties are: + /// - q * α >= 2 sqrt(n) + /// + /// Returns an empty result if the public parameters guarantee security + /// w.r.t. `n` or a [`MathError`] if the instance would + /// not be secure. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RingLPR; + /// let lpr = RingLPR::default(); + /// + /// assert!(lpr.check_security().is_ok()); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if at least one parameter was not chosen appropriately for a + /// secure RingLPR public key encryption instance. + pub fn check_security(&self) -> Result<(), MathError> { + let q = Z::from(&self.q.get_q()); + + // Security requirements + // q * α >= 2 sqrt(n) + if &q * &self.alpha < 2 * self.n.sqrt() { + return Err(MathError::InvalidIntegerInput(String::from( + "Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.", + ))); + } + + Ok(()) + } + + /// This function instantiates a 128-bit secure [`RingLPR`] scheme. + /// + /// The public parameters used for this scheme were generated via `RingLPR::new_from_n(512)` + /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). + pub fn secure128() -> Self { + Self::new(512, 92897729, 0.000005) + } +} + +impl Default for RingLPR { + /// Initializes a [`RingLPR`] struct with parameters generated by `RingLPR::new_from_n(3)`. + /// This parameter choice is not secure as the dimension of the lattice is too small, + /// but it provides an efficient working example. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::RingLPR; + /// + /// let lpr = RingLPR::default(); + /// ``` + fn default() -> Self { + Self::new(16, 2399, 0.0039) + } +} + +impl PKEncryptionScheme for RingLPR { + type Cipher = (PolynomialRingZq, PolynomialRingZq); + type PublicKey = (PolynomialRingZq, PolynomialRingZq); + type SecretKey = PolynomialRingZq; + + /// Generates a (pk, sk) pair for the RingLPR public key encryption scheme + /// by following these steps: + /// - a <- R_q + /// - s <- χ + /// - e <- χ + /// - b = s * a + e + /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. + /// + /// Then, `pk = (a, b)` and `sk = s` are returned. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RingLPR}; + /// let lpr = RingLPR::default(); + /// + /// let (pk, sk) = lpr.gen(); + /// ``` + fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + // a <- R_q + let a = PolynomialRingZq::sample_uniform(&self.q); + // s <- χ + let s = PolynomialRingZq::sample_discrete_gauss( + &self.q, + &self.n, + 0, + &self.alpha * &self.q.get_q(), + ) + .unwrap(); + // e <- χ + let e = PolynomialRingZq::sample_discrete_gauss( + &self.q, + &self.n, + 0, + &self.alpha * &self.q.get_q(), + ) + .unwrap(); + + // b = s * a + e + let b = &a * &s + e; + + // pk = (a, b), sk = s + ((a, b), s) + } + + /// Generates an encryption of `message mod 2^n` for the provided public key by following these steps: + /// - r <- χ + /// - e1 <- χ + /// - e2 <- χ + /// - u = a * r + e1 + /// - v = b * r + e2 + mu * q/2 + /// - c = (u, v) + /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. + /// + /// Then, cipher `c = (u, v)` as a polynomial of type [`PolynomialRingZq`] is returned. + /// + /// Parameters: + /// - `pk`: specifies the public key `pk = (a, b)` + /// - `message`: specifies the message that should be encrypted + /// + /// Returns a cipher `c = (u, v)` of types [`PolynomialRingZq`]. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RingLPR}; + /// let lpr = RingLPR::default(); + /// let (pk, sk) = lpr.gen(); + /// + /// let cipher = lpr.enc(&pk, 15); + /// ``` + fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { + // ensure mu has at most n bits + let message: Z = message.into().abs(); + let mu = message % Z::from(2).pow(&self.n).unwrap(); + // set mu_q_half to polynomial with n {0,1} coefficients + let mu_q_half = encode_z_bitwise_in_polynomialringzq(&self.q, &mu); + + // r <- χ + let r = PolynomialRingZq::sample_discrete_gauss( + &self.q, + &self.n, + 0, + &self.alpha * &self.q.get_q(), + ) + .unwrap(); + // e1 <- χ + let e1 = PolynomialRingZq::sample_discrete_gauss( + &self.q, + &self.n, + 0, + &self.alpha * &self.q.get_q(), + ) + .unwrap(); + // e2 <- χ + let e2 = PolynomialRingZq::sample_discrete_gauss( + &self.q, + &self.n, + 0, + &self.alpha * &self.q.get_q(), + ) + .unwrap(); + + // u = a * r + e1 + let u = &pk.0 * &r + e1; + // v = b * r + e2 + mu * q/2 + let v = &pk.1 * &r + e2 + mu_q_half; + + // c = (u, v) + (u, v) + } + + /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - v - s * u + /// - result = 0 + /// - for each coefficient of v - s * u: + /// - check whether the coefficient mod q is closer to ⌊q/2⌋ than to 0. + /// If so, add 2^coefficient to result. + /// - return result + /// + /// Parameters: + /// - `sk`: specifies the secret key `sk = s` + /// - `cipher`: specifies the cipher containing `cipher = (u, v)` + /// + /// Returns the decryption of `cipher` as a [`Z`] instance. + /// + /// # Examples + /// ``` + /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RingLPR}; + /// use qfall_math::integer::Z; + /// let lpr = RingLPR::default(); + /// let (pk, sk) = lpr.gen(); + /// let cipher = lpr.enc(&pk, 212); + /// + /// let m = lpr.dec(&sk, &cipher); + /// + /// assert_eq!(Z::from(212), m); + /// ``` + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + // res = v - s * u + let result = &cipher.1 - sk * &cipher.0; + + decode_z_bitwise_from_polynomialringzq(self.q.get_q(), &result) + } +} + +#[cfg(test)] +mod test_pp_generation { + use super::RingLPR; + use super::Z; + + /// Checks whether `new` is available for types implementing [`Into`]. + #[test] + fn new_availability() { + let _ = RingLPR::new(2u8, 2u32, 2u64); + let _ = RingLPR::new(2u16, 2i32, 2i64); + let _ = RingLPR::new(2i16, 2u32, 2u8); + let _ = RingLPR::new(Z::from(2), 2u8, 2i8); + } + + /// Checks whether `new_from_n` works properly for different choices of n. + #[test] + fn suitable_security_params() { + let n_choices = [16, 32, 64, 128, 256, 512, 1024]; + + for n in n_choices { + let _ = RingLPR::new_from_n(n); + } + } + + /// Checks whether the [`Default`] parameter choice is suitable. + #[test] + fn default_suitable() { + let scheme = RingLPR::default(); + + assert!(scheme.check_correctness().is_ok()); + assert!(scheme.check_security().is_ok()); + } + + /// Checks whether the generated public parameters from `new_from_n` are + /// valid choices according to security and correctness of the scheme. + #[test] + fn choice_valid() { + let n_choices = [16, 32, 64, 128, 256, 512, 1024]; + + for n in n_choices { + let scheme = RingLPR::new_from_n(n); + + assert!(scheme.check_correctness().is_ok()); + assert!(scheme.check_security().is_ok()); + } + } + + /// Ensure that `n` chosen as a non-power of two does not result in a provably + /// correct scheme. + #[test] + fn non_power_of_2_n() { + let scheme = RingLPR::new(7, 17, 0.01); + + assert!(scheme.check_correctness().is_err()) + } + + /// Ensures that `new_from_n` is available for types implementing [`Into`]. + #[test] + #[allow(clippy::needless_borrows_for_generic_args)] + fn availability() { + let _ = RingLPR::new_from_n(16u8); + let _ = RingLPR::new_from_n(16u16); + let _ = RingLPR::new_from_n(16u32); + let _ = RingLPR::new_from_n(16u64); + let _ = RingLPR::new_from_n(16i8); + let _ = RingLPR::new_from_n(16i16); + let _ = RingLPR::new_from_n(16i32); + let _ = RingLPR::new_from_n(16i64); + let _ = RingLPR::new_from_n(Z::from(16)); + let _ = RingLPR::new_from_n(&Z::from(16)); + } + + /// Checks whether `new_from_n` returns an error for invalid input n. + #[test] + #[should_panic] + fn invalid_n() { + RingLPR::new_from_n(9); + } + + /// Checks whether `secure128` outputs a new instance with correct and secure + /// parameters. + #[test] + fn secure128_validity() { + let scheme = RingLPR::secure128(); + + assert!(scheme.check_correctness().is_ok()); + assert!(scheme.check_security().is_ok()); + } +} + +#[cfg(test)] +mod test_ring_lpr { + use super::RingLPR; + use crate::pk_encryption::PKEncryptionScheme; + use qfall_math::integer::Z; + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for several messages and small n. + #[test] + fn cycle_small_n() { + let scheme = RingLPR::default(); + let (pk, sk) = scheme.gen(); + let messages = [0, 1, 2, 15, 70, 256, 580, 1000, 4000, 8000, 65535]; + + for message in messages { + let cipher = scheme.enc(&pk, message); + let m = scheme.dec(&sk, &cipher); + + assert_eq!(Z::from(message), m); + } + } + + /// Checks whether the full-cycle of gen, enc, dec works properly + /// for several messages and larger n. + #[test] + fn cycle_large_n() { + let scheme = RingLPR::new_from_n(64); + let (pk, sk) = scheme.gen(); + let messages = [ + 0, + 1, + 2, + 15, + 70, + 256, + 580, + 1_000, + 4_000, + 8_000, + 20_000, + 80_000, + 240_000, + 4_000_000, + 100_000_000, + ]; + + for message in messages { + let cipher = scheme.enc(&pk, message); + let m = scheme.dec(&sk, &cipher); + + assert_eq!(Z::from(message), m); + } + } + + /// Checks that modulus 2^n is applied correctly. + #[test] + fn modulus_application() { + let messages = [65536]; + let scheme = RingLPR::default(); + let (pk, sk) = scheme.gen(); + + for msg in messages { + let cipher = scheme.enc(&pk, msg); + let m = scheme.dec(&sk, &cipher); + + assert_eq!(Z::ZERO, m); + } + } +} diff --git a/src/signature.rs b/src/signature.rs new file mode 100644 index 0000000..6dcbb3a --- /dev/null +++ b/src/signature.rs @@ -0,0 +1,49 @@ +// Copyright © 2023 Marcel Luca Schmidt, Marvin Beckmann +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This module provides the trait a struct should implement if it is an +//! instance of a signature scheme. Furthermore, it contains cryptographic signatures +//! implementing the [`SignatureScheme`] trait. +//! +//! - \[1\] Gentry, Craig, Chris Peikert, and Vinod Vaikuntanathan. +//! "Trapdoors for hard lattices and new cryptographic constructions." +//! Proceedings of the fortieth annual ACM symposium on Theory of computing. 2008. +//! + +pub mod fdh; +pub mod pfdh; + +/// This trait should be implemented by every signature scheme. +/// It captures the essential functionalities each signature scheme has to support. +/// +/// Note: The gen does not take in the parameter `1^n`, as this is a public parameter, +/// which shall be defined by the struct implementing this trait. +pub trait SignatureScheme { + /// The type of the secret key. + type SecretKey; + /// The type of the public key. + type PublicKey; + /// The type of the signature. + type Signature; + + /// Generates a public key and a secret key from the attributes the + /// struct has, which implements this trait. + /// + /// Returns the public key and the secret key. + fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey); + + /// Signs a message using the secret key (and potentially the public key). + /// + /// Returns the resulting signature. + fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature; + + /// Verifies that a signature is valid for a message by using the public key. + /// + /// Returns the result of the verification as a boolean. + fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool; +} diff --git a/src/signature/fdh.rs b/src/signature/fdh.rs new file mode 100644 index 0000000..c1612ff --- /dev/null +++ b/src/signature/fdh.rs @@ -0,0 +1,42 @@ +// Copyright © 2023 Marvin Beckmann +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This Module contains a implementations of the full domain hash signature scheme, +//! which only has to be instantiated with a corresponding PSF, a storage and +//! a corresponding hash function. +//! +//! The constructions follow the general definition of a hash-then-sign signature scheme +//! that uses a hash function as in [\[1\]]() and a PSF. +//! This signature scheme uses a storage, so it is stateful. +//! +//! Requirements +//! - `psf`: The PSF which has to implement the [`PSF`](crate::primitive::psf::PSF) trait +//! and must also be (de-)serializable. +//! - `storage`: A Hashmap that safes all previously signed messages and their signature +//! - `hash`: The hash-function which has to map a string into the correct domain +//! +//! # Example +//! ## Signature Scheme from [`PSFGPV`](crate::primitive::psf::PSFGPV) +//! ``` +//! use qfall_schemes::signature::{fdh::FDHGPV, SignatureScheme}; +//! +//! let mut fdh = FDHGPV::setup(4, 113, 17); +//! +//! let m = "Hello World!"; +//! +//! let (pk, sk) = fdh.gen(); +//! let sigma = fdh.sign(m.to_owned(), &sk, &pk); +//! +//! assert!(fdh.vfy(m.to_owned(), &sigma, &pk)); +//! ``` + +mod gpv; +mod gpv_ring; + +pub use gpv::FDHGPV; +pub use gpv_ring::FDHGPVRing; diff --git a/src/signature/fdh/gpv.rs b/src/signature/fdh/gpv.rs new file mode 100644 index 0000000..3fce3f7 --- /dev/null +++ b/src/signature/fdh/gpv.rs @@ -0,0 +1,202 @@ +// Copyright © 2023 Marvin Beckmann +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! A classical implementation of the FDH signature scheme using the [`PSFGPV`] +//! according to [\[1\]](<../index.html#:~:text=[1]>). + +use crate::{ + hash::{sha256::HashMatZq, HashInto}, + signature::SignatureScheme, +}; +use qfall_crypto::{ + primitive::psf::{PSF, PSFGPV}, + sample::g_trapdoor::gadget_parameters::GadgetParameters, +}; +use qfall_math::{ + integer::{MatZ, Z}, + integer_mod_q::{MatZq, Modulus}, + rational::{MatQ, Q}, +}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Initializes an FDH signature scheme from a [`PSFGPV`]. +/// +/// This function corresponds to an implementation of an FDH-signature +/// scheme with the explicit PSF [`PSFGPV`] which is generated using +/// the default of [`GadgetParameters`]. +/// +/// Attributes: +/// - `psf`: Defines the PSF needed for preimage sampling. +/// - `storage`: Stores all previously constructed signatures. +/// - `hash`: Defines the hash function going from Strings to the PSFs range. +/// +/// Returns an explicit implementation of a FDH-signature scheme. +/// +/// # Example +/// ``` +/// use qfall_schemes::signature::{fdh::FDHGPV, SignatureScheme}; +/// +/// let m = "Hello World!"; +/// +/// let mut fdh = FDHGPV::setup(4, 113, 17); +/// let (pk, sk) = fdh.gen(); +/// +/// let sigma = fdh.sign(m.to_string(), &sk, &pk); +/// +/// assert!(fdh.vfy(m.to_string(), &sigma, &pk)); +/// ``` +#[derive(Serialize, Deserialize)] +pub struct FDHGPV { + pub psf: PSFGPV, + pub storage: HashMap, + pub hash: HashMatZq, +} + +impl FDHGPV { + /// Initializes the [`FDHGPV`] with default parameters. + /// The setup function takes in the security parameter, the modulus, a length bound + /// for the signatures, and the length of randomness for this construction. + /// Then, the [`PSFGPV`] is instantiated with the default [`GadgetParameters`]. + /// This PSF with an additional storage and hash function are secured in the struct. + /// + /// Parameters: + /// - `n`: The security parameter + /// - `q`: The modulus used for the G-Trapdoors + /// - `s`: The Gaussian parameter with which is sampled + /// + /// Returns an explicit instantiation of a FDH signature scheme using the default + /// parameters. + /// + /// # Example + /// ``` + /// use qfall_schemes::signature::{fdh::FDHGPV, SignatureScheme}; + /// + /// let mut fdh = FDHGPV::setup(4, 113, 17); + /// ``` + /// + /// # Panics ... + /// - if the security parameter n is not in [1, i64::MAX]. + /// - if `q <= 1`. + pub fn setup(n: impl Into, q: impl Into, s: impl Into) -> Self { + let (n, q, s) = (n.into(), q.into(), s.into()); + let psf = PSFGPV { + gp: GadgetParameters::init_default(&n, &q), + s, + }; + Self { + psf, + storage: HashMap::new(), + hash: HashMatZq { + modulus: q, + rows: i64::try_from(n).unwrap(), + cols: 1, + }, + } + } +} + +impl SignatureScheme for FDHGPV { + /// The trapdoor and a precomputed short basis that speeds up preimage sampling. + type SecretKey = (MatZ, MatQ); + /// The public matrix defining the PSF, for which the secret key defines a trapdoor + type PublicKey = MatZq; + /// Defined by the domain of the PSF. + type Signature = MatZ; + + /// Generates a trapdoor by calling the `trap_gen` of the psf + fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { + self.psf.trap_gen() + } + /// Firstly checks if the message has been signed before, and if, return that + /// signature, else it continues. + /// It hashes the message into the domain and then computes a signature using + /// `samp_p` from the psf with the trapdoor. + fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature { + // check if it is in the HashMap + if let Some(sigma) = self.storage.get(&m) { + return sigma.clone(); + } + + let u = (self.hash).hash(&m); + let signature = self.psf.samp_p(pk, sk, &u); + + // insert signature in HashMap + self.storage.insert(m, signature.clone()); + signature + } + + /// Checks if a signature is firstly within D_n, and then checks if + /// the signature is actually a valid preimage under `fa` of `hash(m)`. + fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool { + if !self.psf.check_domain(sigma) { + return false; + } + + let u = (self.hash).hash(&m); + + self.psf.f_a(pk, sigma) == u + } +} + +#[cfg(test)] +mod test_fdh { + use crate::signature::{fdh::gpv::FDHGPV, SignatureScheme}; + use qfall_math::{integer::Z, rational::Q, traits::Pow}; + + /// Ensure that the generated signature is valid. + #[test] + fn ensure_valid_signature_is_generated() { + let n = Z::from(4); + let k = Z::from(6); + // `s >= ||\tilde short_base|| * omega(sqrt{log m})`, + // here `log(2*n*k) = omega(sqrt{log m}))` (Theorem 4.1 - GPV08) + let s: Q = ((&n * &k).sqrt() + 1) * Q::from(2) * (Z::from(2) * &n * &k).log(2).unwrap(); + let q = Z::from(2).pow(&k).unwrap(); + + let mut fdh = FDHGPV::setup(n, &q, &s); + let (pk, sk) = fdh.gen(); + + for i in 0..10 { + let m = format!("Hello World! {i}"); + + let sigma = fdh.sign(m.to_owned(), &sk, &pk); + + assert_eq!(&sigma, &fdh.sign(m.to_owned(), &sk, &pk)); + assert!(fdh.vfy(m.to_owned(), &sigma, &pk)) + } + } + + /// Ensure that an entry is actually added to the local storage. + #[test] + fn storage_filled() { + let mut fdh = FDHGPV::setup(5, 1024, 10); + + let m = "Hello World!"; + let (pk, sk) = fdh.gen(); + let _ = fdh.sign(m.to_owned(), &sk, &pk); + + assert!(fdh.storage.contains_key(m)) + } + + /// Ensure that after deserialization the HashMap still contains all entries. + #[test] + fn reload_hashmap() { + let mut fdh = FDHGPV::setup(5, 1024, 10); + + // fill one entry in the HashMap + let m = "Hello World!"; + let (pk, sk) = fdh.gen(); + let _ = fdh.sign(m.to_owned(), &sk, &pk); + + let fdh_string = serde_json::to_string(&fdh).expect("Unable to create a json object"); + let fdh_2: FDHGPV = serde_json::from_str(&fdh_string).unwrap(); + + assert_eq!(fdh.storage, fdh_2.storage); + } +} diff --git a/src/signature/fdh/gpv_ring.rs b/src/signature/fdh/gpv_ring.rs new file mode 100644 index 0000000..1e9572c --- /dev/null +++ b/src/signature/fdh/gpv_ring.rs @@ -0,0 +1,229 @@ +// Copyright © 2023 Marvin Beckmann +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! A ring implementation of the FDH signature scheme using the [`PSFGPVRing`] +//! according to [\[1\]](<../index.html#:~:text=[1]>). + +use crate::{ + hash::{sha256::HashMatPolynomialRingZq, HashInto}, + signature::SignatureScheme, +}; +use qfall_crypto::{ + primitive::psf::{PSFGPVRing, PSF}, + sample::g_trapdoor::gadget_parameters::GadgetParametersRing, +}; +use qfall_math::{ + integer::{MatPolyOverZ, Z}, + integer_mod_q::{MatPolynomialRingZq, Modulus}, + rational::Q, +}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Initializes an FDH signature scheme from a [`PSFGPVRing`]. +/// The trapdoor is sampled with a Gaussian parameter of 1.005 +/// as done in [\[3\]]() who derived it from +/// [\[5\]](). +/// +/// This function corresponds to an implementation of an FDH-signature +/// scheme with the explicit PSF [`PSFGPVRing`] which is generated using +/// the default of [`GadgetParametersRing`]. +/// +/// Attributes: +/// - `psf`: Defines the PSF needed for preimage sampling. +/// - `storage`: Stores all previously constructed signatures. +/// - `hash`: Defines the hash function going from Strings to the PSFs range. +/// +/// Returns an explicit implementation of an FDH-signature scheme. +/// +/// # Example +/// ``` +/// use qfall_schemes::signature::fdh::FDHGPVRing; +/// use crate::qfall_schemes::signature::SignatureScheme; +/// use qfall_math::rational::Q; +/// +/// const MODULUS: i64 = 512; +/// const N: i64 = 8; +/// fn compute_s() -> Q { +/// ((2 * 2 * Q::from(1.005_f64) * Q::from(N).sqrt() + 1) * 2) * 4 +/// } +/// +/// let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); +/// let (pk, sk) = fdh.gen(); +/// let m = &format!("Hello World!"); +/// let sigma = fdh.sign(m.to_owned(), &sk, &pk); +/// assert!( +/// fdh.vfy(m.to_owned(), &sigma, &pk), +/// ); +/// ``` +#[derive(Serialize, Deserialize)] +pub struct FDHGPVRing { + pub psf: PSFGPVRing, + pub storage: HashMap, + pub hash: HashMatPolynomialRingZq, +} + +impl FDHGPVRing { + /// Initializes the [`FDHGPVRing`] with default parameters. + /// The setup function takes in the security parameter, the modulus, a length bound + /// for the signatures, and the length of randomness for this construction. + /// Then, the [`PSFGPVRing`] is instantiated with the default [`GadgetParametersRing`]. + /// This PSF with an additional storage and hash function are secured in the struct. + /// + /// Parameters: + /// - `n`: The security parameter + /// - `q`: The modulus used for the G-Trapdoors + /// - `s`: The Gaussian parameter with which is sampled + /// + /// Returns an explicit instantiation of a FDH signature scheme using the default + /// parameters. + /// + /// # Example + /// ``` + /// use qfall_schemes::signature::fdh::FDHGPVRing; + /// use crate::qfall_schemes::signature::SignatureScheme; + /// use qfall_math::rational::Q; + /// + /// const MODULUS: i64 = 512; + /// const N: i64 = 8; + /// fn compute_s() -> Q { + /// ((2 * 2 * Q::from(1.005_f64) * Q::from(N).sqrt() + 1) * 2) * 4 + /// } + /// + /// let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); + /// ``` + /// + /// # Panics ... + /// - if the security parameter n is not in [1, i64::MAX]. + /// - if `q <= 1`. + pub fn setup(n: impl Into, q: impl Into, s: impl Into) -> Self { + let (n, q, s) = (n.into(), q.into(), s.into()); + let psf = PSFGPVRing { + gp: GadgetParametersRing::init_default(&n, &q), + s, + s_td: Q::from(1.005_f64), + }; + let modulus = psf.gp.modulus.clone(); + Self { + psf, + storage: HashMap::new(), + hash: HashMatPolynomialRingZq { + modulus, + rows: 1, + cols: 1, + }, + } + } +} + +impl SignatureScheme for FDHGPVRing { + /// The trapdoor and a precomputed short basis that speeds up preimage sampling. + type SecretKey = (MatPolyOverZ, MatPolyOverZ); + /// The public matrix defining the PSF, for which the secret key defines a trapdoor + type PublicKey = MatPolynomialRingZq; + /// Defined by the domain of the PSF. + type Signature = MatPolyOverZ; + + /// Generates a trapdoor by calling the `trap_gen` of the psf + fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { + self.psf.trap_gen() + } + + /// Firstly checks if the message has been signed before, and if, return that + /// signature, else it continues. + /// It hashes the message into the domain and then computes a signature using + /// `samp_p` from the psf with the trapdoor. + fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature { + // check if it is in the HashMap + if let Some(sigma) = self.storage.get(&m) { + return sigma.clone(); + } + + let u = (self.hash).hash(&m); + let signature = self.psf.samp_p(pk, sk, &u); + + // insert signature in HashMap + self.storage.insert(m, signature.clone()); + signature + } + + /// Checks if a signature is firstly within D_n, and then checks if + /// the signature is actually a valid preimage under `fa` of `hash(m)`. + fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool { + if !self.psf.check_domain(sigma) { + return false; + } + + let u = (self.hash).hash(&m); + + self.psf.f_a(pk, sigma) == u + } +} + +#[cfg(test)] +mod test_fdh { + use crate::signature::{fdh::gpv_ring::FDHGPVRing, SignatureScheme}; + use qfall_math::rational::Q; + + const MODULUS: i64 = 512; + const N: i64 = 8; + fn compute_s() -> Q { + ((2 * 2 * Q::from(1.005_f64) * Q::from(N).sqrt() + 1) * 2) * 4 + } + + /// Ensure that the generated signature is valid. + #[test] + fn ensure_valid_signature_is_generated() { + let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); + let (pk, sk) = fdh.gen(); + + for i in 0..10 { + let m = &format!("Hello World! {i}"); + + let sigma = fdh.sign(m.to_owned(), &sk, &pk); + + assert!( + fdh.vfy(m.to_owned(), &sigma, &pk), + "This is a probabilistic test and may fail with negligible probability. \ + As n is rather small here, try to rerun the test and check whether the \ + test fails again." + ) + } + } + + /// Ensure that an entry is actually added to the local storage. + #[test] + fn storage_filled() { + let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); + + let m = "Hello World!"; + let (pk, sk) = fdh.gen(); + let sign_1 = fdh.sign(m.to_owned(), &sk, &pk); + let sign_2 = fdh.sign(m.to_owned(), &sk, &pk); + + assert!(fdh.storage.contains_key(m)); + assert_eq!(sign_1, sign_2); + } + + /// Ensure that after deserialization the HashMap still contains all entries. + #[test] + fn reload_hashmap() { + let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); + + // fill one entry in the HashMap + let m = "Hello World!"; + let (pk, sk) = fdh.gen(); + let _ = fdh.sign(m.to_owned(), &sk, &pk); + + let fdh_string = serde_json::to_string(&fdh).expect("Unable to create a json object"); + + let fdh_2: FDHGPVRing = serde_json::from_str(&fdh_string).unwrap(); + + assert_eq!(fdh.storage, fdh_2.storage); + } +} diff --git a/src/signature/pfdh.rs b/src/signature/pfdh.rs new file mode 100644 index 0000000..c09b4d1 --- /dev/null +++ b/src/signature/pfdh.rs @@ -0,0 +1,42 @@ +// Copyright © 2023 Phil Milewski +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! This Module contains a general implementation of the probabilistic full domain +//! hash signature scheme. +//! +//! The constructions follow the general definition of a hash-then-sign signature scheme +//! that uses a hash function as in [\[1\]]() and a PSF. +//! +//! These signature schemes also include randomness into the hashed strings rather than +//! using a storage, so it is stateless. +//! +//! Requirements +//! - `psf`: The PSF which has to implement the [`PSF`](crate::primitive::psf::PSF) trait +//! and must also be (de-)serializable. +//! - `hash`: The hash-function which has to map a string into the correct domain. +//! - `randomness_length`: The length of the salt that is added to the string before +//! hashing. +//! +//! # Example +//! ## Signature Scheme from [`PSFGPV`](crate::primitive::psf::PSFGPV) +//! ``` +//! use qfall_schemes::signature::{pfdh::PFDHGPV, SignatureScheme}; +//! +//! let mut pfdh = PFDHGPV::setup(4, 113, 17, 128); +//! +//! let m = "Hello World!"; +//! +//! let (pk, sk) = pfdh.gen(); +//! let sigma = pfdh.sign(m.to_owned(), &sk, &pk); +//! +//! assert!(pfdh.vfy(m.to_owned(), &sigma, &pk)); +//! ``` + +mod gpv; + +pub use gpv::PFDHGPV; diff --git a/src/signature/pfdh/gpv.rs b/src/signature/pfdh/gpv.rs new file mode 100644 index 0000000..b8bc67b --- /dev/null +++ b/src/signature/pfdh/gpv.rs @@ -0,0 +1,173 @@ +// Copyright © 2023 Phil Milewski +// +// This file is part of qFALL-crypto. +// +// qFALL-crypto is free software: you can redistribute it and/or modify it under +// the terms of the Mozilla Public License Version 2.0 as published by the +// Mozilla Foundation. See . + +//! A classical implementation of the PFDH signature scheme using the [`PSFGPV`] +//! according to [\[1\]](<../index.html#:~:text=[1]>). + +use crate::{ + hash::{sha256::HashMatZq, HashInto}, + signature::SignatureScheme, +}; +use qfall_crypto::{ + primitive::psf::{PSF, PSFGPV}, + sample::g_trapdoor::gadget_parameters::GadgetParameters, +}; +use qfall_math::{ + integer::{MatZ, Z}, + integer_mod_q::{MatZq, Modulus}, + rational::{MatQ, Q}, + traits::Pow, +}; + +/// Initializes an PFDH signature scheme from a [`PSFGPV`]. +/// +/// This function corresponds to an implementation of an PFDH-signature +/// scheme with the explicit PSF [`PSFGPV`] which is generated using +/// the default of [`GadgetParameters`]. +/// +/// Attributes: +/// - `psf`: Defines the PSF needed for preimage sampling. +/// - `hash`: Defines the hash function going from Strings to the PSFs range. +/// - `randomness_length`: Defines the number of bits used for the randomness. +/// +/// Returns an explicit implementation of a PFDH-signature scheme. +/// +/// # Example +/// ``` +/// use qfall_schemes::signature::{pfdh::PFDHGPV, SignatureScheme}; +/// +/// let mut pfdh = PFDHGPV::setup(4, 113, 17, 128); +/// +/// let m = "Hello World!"; +/// +/// let (pk, sk) = pfdh.gen(); +/// let sigma = pfdh.sign(m.to_owned(), &sk, &pk); +/// +/// assert!(pfdh.vfy(m.to_owned(), &sigma, &pk)); +/// ``` +pub struct PFDHGPV { + pub psf: PSFGPV, + pub hash: HashMatZq, + pub randomness_length: Z, +} + +impl PFDHGPV { + /// Initializes the [`PFDHGPV`] with default parameters. + /// The setup function takes in the security parameter, the modulus, a length bound + /// for the signatures, and the length of randomness for this construction. + /// Then, the [`PSFGPV`] is instantiated with the default [`GadgetParameters`]. + /// This PSF with an additional storage and hash function are secured in the struct. + /// + /// Parameters: + /// - `n`: The security parameter + /// - `q`: The modulus used for the G-Trapdoors + /// - `s`: The Gaussian parameter with which is sampled + /// - `randomness_length`: The length of the randomness. + /// + /// Returns an explicit instantiation of a PFDH signature scheme using the default + /// parameters. + /// + /// # Example + /// ``` + /// use qfall_schemes::signature::{pfdh::PFDHGPV, SignatureScheme}; + /// + /// let mut pfdh = PFDHGPV::setup(4, 113, 17, 128); + /// ``` + /// + /// # Panics ... + /// - if the security parameter n is not in [1, i64::MAX]. + /// - if `q <= 1`. + pub fn setup( + n: impl Into, + q: impl Into, + s: impl Into, + randomness_length: impl Into, + ) -> Self { + let (n, q, s, randomness_length) = (n.into(), q.into(), s.into(), randomness_length.into()); + let psf = PSFGPV { + gp: GadgetParameters::init_default(&n, &q), + s, + }; + let n = i64::try_from(&n).unwrap(); + Self { + psf, + hash: HashMatZq { + modulus: q, + rows: n, + cols: 1, + }, + randomness_length, + } + } +} + +impl SignatureScheme for PFDHGPV { + /// The trapdoor and a precomputed short basis that speeds up preimage sampling. + type SecretKey = (MatZ, MatQ); + /// The public matrix defining the PSF, for which the secret key defines a trapdoor + type PublicKey = MatZq; + /// Defined by the domain of the PSF and additional randomness. + type Signature = (MatZ, Z); + + /// Generates a trapdoor by calling the `trap_gen` of the psf + fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { + self.psf.trap_gen() + } + + /// Firstly generate randomness + /// It hashes the message and randomness into the domain and then computes a signature using + /// `samp_p` from the psf with the trapdoor. + fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature { + let randomness = + Z::sample_uniform(0, Z::from(2).pow(&self.randomness_length).unwrap()).unwrap(); + let u = (self.hash).hash(&format!("{m} {randomness} {}", &self.randomness_length)); + let signature_part1 = self.psf.samp_p(pk, sk, &u); + + (signature_part1, randomness) + } + + /// Checks if a signature is firstly within D_n, and then checks if + /// the signature is actually a valid preimage under `fa` of `hash(m||r)`. + fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool { + if !self.psf.check_domain(&sigma.0) { + return false; + } + + let u = (self.hash).hash(&format!("{m} {} {}", sigma.1, &self.randomness_length)); + + self.psf.f_a(pk, &sigma.0) == u + } +} + +#[cfg(test)] +mod test_pfdh { + use crate::signature::{pfdh::gpv::PFDHGPV, SignatureScheme}; + use qfall_math::{integer::Z, rational::Q, traits::Pow}; + + /// Ensure that the generated signature is valid. + #[test] + fn ensure_valid_signature_is_generated() { + let n = Z::from(4); + let k = Z::from(6); + // `s >= ||\tilde short_base|| * omega(sqrt{log m})`, + // here `log(2*n*k) = omega(sqrt{log m}))` (Theorem 4.1 - GPV08) + let s: Q = ((&n * &k).sqrt() + 1) * Q::from(2) * (Z::from(2) * &n * &k).log(2).unwrap(); + let q = Z::from(2).pow(&k).unwrap(); + + let mut pfdh = PFDHGPV::setup(n, &q, &s, 128); + let (pk, sk) = pfdh.gen(); + + for i in 0..10 { + let m = format!("Hello World! {i}"); + + let sigma = pfdh.sign(m.to_owned(), &sk, &pk); + + assert!(pfdh.vfy(m.to_owned(), &sigma, &pk)) + } + } +} From 6bf63251dc3de41c78f25386b1c5a22f3dea27e1 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 12:44:57 +0200 Subject: [PATCH 02/30] fix naming of repo --- README.md | 4 ++-- benches/benchmarks.rs | 2 +- benches/k_pke.rs | 2 +- benches/pfdh.rs | 2 +- benches/regev.rs | 2 +- src/hash.rs | 2 +- src/hash/sha256.rs | 2 +- src/hash/sis.rs | 2 +- src/identity_based_encryption.rs | 2 +- src/identity_based_encryption/dual_regev_ibe.rs | 2 +- src/lib.rs | 2 +- src/pk_encryption.rs | 2 +- src/pk_encryption/ccs_from_ibe.rs | 2 +- src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs | 2 +- src/pk_encryption/dual_regev.rs | 2 +- src/pk_encryption/dual_regev_discrete_gauss.rs | 2 +- src/pk_encryption/k_pke.rs | 2 +- src/pk_encryption/lpr.rs | 2 +- src/pk_encryption/regev.rs | 2 +- src/pk_encryption/regev_discrete_gauss.rs | 2 +- src/pk_encryption/ring_lpr.rs | 2 +- src/signature.rs | 2 +- src/signature/fdh.rs | 2 +- src/signature/fdh/gpv.rs | 2 +- src/signature/fdh/gpv_ring.rs | 2 +- src/signature/pfdh.rs | 2 +- src/signature/pfdh/gpv.rs | 2 +- 27 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 4a0095e..7ce2700 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ It provides a step-by-step guide to install the required libraries and gives fur ## What does qFALL-schemes offer? -qFALL-crypto offers a variety of implementations of cryptographic schemes, constructions, and primitives. +qFALL-schemes offers a variety of implementations of cryptographic schemes, constructions, and primitives. We provide a brief overview in the following list. For a more detailed description, please refer to [our tutorial section](https://qfall.github.io/book/crypto/features.html). @@ -55,7 +55,7 @@ Permissions of this weak copyleft license are conditioned on making available so Please use the following bibtex entry to cite [qFALL-schemes](https://github.com/qfall/schemes): ```text -@misc{qFALL-crypto, +@misc{qFALL-schemes, author = {Porzenheim, Laurens and Beckmann, Marvin and Kramer, Paul and Milewski, Phil and Moog, Sven and Schmidt, Marcel and Siemer, Niklas}, title = {qFALL-crypto v0.0}, howpublished = {Online: \url{https://github.com/qfall/crypto}}, diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index c20d019..ba6298d 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Sven Moog // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/benches/k_pke.rs b/benches/k_pke.rs index 88ec1e1..521e8ba 100644 --- a/benches/k_pke.rs +++ b/benches/k_pke.rs @@ -1,6 +1,6 @@ // Copyright © 2025 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/benches/pfdh.rs b/benches/pfdh.rs index fc19126..78424f6 100644 --- a/benches/pfdh.rs +++ b/benches/pfdh.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/benches/regev.rs b/benches/regev.rs index deb5ff3..3392c6b 100644 --- a/benches/regev.rs +++ b/benches/regev.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Sven Moog // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/hash.rs b/src/hash.rs index ec3bfea..3612dea 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/hash/sha256.rs b/src/hash/sha256.rs index fbfcd77..82b855a 100644 --- a/src/hash/sha256.rs +++ b/src/hash/sha256.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Phil Milewski // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/hash/sis.rs b/src/hash/sis.rs index 7276162..e2f46c4 100644 --- a/src/hash/sis.rs +++ b/src/hash/sis.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/identity_based_encryption.rs b/src/identity_based_encryption.rs index 10cf514..7d547ce 100644 --- a/src/identity_based_encryption.rs +++ b/src/identity_based_encryption.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Phil Milewski // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index 2ca2814..0ff1fd8 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Phil Milewski // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/lib.rs b/src/lib.rs index e5c6264..acf14b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer, Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption.rs b/src/pk_encryption.rs index acb84bc..2cb8f4e 100644 --- a/src/pk_encryption.rs +++ b/src/pk_encryption.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/ccs_from_ibe.rs b/src/pk_encryption/ccs_from_ibe.rs index 00d201d..ac5fd15 100644 --- a/src/pk_encryption/ccs_from_ibe.rs +++ b/src/pk_encryption/ccs_from_ibe.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs index c607b34..d517915 100644 --- a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs +++ b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index 9b53d3c..4435528 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index 0ce5f67..d456825 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index 586b908..e1bd9ec 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -1,6 +1,6 @@ // Copyright © 2025 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index 178dfeb..d42fb66 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 1efdd5c..606a003 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index 505f616..e646f55 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index 3ac8f5f..bed5740 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/signature.rs b/src/signature.rs index 6dcbb3a..0df9b20 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Marcel Luca Schmidt, Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/signature/fdh.rs b/src/signature/fdh.rs index c1612ff..3fcdf94 100644 --- a/src/signature/fdh.rs +++ b/src/signature/fdh.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/signature/fdh/gpv.rs b/src/signature/fdh/gpv.rs index 3fce3f7..cc0883e 100644 --- a/src/signature/fdh/gpv.rs +++ b/src/signature/fdh/gpv.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/signature/fdh/gpv_ring.rs b/src/signature/fdh/gpv_ring.rs index 1e9572c..0455646 100644 --- a/src/signature/fdh/gpv_ring.rs +++ b/src/signature/fdh/gpv_ring.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/signature/pfdh.rs b/src/signature/pfdh.rs index c09b4d1..866cd36 100644 --- a/src/signature/pfdh.rs +++ b/src/signature/pfdh.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Phil Milewski // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the diff --git a/src/signature/pfdh/gpv.rs b/src/signature/pfdh/gpv.rs index b8bc67b..b13cfb5 100644 --- a/src/signature/pfdh/gpv.rs +++ b/src/signature/pfdh/gpv.rs @@ -1,6 +1,6 @@ // Copyright © 2023 Phil Milewski // -// This file is part of qFALL-crypto. +// This file is part of qFALL-schemes. // // qFALL-crypto is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the From 4f31b35aca01d1775cfda7016bd641baced79549 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 12:51:02 +0200 Subject: [PATCH 03/30] fix cargo doc some naming changes --- benches/README.md | 2 +- benches/benchmarks.rs | 2 +- benches/k_pke.rs | 2 +- benches/pfdh.rs | 2 +- benches/regev.rs | 2 +- src/hash.rs | 2 +- src/hash/sha256.rs | 2 +- src/hash/sis.rs | 2 +- src/identity_based_encryption.rs | 2 +- .../dual_regev_ibe.rs | 2 +- src/lib.rs | 24 +++++++++---------- src/pk_encryption.rs | 2 +- src/pk_encryption/ccs_from_ibe.rs | 2 +- .../ccs_from_ibe/dual_regev_ibe_pfdh.rs | 2 +- src/pk_encryption/dual_regev.rs | 2 +- .../dual_regev_discrete_gauss.rs | 2 +- src/pk_encryption/k_pke.rs | 2 +- src/pk_encryption/lpr.rs | 2 +- src/pk_encryption/regev.rs | 2 +- src/pk_encryption/regev_discrete_gauss.rs | 2 +- src/pk_encryption/ring_lpr.rs | 2 +- src/signature.rs | 2 +- src/signature/fdh.rs | 6 ++--- src/signature/fdh/gpv.rs | 2 +- src/signature/fdh/gpv_ring.rs | 6 ++--- src/signature/pfdh.rs | 6 ++--- src/signature/pfdh/gpv.rs | 2 +- 27 files changed, 43 insertions(+), 45 deletions(-) diff --git a/benches/README.md b/benches/README.md index 3d692bc..90523c7 100644 --- a/benches/README.md +++ b/benches/README.md @@ -3,7 +3,7 @@ Copyright © 2023 Sven Moog This file is part of qFALL-schemes. -qFALL-crypto is free software: you can redistribute it and/or modify it under +qfall-schemes is free software: you can redistribute it and/or modify it under the terms of the Mozilla Public License Version 2.0 as published by the Mozilla Foundation. See . --> diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index ba6298d..d853ba0 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . //! This file collects the benchmarks from other files. diff --git a/benches/k_pke.rs b/benches/k_pke.rs index 521e8ba..55c895e 100644 --- a/benches/k_pke.rs +++ b/benches/k_pke.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/benches/pfdh.rs b/benches/pfdh.rs index 78424f6..5dcc793 100644 --- a/benches/pfdh.rs +++ b/benches/pfdh.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/benches/regev.rs b/benches/regev.rs index 3392c6b..10635a4 100644 --- a/benches/regev.rs +++ b/benches/regev.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/hash.rs b/src/hash.rs index 3612dea..cfe7d6d 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/hash/sha256.rs b/src/hash/sha256.rs index 82b855a..587f8e7 100644 --- a/src/hash/sha256.rs +++ b/src/hash/sha256.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/hash/sis.rs b/src/hash/sis.rs index e2f46c4..616e570 100644 --- a/src/hash/sis.rs +++ b/src/hash/sis.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/identity_based_encryption.rs b/src/identity_based_encryption.rs index 7d547ce..5606c3e 100644 --- a/src/identity_based_encryption.rs +++ b/src/identity_based_encryption.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index 0ff1fd8..3989152 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/lib.rs b/src/lib.rs index acf14b7..ae69b1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,35 +2,33 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! # What is qFALL-crypto? -//! qFall-crypto provides cryptographic basics such as mathematical primitives, +//! # What is qFALL-schemes? +//! qFall-schemes provides cryptographic basics such as mathematical primitives, //! fundamental lattice-based cryptographic constructions, and samplable distributions/ //! possibilities to sample instances of lattice problems to prototype //! lattice-based cryptographic constructions and more. //! -//! Currently qFALL-crypto supports 3 main construction types: -//! - [Identity-Based Encryptions](construction::identity_based_encryption::IBEScheme) -//! - [Public-Key Encryptions](construction::pk_encryption::PKEncryptionScheme) -//! - [Signatures](construction::signature::SignatureScheme) +//! Currently qFALL-schemes supports 3 main construction types: +//! - [Identity-Based Encryptions](identity_based_encryption::IBEScheme) +//! - [Public-Key Encryptions](pk_encryption::PKEncryptionScheme) +//! - [Signatures](signature::SignatureScheme) //! //! These are identified by traits and then implemented for specific constructions, e.g. -//! [`RingLPR`](construction::pk_encryption::RingLPR). -//! Our library has further primitives useful for prototyping such as -//! [`PSFs`](primitive::psf::PSF) that can be used to implement constructions. +//! [`RingLPR`](pk_encryption::RingLPR). //! -//! qFALL-crypto is free software: you can redistribute it and/or modify it under +//! qfall-schemes is free software: you can redistribute it and/or modify it under //! the terms of the Mozilla Public License Version 2.0 as published by the //! Mozilla Foundation. See . //! //! ## Tutorial + Website -//! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-crypto on our [website](https://qfall.github.io/). +//! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-schemes on our [website](https://qfall.github.io/). //! The tutorial explains the basic steps starting from installation and //! continues with basic usage. -//! qFALL-crypto is co-developed together with qFALL-math which provides the basic +//! qfall-schemes is co-developed together with qFALL-math and qfall-crypto which provides the basic //! foundation that is used to implement the cryptographic constructions. //! //! This module contains fundamental cryptographic constructions, on which other diff --git a/src/pk_encryption.rs b/src/pk_encryption.rs index 2cb8f4e..bd12830 100644 --- a/src/pk_encryption.rs +++ b/src/pk_encryption.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/ccs_from_ibe.rs b/src/pk_encryption/ccs_from_ibe.rs index ac5fd15..50cc089 100644 --- a/src/pk_encryption/ccs_from_ibe.rs +++ b/src/pk_encryption/ccs_from_ibe.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs index d517915..b92e38d 100644 --- a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs +++ b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index 4435528..362793c 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index d456825..fcadb54 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index e1bd9ec..daf3793 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index d42fb66..d1236c8 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 606a003..607c066 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index e646f55..b074f9b 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index bed5740..cda2a8f 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/signature.rs b/src/signature.rs index 0df9b20..9992f85 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/signature/fdh.rs b/src/signature/fdh.rs index 3fcdf94..a525302 100644 --- a/src/signature/fdh.rs +++ b/src/signature/fdh.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -15,13 +15,13 @@ //! This signature scheme uses a storage, so it is stateful. //! //! Requirements -//! - `psf`: The PSF which has to implement the [`PSF`](crate::primitive::psf::PSF) trait +//! - `psf`: The PSF which has to implement the [`PSF`](qfall_crypto::primitive::psf::PSF) trait //! and must also be (de-)serializable. //! - `storage`: A Hashmap that safes all previously signed messages and their signature //! - `hash`: The hash-function which has to map a string into the correct domain //! //! # Example -//! ## Signature Scheme from [`PSFGPV`](crate::primitive::psf::PSFGPV) +//! ## Signature Scheme from [`PSFGPV`](qfall_crypto::primitive::psf::PSFGPV) //! ``` //! use qfall_schemes::signature::{fdh::FDHGPV, SignatureScheme}; //! diff --git a/src/signature/fdh/gpv.rs b/src/signature/fdh/gpv.rs index cc0883e..6c477a4 100644 --- a/src/signature/fdh/gpv.rs +++ b/src/signature/fdh/gpv.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/signature/fdh/gpv_ring.rs b/src/signature/fdh/gpv_ring.rs index 0455646..88fc329 100644 --- a/src/signature/fdh/gpv_ring.rs +++ b/src/signature/fdh/gpv_ring.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -27,8 +27,8 @@ use std::collections::HashMap; /// Initializes an FDH signature scheme from a [`PSFGPVRing`]. /// The trapdoor is sampled with a Gaussian parameter of 1.005 -/// as done in [\[3\]]() who derived it from -/// [\[5\]](). +/// as done in [\[3\]]() who derived it from +/// [\[5\]](). /// /// This function corresponds to an implementation of an FDH-signature /// scheme with the explicit PSF [`PSFGPVRing`] which is generated using diff --git a/src/signature/pfdh.rs b/src/signature/pfdh.rs index 866cd36..bcb781c 100644 --- a/src/signature/pfdh.rs +++ b/src/signature/pfdh.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -16,14 +16,14 @@ //! using a storage, so it is stateless. //! //! Requirements -//! - `psf`: The PSF which has to implement the [`PSF`](crate::primitive::psf::PSF) trait +//! - `psf`: The PSF which has to implement the [`PSF`](qfall_crypto::primitive::psf::PSF) trait //! and must also be (de-)serializable. //! - `hash`: The hash-function which has to map a string into the correct domain. //! - `randomness_length`: The length of the salt that is added to the string before //! hashing. //! //! # Example -//! ## Signature Scheme from [`PSFGPV`](crate::primitive::psf::PSFGPV) +//! ## Signature Scheme from [`PSFGPV`](qfall_crypto::primitive::psf::PSFGPV) //! ``` //! use qfall_schemes::signature::{pfdh::PFDHGPV, SignatureScheme}; //! diff --git a/src/signature/pfdh/gpv.rs b/src/signature/pfdh/gpv.rs index b13cfb5..c65e187 100644 --- a/src/signature/pfdh/gpv.rs +++ b/src/signature/pfdh/gpv.rs @@ -2,7 +2,7 @@ // // This file is part of qFALL-schemes. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qfall-schemes is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . From b34bc11c3184154011922a67c14b1fe7f00df596 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 13:01:51 +0200 Subject: [PATCH 04/30] fix two links in the signature documentation --- src/signature/fdh.rs | 2 +- src/signature/pfdh.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/signature/fdh.rs b/src/signature/fdh.rs index a525302..ebbd88b 100644 --- a/src/signature/fdh.rs +++ b/src/signature/fdh.rs @@ -11,7 +11,7 @@ //! a corresponding hash function. //! //! The constructions follow the general definition of a hash-then-sign signature scheme -//! that uses a hash function as in [\[1\]]() and a PSF. +//! that uses a hash function as in [\[1\]](<../index.html#:~:text=[1]>) and a PSF. //! This signature scheme uses a storage, so it is stateful. //! //! Requirements diff --git a/src/signature/pfdh.rs b/src/signature/pfdh.rs index bcb781c..f81d3ad 100644 --- a/src/signature/pfdh.rs +++ b/src/signature/pfdh.rs @@ -10,7 +10,7 @@ //! hash signature scheme. //! //! The constructions follow the general definition of a hash-then-sign signature scheme -//! that uses a hash function as in [\[1\]]() and a PSF. +//! that uses a hash function as in [\[1\]](<../index.html#:~:text=[1]>) and a PSF. //! //! These signature schemes also include randomness into the hashed strings rather than //! using a storage, so it is stateless. From 912ed2f05ee77dd54936e4845b1489d8b1a32e6d Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 13:28:45 +0200 Subject: [PATCH 05/30] rename qfall-crypto to qfall-tools as a subsequent change to the tools library --- .github/ISSUE_TEMPLATE/bug_report.md | 25 +++++++++++-------- Cargo.toml | 2 +- README.md | 2 +- src/hash/sha256.rs | 6 ++--- .../dual_regev_ibe.rs | 2 +- src/pk_encryption/k_pke.rs | 10 ++++---- src/pk_encryption/ring_lpr.rs | 12 ++++----- src/signature/fdh.rs | 4 +-- src/signature/fdh/gpv.rs | 2 +- src/signature/fdh/gpv_ring.rs | 6 ++--- src/signature/pfdh.rs | 4 +-- src/signature/pfdh/gpv.rs | 2 +- 12 files changed, 41 insertions(+), 36 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index fd1cc88..577ddf1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,46 +1,51 @@ --- name: Bug report about: Create a report to help us improve -title: '' +title: "" labels: bug -assignees: '' - +assignees: "" --- **Describe the bug** + **To Reproduce** + + ```rust // write your code here ``` **Expected behavior** - + **Screenshots** - + **Desktop (please complete the following information):** - - OS: - - Version of qFALL-crypto: + +- OS: +- Version of qFALL-schemes: **Additional context** + **Solution** + diff --git a/Cargo.toml b/Cargo.toml index 7db6c95..591ed7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-crypto = { git = "https://github.com/qfall/crypto", branch="move-schemes" } +qfall-tools = { git = "https://github.com/qfall/crypto", branch="move-schemes" } qfall-math = { git = "https://github.com/qfall/math", rev="5f50c9cd31c869462d959774fb4b51fcd1727dbe" } sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} diff --git a/README.md b/README.md index 7ce2700..4e0073e 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Please use the following bibtex entry to cite [qFALL-schemes](https://github.com ```text @misc{qFALL-schemes, author = {Porzenheim, Laurens and Beckmann, Marvin and Kramer, Paul and Milewski, Phil and Moog, Sven and Schmidt, Marcel and Siemer, Niklas}, - title = {qFALL-crypto v0.0}, + title = {qFALL-schemes v0.0}, howpublished = {Online: \url{https://github.com/qfall/crypto}}, month = Mar, year = 2023, diff --git a/src/hash/sha256.rs b/src/hash/sha256.rs index 587f8e7..4b64249 100644 --- a/src/hash/sha256.rs +++ b/src/hash/sha256.rs @@ -207,7 +207,7 @@ impl HashInto for HashMatZq { /// # Examples /// ``` /// use qfall_schemes::hash::{HashInto, sha256::HashMatPolynomialRingZq}; -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// /// let gp = GadgetParametersRing::init_default(10, 99); /// @@ -237,7 +237,7 @@ impl HashInto for HashMatPolynomialRingZq { /// # Examples /// ``` /// use qfall_schemes::hash::{HashInto, sha256::{HashMatPolynomialRingZq}}; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// /// let gp = GadgetParametersRing::init_default(10, 99); /// @@ -347,7 +347,7 @@ mod tests_sha { #[cfg(test)] mod hash_into_mat_polynomial_ring_zq { use super::{HashInto, HashMatPolynomialRingZq}; - use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; use qfall_math::{integer::PolyOverZ, traits::*}; /// Ensure that the hash function maps into the correct dimension and it is also diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index 3989152..cde90a3 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -15,7 +15,7 @@ use crate::{ hash::sha256::hash_to_mat_zq_sha256, pk_encryption::{DualRegev, PKEncryptionScheme}, }; -use qfall_crypto::{ +use qfall_tools::{ primitive::psf::{PSF, PSFGPV}, sample::g_trapdoor::gadget_parameters::GadgetParameters, }; diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index daf3793..3b99d03 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -13,16 +13,16 @@ //! ML-KEM and mostly supposed to showcase the prototyping capabilities of the `qfall`-library. use crate::pk_encryption::PKEncryptionScheme; -use qfall_crypto::utils::{ +use qfall_math::{ + integer::Z, + integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq}, +}; +use qfall_tools::utils::{ common_encodings::{ decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, }, common_moduli::new_anticyclic, }; -use qfall_math::{ - integer::Z, - integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq}, -}; use serde::{Deserialize, Serialize}; /// This is a naive toy-implementation of the [`PKEncryptionScheme`] used diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index cda2a8f..2a85d79 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -10,12 +10,6 @@ //! public key Ring-LPR encryption scheme. use super::PKEncryptionScheme; -use qfall_crypto::utils::{ - common_encodings::{ - decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, - }, - common_moduli::new_anticyclic, -}; use qfall_math::{ error::MathError, integer::Z, @@ -23,6 +17,12 @@ use qfall_math::{ rational::Q, traits::Pow, }; +use qfall_tools::utils::{ + common_encodings::{ + decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, + }, + common_moduli::new_anticyclic, +}; use serde::{Deserialize, Serialize}; /// This struct manages and stores the public parameters of a [`RingLPR`] diff --git a/src/signature/fdh.rs b/src/signature/fdh.rs index ebbd88b..59fc222 100644 --- a/src/signature/fdh.rs +++ b/src/signature/fdh.rs @@ -15,13 +15,13 @@ //! This signature scheme uses a storage, so it is stateful. //! //! Requirements -//! - `psf`: The PSF which has to implement the [`PSF`](qfall_crypto::primitive::psf::PSF) trait +//! - `psf`: The PSF which has to implement the [`PSF`](qfall_tools::primitive::psf::PSF) trait //! and must also be (de-)serializable. //! - `storage`: A Hashmap that safes all previously signed messages and their signature //! - `hash`: The hash-function which has to map a string into the correct domain //! //! # Example -//! ## Signature Scheme from [`PSFGPV`](qfall_crypto::primitive::psf::PSFGPV) +//! ## Signature Scheme from [`PSFGPV`](qfall_tools::primitive::psf::PSFGPV) //! ``` //! use qfall_schemes::signature::{fdh::FDHGPV, SignatureScheme}; //! diff --git a/src/signature/fdh/gpv.rs b/src/signature/fdh/gpv.rs index 6c477a4..b8515ea 100644 --- a/src/signature/fdh/gpv.rs +++ b/src/signature/fdh/gpv.rs @@ -13,7 +13,7 @@ use crate::{ hash::{sha256::HashMatZq, HashInto}, signature::SignatureScheme, }; -use qfall_crypto::{ +use qfall_tools::{ primitive::psf::{PSF, PSFGPV}, sample::g_trapdoor::gadget_parameters::GadgetParameters, }; diff --git a/src/signature/fdh/gpv_ring.rs b/src/signature/fdh/gpv_ring.rs index 88fc329..651d975 100644 --- a/src/signature/fdh/gpv_ring.rs +++ b/src/signature/fdh/gpv_ring.rs @@ -13,7 +13,7 @@ use crate::{ hash::{sha256::HashMatPolynomialRingZq, HashInto}, signature::SignatureScheme, }; -use qfall_crypto::{ +use qfall_tools::{ primitive::psf::{PSFGPVRing, PSF}, sample::g_trapdoor::gadget_parameters::GadgetParametersRing, }; @@ -27,8 +27,8 @@ use std::collections::HashMap; /// Initializes an FDH signature scheme from a [`PSFGPVRing`]. /// The trapdoor is sampled with a Gaussian parameter of 1.005 -/// as done in [\[3\]]() who derived it from -/// [\[5\]](). +/// as done in [\[3\]]() who derived it from +/// [\[5\]](). /// /// This function corresponds to an implementation of an FDH-signature /// scheme with the explicit PSF [`PSFGPVRing`] which is generated using diff --git a/src/signature/pfdh.rs b/src/signature/pfdh.rs index f81d3ad..8d6f0ec 100644 --- a/src/signature/pfdh.rs +++ b/src/signature/pfdh.rs @@ -16,14 +16,14 @@ //! using a storage, so it is stateless. //! //! Requirements -//! - `psf`: The PSF which has to implement the [`PSF`](qfall_crypto::primitive::psf::PSF) trait +//! - `psf`: The PSF which has to implement the [`PSF`](qfall_tools::primitive::psf::PSF) trait //! and must also be (de-)serializable. //! - `hash`: The hash-function which has to map a string into the correct domain. //! - `randomness_length`: The length of the salt that is added to the string before //! hashing. //! //! # Example -//! ## Signature Scheme from [`PSFGPV`](qfall_crypto::primitive::psf::PSFGPV) +//! ## Signature Scheme from [`PSFGPV`](qfall_tools::primitive::psf::PSFGPV) //! ``` //! use qfall_schemes::signature::{pfdh::PFDHGPV, SignatureScheme}; //! diff --git a/src/signature/pfdh/gpv.rs b/src/signature/pfdh/gpv.rs index c65e187..2522553 100644 --- a/src/signature/pfdh/gpv.rs +++ b/src/signature/pfdh/gpv.rs @@ -13,7 +13,7 @@ use crate::{ hash::{sha256::HashMatZq, HashInto}, signature::SignatureScheme, }; -use qfall_crypto::{ +use qfall_tools::{ primitive::psf::{PSF, PSFGPV}, sample::g_trapdoor::gadget_parameters::GadgetParameters, }; From 21a0b05116f647d1b1c0fae86c10b40f63882a37 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 13:35:35 +0200 Subject: [PATCH 06/30] cargo fmt due to renaming --- src/hash/sha256.rs | 2 +- src/identity_based_encryption/dual_regev_ibe.rs | 8 ++++---- src/signature/fdh/gpv.rs | 8 ++++---- src/signature/fdh/gpv_ring.rs | 8 ++++---- src/signature/pfdh/gpv.rs | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/hash/sha256.rs b/src/hash/sha256.rs index 4b64249..1a6c8a3 100644 --- a/src/hash/sha256.rs +++ b/src/hash/sha256.rs @@ -347,8 +347,8 @@ mod tests_sha { #[cfg(test)] mod hash_into_mat_polynomial_ring_zq { use super::{HashInto, HashMatPolynomialRingZq}; - use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; use qfall_math::{integer::PolyOverZ, traits::*}; + use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// Ensure that the hash function maps into the correct dimension and it is also /// static, i.e. the same value is returned, when the same value is hashed. diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index cde90a3..78cf26e 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -15,10 +15,6 @@ use crate::{ hash::sha256::hash_to_mat_zq_sha256, pk_encryption::{DualRegev, PKEncryptionScheme}, }; -use qfall_tools::{ - primitive::psf::{PSF, PSFGPV}, - sample::g_trapdoor::gadget_parameters::GadgetParameters, -}; use qfall_math::{ error::MathError, integer::{MatZ, Z}, @@ -26,6 +22,10 @@ use qfall_math::{ rational::{MatQ, Q}, traits::{Concatenate, MatrixDimensions, Pow}, }; +use qfall_tools::{ + primitive::psf::{PSF, PSFGPV}, + sample::g_trapdoor::gadget_parameters::GadgetParameters, +}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/src/signature/fdh/gpv.rs b/src/signature/fdh/gpv.rs index b8515ea..93154a9 100644 --- a/src/signature/fdh/gpv.rs +++ b/src/signature/fdh/gpv.rs @@ -13,15 +13,15 @@ use crate::{ hash::{sha256::HashMatZq, HashInto}, signature::SignatureScheme, }; -use qfall_tools::{ - primitive::psf::{PSF, PSFGPV}, - sample::g_trapdoor::gadget_parameters::GadgetParameters, -}; use qfall_math::{ integer::{MatZ, Z}, integer_mod_q::{MatZq, Modulus}, rational::{MatQ, Q}, }; +use qfall_tools::{ + primitive::psf::{PSF, PSFGPV}, + sample::g_trapdoor::gadget_parameters::GadgetParameters, +}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/src/signature/fdh/gpv_ring.rs b/src/signature/fdh/gpv_ring.rs index 651d975..0dc4793 100644 --- a/src/signature/fdh/gpv_ring.rs +++ b/src/signature/fdh/gpv_ring.rs @@ -13,15 +13,15 @@ use crate::{ hash::{sha256::HashMatPolynomialRingZq, HashInto}, signature::SignatureScheme, }; -use qfall_tools::{ - primitive::psf::{PSFGPVRing, PSF}, - sample::g_trapdoor::gadget_parameters::GadgetParametersRing, -}; use qfall_math::{ integer::{MatPolyOverZ, Z}, integer_mod_q::{MatPolynomialRingZq, Modulus}, rational::Q, }; +use qfall_tools::{ + primitive::psf::{PSFGPVRing, PSF}, + sample::g_trapdoor::gadget_parameters::GadgetParametersRing, +}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/src/signature/pfdh/gpv.rs b/src/signature/pfdh/gpv.rs index 2522553..44c5ec0 100644 --- a/src/signature/pfdh/gpv.rs +++ b/src/signature/pfdh/gpv.rs @@ -13,16 +13,16 @@ use crate::{ hash::{sha256::HashMatZq, HashInto}, signature::SignatureScheme, }; -use qfall_tools::{ - primitive::psf::{PSF, PSFGPV}, - sample::g_trapdoor::gadget_parameters::GadgetParameters, -}; use qfall_math::{ integer::{MatZ, Z}, integer_mod_q::{MatZq, Modulus}, rational::{MatQ, Q}, traits::Pow, }; +use qfall_tools::{ + primitive::psf::{PSF, PSFGPV}, + sample::g_trapdoor::gadget_parameters::GadgetParameters, +}; /// Initializes an PFDH signature scheme from a [`PSFGPV`]. /// From 7c2a3598db33fc900403aa86c428ec0b092891e8 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 13:37:47 +0200 Subject: [PATCH 07/30] fix renamed repository link in cargo toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 591ed7a..66a37d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-tools = { git = "https://github.com/qfall/crypto", branch="move-schemes" } +qfall-tools = { git = "https://github.com/qfall/tools", branch="move-schemes" } qfall-math = { git = "https://github.com/qfall/math", rev="5f50c9cd31c869462d959774fb4b51fcd1727dbe" } sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} From e09429eb8da2e315041a1cb6b8a9ba9e6688cbfe Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 14:05:52 +0200 Subject: [PATCH 08/30] small name fix --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ae69b1a..b009906 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ //! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-schemes on our [website](https://qfall.github.io/). //! The tutorial explains the basic steps starting from installation and //! continues with basic usage. -//! qfall-schemes is co-developed together with qFALL-math and qfall-crypto which provides the basic +//! qfall-schemes is co-developed together with qFALL-math and qFALL-tools which provides the basic //! foundation that is used to implement the cryptographic constructions. //! //! This module contains fundamental cryptographic constructions, on which other From 7fb5f11a48ca1e53429c21cf65a4d7f1e54af940 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Fri, 17 Oct 2025 13:49:35 +0100 Subject: [PATCH 09/30] Revise descriptions --- README.md | 41 +++++++++++++++++++++-------------------- src/lib.rs | 19 +++---------------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 4e0073e..54c491c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # qFALL-schemes [![made-with-rust](https://img.shields.io/badge/Made%20with-Rust-1f425f.svg)](https://www.rust-lang.org/) -[![CI](https://github.com/qfall/crypto/actions/workflows/push.yml/badge.svg?branch=dev)](https://github.com/qfall/schemes/actions/workflows/pull_request.yml) +[![CI](https://github.com/qfall/schemes/actions/workflows/push.yml/badge.svg?branch=dev)](https://github.com/qfall/schemes/actions/workflows/pull_request.yml) [![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0) This repository is currently being developed by the project group [qFALL - quantum resistant fast lattice library](https://cs.uni-paderborn.de/cuk/lehre/veranstaltungen/ws-2022-23/project-group-qfall) in the winter term 2022 and summer term 2023 by the Codes and Cryptography research group in Paderborn. @@ -27,27 +27,28 @@ qFALL-schemes offers a variety of implementations of cryptographic schemes, cons We provide a brief overview in the following list. For a more detailed description, please refer to [our tutorial section](https://qfall.github.io/book/crypto/features.html). -Full-fledged Cryptographic Features - -- [Public Key Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption.rs) - - [LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/regev.rs) - - [Dual LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/dual_regev.rs) - - [LPR Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/lpr.rs) - - [Ring-based LPR Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/ring_lpr.rs) - - [CCA-secure Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/ccs_from_ibe.rs) -- [Signatures](https://github.com/qfall/crypto/blob/dev/src/construction/signature.rs) - - [Full-Domain Hash (FDH)](https://github.com/qfall/crypto/blob/dev/src/construction/signature/fdh.rs) - - [Probabilistic FDH (PFDH)](https://github.com/qfall/crypto/blob/dev/src/construction/signature/pfdh.rs) - - [Ring-based FDH](https://github.com/qfall/crypto/blob/dev/src/construction/signature/fdh/gpv_ring.rs) -- [Identity Based Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/identity_based_encryption.rs) - - [From Dual LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/identity_based_encryption/dual_regev_ibe.rs) -- [Hash Functions](https://github.com/qfall/crypto/blob/dev/src/construction/hash.rs) - - [SIS-Hash Function](https://github.com/qfall/crypto/blob/dev/src/construction/hash/sis.rs) - - [SHA-256-based Hash](https://github.com/qfall/crypto/blob/dev/src/construction/hash/sha256.rs) +Constructions + +- [Public Key Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption.rs) + - [LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/regev.rs) + - [Dual LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/dual_regev.rs) + - [LPR Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/lpr.rs) + - [Ring-based LPR Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/ring_lpr.rs) + - [Prototype of K-PKE](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/k_pke.rs), which is the foundation of [CRYSTALS-Kyber](https://pq-crystals.org/kyber/) and [ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) + - [CCA-secure Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/ccs_from_ibe.rs) +- [Signatures](https://github.com/qfall/schemes/blob/dev/src/construction/signature.rs) + - [Full-Domain Hash (FDH)](https://github.com/qfall/schemes/blob/dev/src/construction/signature/fdh.rs) + - [Probabilistic FDH (PFDH)](https://github.com/qfall/schemes/blob/dev/src/construction/signature/pfdh.rs) + - [Ring-based FDH](https://github.com/qfall/schemes/blob/dev/src/construction/signature/fdh/gpv_ring.rs) +- [Identity Based Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/identity_based_encryption.rs) + - [From Dual LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/identity_based_encryption/dual_regev_ibe.rs) +- [Hash Functions](https://github.com/qfall/schemes/blob/dev/src/construction/hash.rs) + - [SIS-Hash Function](https://github.com/qfall/schemes/blob/dev/src/construction/hash/sis.rs) + - [SHA-256-based Hash](https://github.com/qfall/schemes/blob/dev/src/construction/hash/sha256.rs) ## License -This library is distributed under the **Mozilla Public License Version 2.0** which can be found here [License](https://github.com/qfall/crypto/blob/dev/LICENSE). +This library is distributed under the **Mozilla Public License Version 2.0** which can be found here [License](https://github.com/qfall/schemes/blob/dev/LICENSE). Permissions of this weak copyleft license are conditioned on making available source code of licensed files and modifications of those files under the same license (or in certain cases, one of the GNU licenses). Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. However, a larger work using the licensed work may be distributed under different terms and without source code for files added in the larger work. ## Citing @@ -58,7 +59,7 @@ Please use the following bibtex entry to cite [qFALL-schemes](https://github.com @misc{qFALL-schemes, author = {Porzenheim, Laurens and Beckmann, Marvin and Kramer, Paul and Milewski, Phil and Moog, Sven and Schmidt, Marcel and Siemer, Niklas}, title = {qFALL-schemes v0.0}, - howpublished = {Online: \url{https://github.com/qfall/crypto}}, + howpublished = {Online: \url{https://github.com/qfall/schemes}}, month = Mar, year = 2023, note = {University Paderborn, Codes and Cryptography} diff --git a/src/lib.rs b/src/lib.rs index b009906..6d324e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,10 +7,8 @@ // Mozilla Foundation. See . //! # What is qFALL-schemes? -//! qFall-schemes provides cryptographic basics such as mathematical primitives, -//! fundamental lattice-based cryptographic constructions, and samplable distributions/ -//! possibilities to sample instances of lattice problems to prototype -//! lattice-based cryptographic constructions and more. +//! qFall-schemes provides lattice-based cryptographic constructions to enable prototyping +//! based on the existing constructions. //! //! Currently qFALL-schemes supports 3 main construction types: //! - [Identity-Based Encryptions](identity_based_encryption::IBEScheme) @@ -28,19 +26,8 @@ //! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-schemes on our [website](https://qfall.github.io/). //! The tutorial explains the basic steps starting from installation and //! continues with basic usage. -//! qfall-schemes is co-developed together with qFALL-math and qFALL-tools which provides the basic +//! qfall-schemes is co-developed together with qFALL-math and qFALL-tools which provide the //! foundation that is used to implement the cryptographic constructions. -//! -//! This module contains fundamental cryptographic constructions, on which other -//! constructions can be build on. -//! -//! Among others, these include encryption schemes and signature schemes. -//! A construction is always build the same way: -//! -//! 1. A trait that combines the common feature, e.g. -//! [`public key encryption`](pk_encryption::PKEncryptionScheme). -//! 2. Explicit implementations of the trait, e.g. -//! [`RingLPR`](pk_encryption::RingLPR). pub mod hash; pub mod identity_based_encryption; From e38e1abe789157c5e120ed13f803c77b653884cb Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 16:35:00 +0200 Subject: [PATCH 10/30] base on dev now that other branch is merged --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 66a37d8..b815065 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-tools = { git = "https://github.com/qfall/tools", branch="move-schemes" } +qfall-tools = { git = "https://github.com/qfall/tools", branch="dev" } qfall-math = { git = "https://github.com/qfall/math", rev="5f50c9cd31c869462d959774fb4b51fcd1727dbe" } sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} From 88dae654e7e9f35a4d4aa39205fe1e632379dd05 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Sat, 18 Oct 2025 11:50:47 +0100 Subject: [PATCH 11/30] Update links in README --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 54c491c..95efff6 100644 --- a/README.md +++ b/README.md @@ -29,22 +29,22 @@ For a more detailed description, please refer to [our tutorial section](https:// Constructions -- [Public Key Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption.rs) - - [LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/regev.rs) - - [Dual LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/dual_regev.rs) - - [LPR Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/lpr.rs) - - [Ring-based LPR Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/ring_lpr.rs) - - [Prototype of K-PKE](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/k_pke.rs), which is the foundation of [CRYSTALS-Kyber](https://pq-crystals.org/kyber/) and [ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) - - [CCA-secure Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/pk_encryption/ccs_from_ibe.rs) -- [Signatures](https://github.com/qfall/schemes/blob/dev/src/construction/signature.rs) - - [Full-Domain Hash (FDH)](https://github.com/qfall/schemes/blob/dev/src/construction/signature/fdh.rs) - - [Probabilistic FDH (PFDH)](https://github.com/qfall/schemes/blob/dev/src/construction/signature/pfdh.rs) - - [Ring-based FDH](https://github.com/qfall/schemes/blob/dev/src/construction/signature/fdh/gpv_ring.rs) -- [Identity Based Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/identity_based_encryption.rs) - - [From Dual LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/construction/identity_based_encryption/dual_regev_ibe.rs) -- [Hash Functions](https://github.com/qfall/schemes/blob/dev/src/construction/hash.rs) - - [SIS-Hash Function](https://github.com/qfall/schemes/blob/dev/src/construction/hash/sis.rs) - - [SHA-256-based Hash](https://github.com/qfall/schemes/blob/dev/src/construction/hash/sha256.rs) +- [Public Key Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption.rs) + - [LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/regev.rs) + - [Dual LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/dual_regev.rs) + - [LPR Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/lpr.rs) + - [Ring-based LPR Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/ring_lpr.rs) + - [Prototype of K-PKE](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/k_pke.rs), which is the foundation of [CRYSTALS-Kyber](https://pq-crystals.org/kyber/) and [ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) + - [CCA-secure Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/ccs_from_ibe.rs) +- [Signatures](https://github.com/qfall/schemes/blob/dev/src/signature.rs) + - [Full-Domain Hash (FDH)](https://github.com/qfall/schemes/blob/dev/src/signature/fdh.rs) + - [Probabilistic FDH (PFDH)](https://github.com/qfall/schemes/blob/dev/src/signature/pfdh.rs) + - [Ring-based FDH](https://github.com/qfall/schemes/blob/dev/src/signature/fdh/gpv_ring.rs) +- [Identity Based Encryption](https://github.com/qfall/schemes/blob/dev/src/identity_based_encryption.rs) + - [From Dual LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/identity_based_encryption/dual_regev_ibe.rs) +- [Hash Functions](https://github.com/qfall/schemes/blob/dev/src/hash.rs) + - [SIS-Hash Function](https://github.com/qfall/schemes/blob/dev/src/hash/sis.rs) + - [SHA-256-based Hash](https://github.com/qfall/schemes/blob/dev/src/hash/sha256.rs) ## License From fed3603c594ed8394bb05fa255540891a8fcf5b7 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Sat, 18 Oct 2025 11:51:37 +0100 Subject: [PATCH 12/30] Remove bad 128-bit secure parameter choices --- src/pk_encryption/dual_regev.rs | 18 ------------------ .../dual_regev_discrete_gauss.rs | 19 ------------------- src/pk_encryption/lpr.rs | 18 ------------------ src/pk_encryption/regev.rs | 18 ------------------ src/pk_encryption/regev_discrete_gauss.rs | 18 ------------------ src/pk_encryption/ring_lpr.rs | 18 ------------------ 6 files changed, 109 deletions(-) diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index 362793c..b8375fe 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -292,14 +292,6 @@ impl DualRegev { Ok(()) } - - /// This function instantiates a 128-bit secure [`DualRegev`] scheme. - /// - /// The public parameters used for this scheme were generated via `DualRegev::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(230, 5313, 7764299, 0.0011) - } } impl Default for DualRegev { @@ -524,16 +516,6 @@ mod test_pp_generation { fn invalid_n() { DualRegev::new_from_n(9); } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let dr = DualRegev::secure128(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } } #[cfg(test)] diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index fcadb54..7f64671 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -312,15 +312,6 @@ impl DualRegevWithDiscreteGaussianRegularity { Ok(()) } - - /// This function instantiates a 128-bit secure [`DualRegevWithDiscreteGaussianRegularity`] scheme. - /// - /// The public parameters used for this scheme were generated - /// via `DualRegevWithDiscreteGaussianRegularity::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(350, 5248, 29892991, 12.357, 0.00009) - } } impl Default for DualRegevWithDiscreteGaussianRegularity { @@ -545,16 +536,6 @@ mod test_pp_generation { fn invalid_n() { DualRegevWithDiscreteGaussianRegularity::new_from_n(1); } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let dr = DualRegevWithDiscreteGaussianRegularity::secure128(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } } #[cfg(test)] diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index d1236c8..2f132f3 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -282,14 +282,6 @@ impl LPR { Ok(()) } - - /// This function instantiates a 128-bit secure [`LPR`] scheme. - /// - /// The public parameters used for this scheme were generated via `LPR::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(500, 76859609, 0.000005) - } } impl Default for LPR { @@ -543,16 +535,6 @@ mod test_pp_generation { fn invalid_n() { LPR::new_from_n(9); } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let lpr = LPR::secure128(); - - assert!(lpr.check_correctness().is_ok()); - assert!(lpr.check_security().is_ok()); - } } #[cfg(test)] diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 607c066..3849fa7 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -293,14 +293,6 @@ impl Regev { Ok(()) } - - /// This function instantiates a 128-bit secure [`Regev`] scheme. - /// - /// The public parameters used for this scheme were generated via `Regev::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(230, 5313, 7764299, 0.0011) - } } impl Default for Regev { @@ -526,16 +518,6 @@ mod test_pp_generation { fn invalid_n() { Regev::new_from_n(9); } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let regev = Regev::secure128(); - - assert!(regev.check_correctness().is_ok()); - assert!(regev.check_security().is_ok()); - } } #[cfg(test)] diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index b074f9b..ec5e1f7 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -312,14 +312,6 @@ impl RegevWithDiscreteGaussianRegularity { Ok(()) } - - /// This function instantiates a 128-bit secure [`RegevWithDiscreteGaussianRegularity`] scheme. - /// - /// The public parameters used for this scheme were generated via `RegevWithDiscreteGaussianRegularity::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(350, 5248, 29892991, 12.357, 0.00009) - } } impl Default for RegevWithDiscreteGaussianRegularity { @@ -540,16 +532,6 @@ mod test_pp_generation { fn invalid_n() { RegevWithDiscreteGaussianRegularity::new_from_n(1); } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let dr = RegevWithDiscreteGaussianRegularity::secure128(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } } #[cfg(test)] diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index 2a85d79..9e70e0b 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -309,14 +309,6 @@ impl RingLPR { Ok(()) } - - /// This function instantiates a 128-bit secure [`RingLPR`] scheme. - /// - /// The public parameters used for this scheme were generated via `RingLPR::new_from_n(512)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(512, 92897729, 0.000005) - } } impl Default for RingLPR { @@ -562,16 +554,6 @@ mod test_pp_generation { fn invalid_n() { RingLPR::new_from_n(9); } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let scheme = RingLPR::secure128(); - - assert!(scheme.check_correctness().is_ok()); - assert!(scheme.check_security().is_ok()); - } } #[cfg(test)] From 8d6b6d33fb97db7da44f5cd60026eb2a7f44148e Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Thu, 20 Nov 2025 17:15:23 +0000 Subject: [PATCH 13/30] Adjust to changes on math-crate --- Cargo.toml | 4 +- src/pk_encryption/dual_regev.rs | 1 - .../dual_regev_discrete_gauss.rs | 16 +++---- src/pk_encryption/lpr.rs | 37 ++++----------- src/pk_encryption/regev.rs | 12 ++--- src/pk_encryption/regev_discrete_gauss.rs | 14 ++---- src/pk_encryption/ring_lpr.rs | 45 +++++-------------- 7 files changed, 33 insertions(+), 96 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b815065..f4d1751 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,8 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-tools = { git = "https://github.com/qfall/tools", branch="dev" } -qfall-math = { git = "https://github.com/qfall/math", rev="5f50c9cd31c869462d959774fb4b51fcd1727dbe" } +qfall-tools = { git = "https://github.com/qfall/tools", branch="update" } +qfall-math = { git = "https://github.com/qfall/math", rev="1ee0b9f41676894d48520322109a3364b8f3338e" } sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} serde_json = "1.0" diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index b8375fe..8a637b3 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -385,7 +385,6 @@ impl PKEncryptionScheme for DualRegev { 1, &(&self.m + 1), &self.q, - &self.n, 0, &self.alpha * Z::from(&self.q), ) diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index 7f64671..00b795f 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -359,7 +359,7 @@ impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { /// ``` fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { // e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r - let vec_e = MatZq::sample_d_common(&self.m, &self.q, &self.n, &self.r).unwrap(); + let vec_e = MatZq::sample_discrete_gauss(&self.m, 1, &self.q, 0, &self.r).unwrap(); // A <- Z_q^{n x m} let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); @@ -399,18 +399,12 @@ impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { // s <- Z_q^n let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); // vec_x <- χ^m - let vec_x = MatZq::sample_discrete_gauss( - &self.m, - 1, - &self.q, - &self.n, - 0, - &(&self.alpha * Z::from(&self.q)), - ) - .unwrap(); + let vec_x = + MatZq::sample_discrete_gauss(&self.m, 1, &self.q, 0, &(&self.alpha * Z::from(&self.q))) + .unwrap(); // x <- χ - let x = Z::sample_discrete_gauss(&self.n, 0, &(&self.alpha * Z::from(&self.q))).unwrap(); + let x = Z::sample_discrete_gauss(0, &(&self.alpha * Z::from(&self.q))).unwrap(); // p = u^t * s + vec_x let vec_p = &pk.0.transpose() * &vec_s + vec_x; diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index 2f132f3..07e4959 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -331,25 +331,13 @@ impl PKEncryptionScheme for LPR { // A <- Z_q^{n x n} let mat_a = MatZq::sample_uniform(&self.n, &self.n, &self.q); // s <- χ^n - let vec_s = MatZq::sample_discrete_gauss( - &self.n, - 1, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); + let vec_s = + MatZq::sample_discrete_gauss(&self.n, 1, &self.q, 0, &self.alpha * Z::from(&self.q)) + .unwrap(); // e <- χ^n - let vec_e_t = MatZq::sample_discrete_gauss( - 1, - &self.n, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); + let vec_e_t = + MatZq::sample_discrete_gauss(1, &self.n, &self.q, 0, &self.alpha * Z::from(&self.q)) + .unwrap(); // b^t = s^t * A + e^t let vec_b_t = vec_s.transpose() * &mat_a + vec_e_t; @@ -388,21 +376,14 @@ impl PKEncryptionScheme for LPR { let message: Z = message.into() % 2; // x <- χ^n - let vec_r = MatZq::sample_discrete_gauss( - &self.n, - 1, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); + let vec_r = + MatZq::sample_discrete_gauss(&self.n, 1, &self.q, 0, &self.alpha * Z::from(&self.q)) + .unwrap(); // e <- χ^{n+1} let vec_e = MatZq::sample_discrete_gauss( &(&self.n + 1), 1, &self.q, - &self.n, 0, &self.alpha * Z::from(&self.q), ) diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 3849fa7..7d4d9e6 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -345,15 +345,9 @@ impl PKEncryptionScheme for Regev { // s <- Z_q^n let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); // e^t <- χ^m - let vec_e_t = MatZq::sample_discrete_gauss( - 1, - &self.m, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); + let vec_e_t = + MatZq::sample_discrete_gauss(1, &self.m, &self.q, 0, &self.alpha * Z::from(&self.q)) + .unwrap(); // b^t = s^t * A + e^t let vec_b_t = vec_s.transpose() * &mat_a + vec_e_t; diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index ec5e1f7..01b0c19 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -365,15 +365,9 @@ impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { // A <- Z_q^{n x m} let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); // x <- χ^m - let vec_x = MatZq::sample_discrete_gauss( - &self.m, - 1, - &self.q, - &self.n, - 0, - &(&self.alpha * Z::from(&self.q)), - ) - .unwrap(); + let vec_x = + MatZq::sample_discrete_gauss(&self.m, 1, &self.q, 0, &(&self.alpha * Z::from(&self.q))) + .unwrap(); // p = A^t * s + x let vec_p = mat_a.transpose() * &vec_s + vec_x; @@ -407,7 +401,7 @@ impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { let message: Z = message.into() % 2; // e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r - let vec_e = MatZq::sample_d_common(&self.m, &self.q, &self.n, &self.r).unwrap(); + let vec_e = MatZq::sample_discrete_gauss(&self.m, 1, &self.q, 0, &self.r).unwrap(); // u = A * e let vec_u = &pk.0 * &vec_e; diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index 9e70e0b..b343ef7 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -353,21 +353,11 @@ impl PKEncryptionScheme for RingLPR { // a <- R_q let a = PolynomialRingZq::sample_uniform(&self.q); // s <- χ - let s = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); + let s = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q()) + .unwrap(); // e <- χ - let e = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); + let e = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q()) + .unwrap(); // b = s * a + e let b = &a * &s + e; @@ -409,29 +399,14 @@ impl PKEncryptionScheme for RingLPR { let mu_q_half = encode_z_bitwise_in_polynomialringzq(&self.q, &mu); // r <- χ - let r = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); + let r = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q()) + .unwrap(); // e1 <- χ - let e1 = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); + let e1 = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q()) + .unwrap(); // e2 <- χ - let e2 = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); + let e2 = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q()) + .unwrap(); // u = a * r + e1 let u = &pk.0 * &r + e1; From 3e2a4f5676503ed694aa962260e8805d970b46cd Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 9 Dec 2025 15:24:11 +0000 Subject: [PATCH 14/30] Bump criterion version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f4d1751..b00961b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} serde_json = "1.0" typetag = "0.2" -criterion = { version = "0.7", features = ["html_reports"] } +criterion = { version = "0.8", features = ["html_reports"] } [profile.bench] debug = true From 9472e9b7916be7724aa597106fde085360d5d8de Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 9 Dec 2025 15:31:13 +0000 Subject: [PATCH 15/30] Add compression to K-PKE --- src/identity_based_encryption.rs | 2 +- src/pk_encryption.rs | 6 ++--- src/pk_encryption/k_pke.rs | 40 ++++++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/identity_based_encryption.rs b/src/identity_based_encryption.rs index 5606c3e..e174f90 100644 --- a/src/identity_based_encryption.rs +++ b/src/identity_based_encryption.rs @@ -78,5 +78,5 @@ pub trait IBEScheme { /// - `cipher`: specifies the ciphertext to be decrypted /// /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; + fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z; } diff --git a/src/pk_encryption.rs b/src/pk_encryption.rs index bd12830..590939e 100644 --- a/src/pk_encryption.rs +++ b/src/pk_encryption.rs @@ -84,7 +84,7 @@ pub trait PKEncryptionScheme { /// - `cipher`: specifies the ciphertext to be decrypted /// /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; + fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z; } /// This trait just exists s.t. we can pass `self` in as mutable for more advanced constructions, which use a storage. @@ -115,7 +115,7 @@ pub trait PKEncryptionSchemeMut { /// - `cipher`: specifies the ciphertext to be decrypted /// /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&mut self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; + fn dec(&mut self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z; } /// This trait generically implements multi-bit encryption @@ -157,7 +157,7 @@ pub trait GenericMultiBitEncryption: PKEncryptionScheme { /// to be decrypted /// /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec_multiple_bits(&self, sk: &Self::SecretKey, cipher: &[Self::Cipher]) -> Z { + fn dec_multiple_bits(&self, sk: &Self::SecretKey, cipher: Vec) -> Z { let mut bits = vec![]; for item in cipher { diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index 3b99d03..001e5bb 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -10,7 +10,7 @@ //! used as foundation for ML-KEM. //! //! **WARNING:** This implementation is a toy implementation of the basics below -//! ML-KEM and mostly supposed to showcase the prototyping capabilities of the `qfall`-library. +//! ML-KEM and mostly supposed to showcase the prototyping capabilities of the `qFALL`-library. use crate::pk_encryption::PKEncryptionScheme; use qfall_math::{ @@ -22,6 +22,7 @@ use qfall_tools::utils::{ decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, }, common_moduli::new_anticyclic, + lossy_compression::LossyCompression, }; use serde::{Deserialize, Serialize}; @@ -29,9 +30,8 @@ use serde::{Deserialize, Serialize}; /// as a basis for ML-KEM. /// /// This implementation is not supposed to be an implementation of the FIPS 203 standard in [\[6\]](), but -/// is supposed to showcase the prototyping capabilities of `qfall` and does not cover compression algorithms -/// as specified in the FIPS 203 document or might deviate for the choice of matrix multiplication algorithms. -/// Especially, NTT-representation, sampling and multiplication are not part of this prototype. +/// is supposed to showcase the prototyping capabilities of `qFALL` and does not cover byte decomposition algorithms +/// as specified in the FIPS 203 document or NTT-multiplication. /// /// Attributes: /// - `q`: defines the modulus polynomial `(X^n + 1) mod p` @@ -64,6 +64,8 @@ pub struct KPKE { k: i64, // defines both dimensions of matrix A eta_1: i64, // defines the binomial distribution of the secret and error drawn in `gen` eta_2: i64, // defines the binomial distribution of the error drawn in `enc` + d_u: i64, // defines the number of kept upper-order bits per entry of vector `u` + d_v: i64, // defines the number of kept upper-order bits per entry of `v` } impl KPKE { @@ -75,6 +77,8 @@ impl KPKE { k: 2, eta_1: 3, eta_2: 2, + d_u: 10, + d_v: 4, } } @@ -86,6 +90,8 @@ impl KPKE { k: 3, eta_1: 2, eta_2: 2, + d_u: 10, + d_v: 4, } } @@ -97,6 +103,8 @@ impl KPKE { k: 4, eta_1: 2, eta_2: 2, + d_u: 11, + d_v: 5, } } } @@ -160,6 +168,7 @@ impl PKEncryptionScheme for KPKE { /// - e_2 <- Bin(eta_2, 0.5) centered around 0 /// - u = A^T * y + e_1 /// - v = t^T * y + e_2 + 𝜇, where 𝜇 is the {q/2, 0} encoding of the bits of `message` + /// - Compress u and v /// /// Then, ciphertext `(u, v)` is returned. /// @@ -208,18 +217,24 @@ impl PKEncryptionScheme for KPKE { .unwrap(); // 19 𝐮 ← NTT^−1(𝐀^⊺ ∘ 𝐲) + 𝐞_𝟏 - let vec_u = &pk.0 * &vec_y + vec_e_1; + let mut vec_u = &pk.0 * &vec_y + vec_e_1; // 20 𝜇 ← Decompress_1(ByteDecode_1(𝑚)) let mu = encode_z_bitwise_in_polynomialringzq(&self.q, &message.into()); // 21 𝑣 ← NTT^−1(𝐭^⊺ ∘ 𝐲) + 𝑒_2 + 𝜇 - let v = pk.1.dot_product(&vec_y).unwrap() + e_2 + mu; + let mut v = pk.1.dot_product(&vec_y).unwrap() + e_2 + mu; + + // 22: 𝑐_1 ← ByteEncode_{𝑑_𝑢}(Compress_{𝑑_𝑢}(𝐮)) + vec_u.compress(self.d_u); + // 23: 𝑐_2 ← ByteEncode_{𝑑_𝑣}(Compress_{𝑑_𝑣}(𝑣)) + v.compress(self.d_v); (vec_u, v) } /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: + /// - Decompress u and v /// - w = v - s^T * u /// - returns the decoding of `w` with 1 and 0 set in the returned [`Z`] instance /// if the corresponding coefficient was closer to q/2 or 0 respectively @@ -241,9 +256,14 @@ impl PKEncryptionScheme for KPKE { /// /// assert_eq!(1, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, (u, v): &Self::Cipher) -> Z { - // 6 𝑤 ← 𝑣 − NTT^−1(𝐬^⊺ ∘ NTT(𝐮)) - let w = v - sk.dot_product(u).unwrap(); + fn dec(&self, sk: &Self::SecretKey, (mut u, mut v): Self::Cipher) -> Z { + // 3: 𝐮′ ← Decompress_{𝑑_𝑢}(ByteDecode_{𝑑_𝑢}(𝑐_1)) + u.decompress(self.d_u); + // 4: 𝑣′ ← Decompress_{𝑑_𝑣}(ByteDecode_{𝑑_𝑣}(𝑐_2)) + v.decompress(self.d_v); + + // 6 𝑤 ← 𝑣′ − NTT^−1(𝐬^⊺ ∘ NTT(𝐮′)) + let w = v - sk.dot_product(&u).unwrap(); // 7 𝑚 ← ByteEncode_1(Compress_1(𝑤)) decode_z_bitwise_from_polynomialringzq(self.q.get_q(), &w) @@ -265,7 +285,7 @@ mod test_kpke { for message in messages { let (pk, sk) = k_pke.gen(); let c = k_pke.enc(&pk, message); - let m = k_pke.dec(&sk, &c); + let m = k_pke.dec(&sk, c); assert_eq!(message, m); } From de7f320fefc69d6788b3de311e7ae0c7195462ce Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 9 Dec 2025 15:32:07 +0000 Subject: [PATCH 16/30] Require ciphertext to be owned by dec --- benches/k_pke.rs | 26 ++++++++++++++++--- benches/regev.rs | 2 +- .../dual_regev_ibe.rs | 12 ++++----- src/pk_encryption/ccs_from_ibe.rs | 4 +-- .../ccs_from_ibe/dual_regev_ibe_pfdh.rs | 4 +-- src/pk_encryption/dual_regev.rs | 18 ++++++------- .../dual_regev_discrete_gauss.rs | 18 ++++++------- src/pk_encryption/lpr.rs | 20 +++++++------- src/pk_encryption/regev.rs | 20 +++++++------- src/pk_encryption/regev_discrete_gauss.rs | 18 ++++++------- src/pk_encryption/ring_lpr.rs | 8 +++--- 11 files changed, 84 insertions(+), 66 deletions(-) diff --git a/benches/k_pke.rs b/benches/k_pke.rs index 55c895e..ea6dbc7 100644 --- a/benches/k_pke.rs +++ b/benches/k_pke.rs @@ -14,7 +14,7 @@ use qfall_schemes::pk_encryption::KPKE; fn kpke_cycle(k_pke: &KPKE) { let (pk, sk) = k_pke.gen(); let cipher = k_pke.enc(&pk, 1); - let _ = k_pke.dec(&sk, &cipher); + let _ = k_pke.dec(&sk, cipher); } /// Benchmark [kpke_cycle] with [KPKE::ml_kem_512]. @@ -51,7 +51,13 @@ fn bench_kpke_dec_512(c: &mut Criterion) { let (pk, sk) = k_pke.gen(); let cipher = k_pke.enc(&pk, i64::MAX); - c.bench_function("K-PKE dec 512", |b| b.iter(|| k_pke.dec(&sk, &cipher))); + c.bench_function("K-PKE dec 512", |b| { + b.iter_batched( + || cipher.clone(), + |cipher| k_pke.dec(&sk, cipher), + criterion::BatchSize::SmallInput, + ) + }); } /// Benchmark [kpke_cycle] with [KPKE::ml_kem_768]. @@ -88,7 +94,13 @@ fn bench_kpke_dec_768(c: &mut Criterion) { let (pk, sk) = k_pke.gen(); let cipher = k_pke.enc(&pk, i64::MAX); - c.bench_function("K-PKE dec 768", |b| b.iter(|| k_pke.dec(&sk, &cipher))); + c.bench_function("K-PKE dec 768", |b| { + b.iter_batched( + || cipher.clone(), + |cipher| k_pke.dec(&sk, cipher), + criterion::BatchSize::SmallInput, + ) + }); } /// Benchmark [kpke_cycle] with [KPKE::ml_kem_1024]. @@ -125,7 +137,13 @@ fn bench_kpke_dec_1024(c: &mut Criterion) { let (pk, sk) = k_pke.gen(); let cipher = k_pke.enc(&pk, i64::MAX); - c.bench_function("K-PKE dec 1024", |b| b.iter(|| k_pke.dec(&sk, &cipher))); + c.bench_function("K-PKE dec 1024", |b| { + b.iter_batched( + || cipher.clone(), + |cipher| k_pke.dec(&sk, cipher), + criterion::BatchSize::SmallInput, + ) + }); } criterion_group!( diff --git a/benches/regev.rs b/benches/regev.rs index 10635a4..d6e75f9 100644 --- a/benches/regev.rs +++ b/benches/regev.rs @@ -18,7 +18,7 @@ fn regev_cycle(n: i64) { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let _ = regev.dec(&sk, &cipher); + let _ = regev.dec(&sk, cipher); } /// Benchmark [regev_cycle] with `n = 50`. diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index 78cf26e..7698965 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -419,7 +419,7 @@ impl IBEScheme for DualRegevIBE { /// /// assert_eq!(msg, m) /// ``` - fn dec(&self, sk_id: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + fn dec(&self, sk_id: &Self::SecretKey, cipher: Self::Cipher) -> Z { self.dual_regev.dec(sk_id, cipher) } } @@ -473,7 +473,7 @@ mod test_dual_regev_ibe { let (pk, sk) = cryptosystem.setup(); let id_sk = cryptosystem.extract(&pk, &sk, &id); let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); + let m = cryptosystem.dec(&id_sk, cipher); assert_eq!(msg, m) } @@ -489,7 +489,7 @@ mod test_dual_regev_ibe { let (pk, sk) = cryptosystem.setup(); let id_sk = cryptosystem.extract(&pk, &sk, &id); let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); + let m = cryptosystem.dec(&id_sk, cipher); assert_eq!(msg, m) } @@ -505,7 +505,7 @@ mod test_dual_regev_ibe { let (pk, sk) = cryptosystem.setup(); let id_sk = cryptosystem.extract(&pk, &sk, &id); let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); + let m = cryptosystem.dec(&id_sk, cipher); assert_eq!(msg, m); } @@ -520,7 +520,7 @@ mod test_dual_regev_ibe { let (pk, sk) = cryptosystem.setup(); let id_sk = cryptosystem.extract(&pk, &sk, &id); let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); + let m = cryptosystem.dec(&id_sk, cipher); assert_eq!(msg, m); } @@ -540,7 +540,7 @@ mod test_dual_regev_ibe { let id_sk = cryptosystem.extract(&pk, &sk, &id); for _j in 1..=100 { let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); + let m = cryptosystem.dec(&id_sk, cipher); assert_eq!(msg, m); } diff --git a/src/pk_encryption/ccs_from_ibe.rs b/src/pk_encryption/ccs_from_ibe.rs index 50cc089..3f73b56 100644 --- a/src/pk_encryption/ccs_from_ibe.rs +++ b/src/pk_encryption/ccs_from_ibe.rs @@ -129,7 +129,7 @@ where /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&mut self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + fn dec(&mut self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { if !self .signature .vfy(cipher.1.to_string(), &cipher.2, &cipher.0) @@ -138,6 +138,6 @@ where } let secret = self.ibe.extract(&sk.0, &sk.1, &cipher.0.clone().into()); - self.ibe.dec(&secret, &cipher.1) + self.ibe.dec(&secret, cipher.1) } } diff --git a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs index b92e38d..d15c21c 100644 --- a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs +++ b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs @@ -108,7 +108,7 @@ mod test_ccs_from_ibe { let (pk, sk) = scheme.gen(); let cipher = scheme.enc(&pk, &msg); - let m = scheme.dec(&sk, &cipher); + let m = scheme.dec(&sk, cipher); assert_eq!(msg, m); } @@ -121,7 +121,7 @@ mod test_ccs_from_ibe { let (pk, sk) = scheme.gen(); let cipher = scheme.enc(&pk, &msg); - let m = scheme.dec(&sk, &cipher); + let m = scheme.dec(&sk, cipher); assert_eq!(msg, m); } } diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index 8a637b3..5454b57 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -425,7 +425,7 @@ impl PKEncryptionScheme for DualRegev { /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { let tmp = (Z::MINUS_ONE * sk) .concat_vertical(&MatZ::identity(1, 1)) .unwrap(); @@ -532,7 +532,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -545,7 +545,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -558,7 +558,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -571,7 +571,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -586,7 +586,7 @@ mod test_dual_regev { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg_mod, m); } @@ -610,7 +610,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -625,7 +625,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -643,7 +643,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index 00b795f..232a9a0 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -437,7 +437,7 @@ impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { let result = &cipher.1 - sk.dot_product(&cipher.0).unwrap(); let result: Z = result.get_representative_least_absolute_residue().abs(); @@ -547,7 +547,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -561,7 +561,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -575,7 +575,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -589,7 +589,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -605,7 +605,7 @@ mod test_dual_regev { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg_mod, m); } @@ -631,7 +631,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -646,7 +646,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -664,7 +664,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index 07e4959..7ac87d9 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -424,11 +424,11 @@ impl PKEncryptionScheme for LPR { /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { let result = (Z::MINUS_ONE * sk.transpose()) .concat_horizontal(&MatZq::identity(1, 1, &self.q)) .unwrap() - .dot_product(cipher) + .dot_product(&cipher) .unwrap(); let result: Z = result.get_representative_least_absolute_residue().abs(); @@ -533,7 +533,7 @@ mod test_lpr { let (pk, sk) = lpr.gen(); let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, &cipher); + let m = lpr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -547,7 +547,7 @@ mod test_lpr { let (pk, sk) = lpr.gen(); let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, &cipher); + let m = lpr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -561,7 +561,7 @@ mod test_lpr { let (pk, sk) = lpr.gen(); let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, &cipher); + let m = lpr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -575,7 +575,7 @@ mod test_lpr { let (pk, sk) = lpr.gen(); let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, &cipher); + let m = lpr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -591,7 +591,7 @@ mod test_lpr { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg_mod, m); } @@ -615,7 +615,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -630,7 +630,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -648,7 +648,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 7d4d9e6..5ead8d1 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -421,11 +421,11 @@ impl PKEncryptionScheme for Regev { /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { let result = (Z::MINUS_ONE * sk) .concat_vertical(&MatZq::identity(1, 1, &self.q)) .unwrap() - .dot_product(cipher) + .dot_product(&cipher) .unwrap(); let result: Z = result.get_representative_least_absolute_residue().abs(); @@ -529,7 +529,7 @@ mod test_regev { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, &cipher); + let m = regev.dec(&sk, cipher); assert_eq!(msg, m); } @@ -542,7 +542,7 @@ mod test_regev { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, &cipher); + let m = regev.dec(&sk, cipher); assert_eq!(msg, m); } @@ -555,7 +555,7 @@ mod test_regev { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, &cipher); + let m = regev.dec(&sk, cipher); assert_eq!(msg, m); } @@ -568,7 +568,7 @@ mod test_regev { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, &cipher); + let m = regev.dec(&sk, cipher); assert_eq!(msg, m); } @@ -583,7 +583,7 @@ mod test_regev { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = regev.enc(&pk, msg); - let m = regev.dec(&sk, &cipher); + let m = regev.dec(&sk, cipher); assert_eq!(msg_mod, m); } @@ -607,7 +607,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -622,7 +622,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -640,7 +640,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index 01b0c19..f5f306c 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -434,7 +434,7 @@ impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { let result = &cipher.1 - sk.dot_product(&cipher.0).unwrap(); let result: Z = result.get_representative_least_absolute_residue().abs(); @@ -543,7 +543,7 @@ mod test_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -557,7 +557,7 @@ mod test_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -571,7 +571,7 @@ mod test_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -585,7 +585,7 @@ mod test_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); + let m = dr.dec(&sk, cipher); assert_eq!(msg, m); } @@ -601,7 +601,7 @@ mod test_regev { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = regev.enc(&pk, msg); - let m = regev.dec(&sk, &cipher); + let m = regev.dec(&sk, cipher); assert_eq!(msg_mod, m); } @@ -627,7 +627,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -642,7 +642,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg, m); } @@ -660,7 +660,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); + let m = scheme.dec_multiple_bits(&sk, cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index b343ef7..d76d93b 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -443,7 +443,7 @@ impl PKEncryptionScheme for RingLPR { /// /// assert_eq!(Z::from(212), m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { // res = v - s * u let result = &cipher.1 - sk * &cipher.0; @@ -547,7 +547,7 @@ mod test_ring_lpr { for message in messages { let cipher = scheme.enc(&pk, message); - let m = scheme.dec(&sk, &cipher); + let m = scheme.dec(&sk, cipher); assert_eq!(Z::from(message), m); } @@ -579,7 +579,7 @@ mod test_ring_lpr { for message in messages { let cipher = scheme.enc(&pk, message); - let m = scheme.dec(&sk, &cipher); + let m = scheme.dec(&sk, cipher); assert_eq!(Z::from(message), m); } @@ -594,7 +594,7 @@ mod test_ring_lpr { for msg in messages { let cipher = scheme.enc(&pk, msg); - let m = scheme.dec(&sk, &cipher); + let m = scheme.dec(&sk, cipher); assert_eq!(Z::ZERO, m); } From 22e2fe5ae891763f968b13f577297772f49635e1 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 9 Dec 2025 15:46:41 +0000 Subject: [PATCH 17/30] Update doc-comments --- src/identity_based_encryption/dual_regev_ibe.rs | 4 ++-- src/pk_encryption/ccs_from_ibe.rs | 4 ++-- src/pk_encryption/dual_regev.rs | 4 ++-- src/pk_encryption/dual_regev_discrete_gauss.rs | 4 ++-- src/pk_encryption/k_pke.rs | 4 ++-- src/pk_encryption/lpr.rs | 4 ++-- src/pk_encryption/regev.rs | 4 ++-- src/pk_encryption/regev_discrete_gauss.rs | 4 ++-- src/pk_encryption/ring_lpr.rs | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index 7698965..8d9dac0 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -56,7 +56,7 @@ use std::collections::HashMap; /// let cipher = ibe.enc(&pk, &identity, &msg); /// /// // decrypt -/// let m = ibe.dec(&id_sk, &cipher); +/// let m = ibe.dec(&id_sk, cipher); /// /// assert_eq!(msg, m) /// ``` @@ -415,7 +415,7 @@ impl IBEScheme for DualRegevIBE { /// let cipher = ibe.enc(&pk, &identity, &msg); /// /// // decrypt - /// let m = ibe.dec(&id_sk, &cipher); + /// let m = ibe.dec(&id_sk, cipher); /// /// assert_eq!(msg, m) /// ``` diff --git a/src/pk_encryption/ccs_from_ibe.rs b/src/pk_encryption/ccs_from_ibe.rs index 3f73b56..9466206 100644 --- a/src/pk_encryption/ccs_from_ibe.rs +++ b/src/pk_encryption/ccs_from_ibe.rs @@ -32,7 +32,7 @@ pub mod dual_regev_ibe_pfdh; /// /// let (pk, sk) = scheme.gen(); /// let cipher = scheme.enc(&pk, 0); -/// let m = scheme.dec(&sk, &cipher); +/// let m = scheme.dec(&sk, cipher); /// /// assert_eq!(Z::ZERO, m); /// ``` @@ -125,7 +125,7 @@ where /// /// let (pk, sk) = scheme.gen(); /// let cipher = scheme.enc(&pk, 1); - /// let m = scheme.dec(&sk, &cipher); + /// let m = scheme.dec(&sk, cipher); /// /// assert_eq!(Z::ONE, m); /// ``` diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index 5454b57..431374f 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = dual_regev.enc(&pk, &msg); /// /// // decrypt -/// let m = dual_regev.dec(&sk, &cipher); +/// let m = dual_regev.dec(&sk, cipher); /// /// assert_eq!(msg, m); /// ``` @@ -421,7 +421,7 @@ impl PKEncryptionScheme for DualRegev { /// let (pk, sk) = dual_regev.gen(); /// let cipher = dual_regev.enc(&pk, 1); /// - /// let m = dual_regev.dec(&sk, &cipher); + /// let m = dual_regev.dec(&sk, cipher); /// /// assert_eq!(Z::ONE, m); /// ``` diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index 232a9a0..535bbfd 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -45,7 +45,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = dual_regev.enc(&pk, &msg); /// /// // decrypt -/// let m = dual_regev.dec(&sk, &cipher); +/// let m = dual_regev.dec(&sk, cipher); /// /// assert_eq!(msg, m); /// ``` @@ -433,7 +433,7 @@ impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { /// let (pk, sk) = dual_regev.gen(); /// let cipher = dual_regev.enc(&pk, 1); /// - /// let m = dual_regev.dec(&sk, &cipher); + /// let m = dual_regev.dec(&sk, cipher); /// /// assert_eq!(Z::ONE, m); /// ``` diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index 001e5bb..e4994f5 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -54,7 +54,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = k_pke.enc(&pk, &msg); /// /// // decrypt the ciphertext -/// let m = k_pke.dec(&sk, &cipher); +/// let m = k_pke.dec(&sk, cipher); /// /// assert_eq!(msg, m); /// ``` @@ -252,7 +252,7 @@ impl PKEncryptionScheme for KPKE { /// let (pk, sk) = k_pke.gen(); /// let c = k_pke.enc(&pk, 1); /// - /// let m = k_pke.dec(&sk, &c); + /// let m = k_pke.dec(&sk, c); /// /// assert_eq!(1, m); /// ``` diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index 7ac87d9..a3b120a 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -41,7 +41,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = lpr.enc(&pk, &msg); /// /// // decrypt -/// let m = lpr.dec(&sk, &cipher); +/// let m = lpr.dec(&sk, cipher); /// /// assert_eq!(msg, m); /// ``` @@ -420,7 +420,7 @@ impl PKEncryptionScheme for LPR { /// let (pk, sk) = lpr.gen(); /// let cipher = lpr.enc(&pk, 1); /// - /// let m = lpr.dec(&sk, &cipher); + /// let m = lpr.dec(&sk, cipher); /// /// assert_eq!(Z::ONE, m); /// ``` diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 5ead8d1..63eb766 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = regev.enc(&pk, &msg); /// /// // decrypt -/// let m = regev.dec(&sk, &cipher); +/// let m = regev.dec(&sk, cipher); /// /// assert_eq!(msg, m); /// ``` @@ -417,7 +417,7 @@ impl PKEncryptionScheme for Regev { /// let (pk, sk) = regev.gen(); /// let cipher = regev.enc(&pk, 1); /// - /// let m = regev.dec(&sk, &cipher); + /// let m = regev.dec(&sk, cipher); /// /// assert_eq!(Z::ONE, m); /// ``` diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index f5f306c..61f9bdb 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -45,7 +45,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = regev.enc(&pk, &msg); /// /// // decrypt -/// let m = regev.dec(&sk, &cipher); +/// let m = regev.dec(&sk, cipher); /// /// assert_eq!(msg, m); /// ``` @@ -430,7 +430,7 @@ impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { /// let (pk, sk) = regev.gen(); /// let cipher = regev.enc(&pk, 1); /// - /// let m = regev.dec(&sk, &cipher); + /// let m = regev.dec(&sk, cipher); /// /// assert_eq!(Z::ONE, m); /// ``` diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index d76d93b..278ec5f 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -49,7 +49,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = lpr.enc(&pk, &msg); /// /// // decrypt -/// let m = lpr.dec(&sk, &cipher); +/// let m = lpr.dec(&sk, cipher); /// /// assert_eq!(msg, m); /// ``` @@ -439,7 +439,7 @@ impl PKEncryptionScheme for RingLPR { /// let (pk, sk) = lpr.gen(); /// let cipher = lpr.enc(&pk, 212); /// - /// let m = lpr.dec(&sk, &cipher); + /// let m = lpr.dec(&sk, cipher); /// /// assert_eq!(Z::from(212), m); /// ``` From 345f9816d540383e54a8f1a5e108b1a93a1fe537 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Wed, 10 Dec 2025 19:57:02 +0000 Subject: [PATCH 18/30] Address changes in tools --- Cargo.toml | 4 ++-- src/pk_encryption/k_pke.rs | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b00961b..927c02d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,8 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-tools = { git = "https://github.com/qfall/tools", branch="update" } -qfall-math = { git = "https://github.com/qfall/math", rev="1ee0b9f41676894d48520322109a3364b8f3338e" } +qfall-tools = { git = "https://github.com/qfall/tools", branch = "update" } +qfall-math = { git = "https://github.com/qfall/math", branch = "dev" } sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} serde_json = "1.0" diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index e4994f5..cb29244 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -14,7 +14,7 @@ use crate::pk_encryption::PKEncryptionScheme; use qfall_math::{ - integer::Z, + integer::{MatPolyOverZ, PolyOverZ, Z}, integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq}, }; use qfall_tools::utils::{ @@ -22,7 +22,7 @@ use qfall_tools::utils::{ decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, }, common_moduli::new_anticyclic, - lossy_compression::LossyCompression, + lossy_compression::LossyCompressionFIPS203, }; use serde::{Deserialize, Serialize}; @@ -112,7 +112,7 @@ impl KPKE { impl PKEncryptionScheme for KPKE { type PublicKey = (MatPolynomialRingZq, MatPolynomialRingZq); type SecretKey = MatPolynomialRingZq; - type Cipher = (MatPolynomialRingZq, PolynomialRingZq); + type Cipher = (MatPolyOverZ, PolyOverZ); /// Generates a `(pk, sk)` pair by following these steps: /// - A <- R_q^{k x k} @@ -217,18 +217,18 @@ impl PKEncryptionScheme for KPKE { .unwrap(); // 19 𝐮 ← NTT^−1(𝐀^⊺ ∘ 𝐲) + 𝐞_𝟏 - let mut vec_u = &pk.0 * &vec_y + vec_e_1; + let vec_u = &pk.0 * &vec_y + vec_e_1; // 20 𝜇 ← Decompress_1(ByteDecode_1(𝑚)) let mu = encode_z_bitwise_in_polynomialringzq(&self.q, &message.into()); // 21 𝑣 ← NTT^−1(𝐭^⊺ ∘ 𝐲) + 𝑒_2 + 𝜇 - let mut v = pk.1.dot_product(&vec_y).unwrap() + e_2 + mu; + let v = pk.1.dot_product(&vec_y).unwrap() + e_2 + mu; // 22: 𝑐_1 ← ByteEncode_{𝑑_𝑢}(Compress_{𝑑_𝑢}(𝐮)) - vec_u.compress(self.d_u); + let vec_u = vec_u.lossy_compress(self.d_u); // 23: 𝑐_2 ← ByteEncode_{𝑑_𝑣}(Compress_{𝑑_𝑣}(𝑣)) - v.compress(self.d_v); + let v = v.lossy_compress(self.d_v); (vec_u, v) } @@ -256,11 +256,11 @@ impl PKEncryptionScheme for KPKE { /// /// assert_eq!(1, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, (mut u, mut v): Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, (u, v): Self::Cipher) -> Z { // 3: 𝐮′ ← Decompress_{𝑑_𝑢}(ByteDecode_{𝑑_𝑢}(𝑐_1)) - u.decompress(self.d_u); + let u = MatPolynomialRingZq::lossy_decompress(&u, self.d_u, &self.q); // 4: 𝑣′ ← Decompress_{𝑑_𝑣}(ByteDecode_{𝑑_𝑣}(𝑐_2)) - v.decompress(self.d_v); + let v = PolynomialRingZq::lossy_decompress(&v, self.d_v, &self.q); // 6 𝑤 ← 𝑣′ − NTT^−1(𝐬^⊺ ∘ NTT(𝐮′)) let w = v - sk.dot_product(&u).unwrap(); From 92fde83a711a4271da504530f5afbb1dcd279c9d Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Thu, 11 Dec 2025 17:27:14 +0000 Subject: [PATCH 19/30] Back to ciphertexts as references --- benches/k_pke.rs | 8 +++---- benches/regev.rs | 2 +- src/identity_based_encryption.rs | 2 +- .../dual_regev_ibe.rs | 16 ++++++------- src/pk_encryption.rs | 6 ++--- src/pk_encryption/ccs_from_ibe.rs | 8 +++---- .../ccs_from_ibe/dual_regev_ibe_pfdh.rs | 4 ++-- src/pk_encryption/dual_regev.rs | 22 ++++++++--------- .../dual_regev_discrete_gauss.rs | 22 ++++++++--------- src/pk_encryption/k_pke.rs | 12 +++++----- src/pk_encryption/lpr.rs | 24 +++++++++---------- src/pk_encryption/regev.rs | 24 +++++++++---------- src/pk_encryption/regev_discrete_gauss.rs | 22 ++++++++--------- src/pk_encryption/ring_lpr.rs | 12 +++++----- 14 files changed, 92 insertions(+), 92 deletions(-) diff --git a/benches/k_pke.rs b/benches/k_pke.rs index ea6dbc7..4925a1b 100644 --- a/benches/k_pke.rs +++ b/benches/k_pke.rs @@ -14,7 +14,7 @@ use qfall_schemes::pk_encryption::KPKE; fn kpke_cycle(k_pke: &KPKE) { let (pk, sk) = k_pke.gen(); let cipher = k_pke.enc(&pk, 1); - let _ = k_pke.dec(&sk, cipher); + let _ = k_pke.dec(&sk, &cipher); } /// Benchmark [kpke_cycle] with [KPKE::ml_kem_512]. @@ -54,7 +54,7 @@ fn bench_kpke_dec_512(c: &mut Criterion) { c.bench_function("K-PKE dec 512", |b| { b.iter_batched( || cipher.clone(), - |cipher| k_pke.dec(&sk, cipher), + |cipher| k_pke.dec(&sk, &cipher), criterion::BatchSize::SmallInput, ) }); @@ -97,7 +97,7 @@ fn bench_kpke_dec_768(c: &mut Criterion) { c.bench_function("K-PKE dec 768", |b| { b.iter_batched( || cipher.clone(), - |cipher| k_pke.dec(&sk, cipher), + |cipher| k_pke.dec(&sk, &cipher), criterion::BatchSize::SmallInput, ) }); @@ -140,7 +140,7 @@ fn bench_kpke_dec_1024(c: &mut Criterion) { c.bench_function("K-PKE dec 1024", |b| { b.iter_batched( || cipher.clone(), - |cipher| k_pke.dec(&sk, cipher), + |cipher| k_pke.dec(&sk, &cipher), criterion::BatchSize::SmallInput, ) }); diff --git a/benches/regev.rs b/benches/regev.rs index d6e75f9..10635a4 100644 --- a/benches/regev.rs +++ b/benches/regev.rs @@ -18,7 +18,7 @@ fn regev_cycle(n: i64) { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let _ = regev.dec(&sk, cipher); + let _ = regev.dec(&sk, &cipher); } /// Benchmark [regev_cycle] with `n = 50`. diff --git a/src/identity_based_encryption.rs b/src/identity_based_encryption.rs index e174f90..5606c3e 100644 --- a/src/identity_based_encryption.rs +++ b/src/identity_based_encryption.rs @@ -78,5 +78,5 @@ pub trait IBEScheme { /// - `cipher`: specifies the ciphertext to be decrypted /// /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z; + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; } diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index 8d9dac0..78cf26e 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -56,7 +56,7 @@ use std::collections::HashMap; /// let cipher = ibe.enc(&pk, &identity, &msg); /// /// // decrypt -/// let m = ibe.dec(&id_sk, cipher); +/// let m = ibe.dec(&id_sk, &cipher); /// /// assert_eq!(msg, m) /// ``` @@ -415,11 +415,11 @@ impl IBEScheme for DualRegevIBE { /// let cipher = ibe.enc(&pk, &identity, &msg); /// /// // decrypt - /// let m = ibe.dec(&id_sk, cipher); + /// let m = ibe.dec(&id_sk, &cipher); /// /// assert_eq!(msg, m) /// ``` - fn dec(&self, sk_id: &Self::SecretKey, cipher: Self::Cipher) -> Z { + fn dec(&self, sk_id: &Self::SecretKey, cipher: &Self::Cipher) -> Z { self.dual_regev.dec(sk_id, cipher) } } @@ -473,7 +473,7 @@ mod test_dual_regev_ibe { let (pk, sk) = cryptosystem.setup(); let id_sk = cryptosystem.extract(&pk, &sk, &id); let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, cipher); + let m = cryptosystem.dec(&id_sk, &cipher); assert_eq!(msg, m) } @@ -489,7 +489,7 @@ mod test_dual_regev_ibe { let (pk, sk) = cryptosystem.setup(); let id_sk = cryptosystem.extract(&pk, &sk, &id); let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, cipher); + let m = cryptosystem.dec(&id_sk, &cipher); assert_eq!(msg, m) } @@ -505,7 +505,7 @@ mod test_dual_regev_ibe { let (pk, sk) = cryptosystem.setup(); let id_sk = cryptosystem.extract(&pk, &sk, &id); let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, cipher); + let m = cryptosystem.dec(&id_sk, &cipher); assert_eq!(msg, m); } @@ -520,7 +520,7 @@ mod test_dual_regev_ibe { let (pk, sk) = cryptosystem.setup(); let id_sk = cryptosystem.extract(&pk, &sk, &id); let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, cipher); + let m = cryptosystem.dec(&id_sk, &cipher); assert_eq!(msg, m); } @@ -540,7 +540,7 @@ mod test_dual_regev_ibe { let id_sk = cryptosystem.extract(&pk, &sk, &id); for _j in 1..=100 { let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, cipher); + let m = cryptosystem.dec(&id_sk, &cipher); assert_eq!(msg, m); } diff --git a/src/pk_encryption.rs b/src/pk_encryption.rs index 590939e..bd12830 100644 --- a/src/pk_encryption.rs +++ b/src/pk_encryption.rs @@ -84,7 +84,7 @@ pub trait PKEncryptionScheme { /// - `cipher`: specifies the ciphertext to be decrypted /// /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z; + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; } /// This trait just exists s.t. we can pass `self` in as mutable for more advanced constructions, which use a storage. @@ -115,7 +115,7 @@ pub trait PKEncryptionSchemeMut { /// - `cipher`: specifies the ciphertext to be decrypted /// /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&mut self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z; + fn dec(&mut self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; } /// This trait generically implements multi-bit encryption @@ -157,7 +157,7 @@ pub trait GenericMultiBitEncryption: PKEncryptionScheme { /// to be decrypted /// /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec_multiple_bits(&self, sk: &Self::SecretKey, cipher: Vec) -> Z { + fn dec_multiple_bits(&self, sk: &Self::SecretKey, cipher: &[Self::Cipher]) -> Z { let mut bits = vec![]; for item in cipher { diff --git a/src/pk_encryption/ccs_from_ibe.rs b/src/pk_encryption/ccs_from_ibe.rs index 9466206..50cc089 100644 --- a/src/pk_encryption/ccs_from_ibe.rs +++ b/src/pk_encryption/ccs_from_ibe.rs @@ -32,7 +32,7 @@ pub mod dual_regev_ibe_pfdh; /// /// let (pk, sk) = scheme.gen(); /// let cipher = scheme.enc(&pk, 0); -/// let m = scheme.dec(&sk, cipher); +/// let m = scheme.dec(&sk, &cipher); /// /// assert_eq!(Z::ZERO, m); /// ``` @@ -125,11 +125,11 @@ where /// /// let (pk, sk) = scheme.gen(); /// let cipher = scheme.enc(&pk, 1); - /// let m = scheme.dec(&sk, cipher); + /// let m = scheme.dec(&sk, &cipher); /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&mut self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { + fn dec(&mut self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { if !self .signature .vfy(cipher.1.to_string(), &cipher.2, &cipher.0) @@ -138,6 +138,6 @@ where } let secret = self.ibe.extract(&sk.0, &sk.1, &cipher.0.clone().into()); - self.ibe.dec(&secret, cipher.1) + self.ibe.dec(&secret, &cipher.1) } } diff --git a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs index d15c21c..b92e38d 100644 --- a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs +++ b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs @@ -108,7 +108,7 @@ mod test_ccs_from_ibe { let (pk, sk) = scheme.gen(); let cipher = scheme.enc(&pk, &msg); - let m = scheme.dec(&sk, cipher); + let m = scheme.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -121,7 +121,7 @@ mod test_ccs_from_ibe { let (pk, sk) = scheme.gen(); let cipher = scheme.enc(&pk, &msg); - let m = scheme.dec(&sk, cipher); + let m = scheme.dec(&sk, &cipher); assert_eq!(msg, m); } } diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index 431374f..8a637b3 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = dual_regev.enc(&pk, &msg); /// /// // decrypt -/// let m = dual_regev.dec(&sk, cipher); +/// let m = dual_regev.dec(&sk, &cipher); /// /// assert_eq!(msg, m); /// ``` @@ -421,11 +421,11 @@ impl PKEncryptionScheme for DualRegev { /// let (pk, sk) = dual_regev.gen(); /// let cipher = dual_regev.enc(&pk, 1); /// - /// let m = dual_regev.dec(&sk, cipher); + /// let m = dual_regev.dec(&sk, &cipher); /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { let tmp = (Z::MINUS_ONE * sk) .concat_vertical(&MatZ::identity(1, 1)) .unwrap(); @@ -532,7 +532,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -545,7 +545,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -558,7 +558,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -571,7 +571,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -586,7 +586,7 @@ mod test_dual_regev { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg_mod, m); } @@ -610,7 +610,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -625,7 +625,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -643,7 +643,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index 535bbfd..00b795f 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -45,7 +45,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = dual_regev.enc(&pk, &msg); /// /// // decrypt -/// let m = dual_regev.dec(&sk, cipher); +/// let m = dual_regev.dec(&sk, &cipher); /// /// assert_eq!(msg, m); /// ``` @@ -433,11 +433,11 @@ impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { /// let (pk, sk) = dual_regev.gen(); /// let cipher = dual_regev.enc(&pk, 1); /// - /// let m = dual_regev.dec(&sk, cipher); + /// let m = dual_regev.dec(&sk, &cipher); /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { let result = &cipher.1 - sk.dot_product(&cipher.0).unwrap(); let result: Z = result.get_representative_least_absolute_residue().abs(); @@ -547,7 +547,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -561,7 +561,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -575,7 +575,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -589,7 +589,7 @@ mod test_dual_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -605,7 +605,7 @@ mod test_dual_regev { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg_mod, m); } @@ -631,7 +631,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -646,7 +646,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -664,7 +664,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index cb29244..f478899 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -54,7 +54,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = k_pke.enc(&pk, &msg); /// /// // decrypt the ciphertext -/// let m = k_pke.dec(&sk, cipher); +/// let m = k_pke.dec(&sk, &cipher); /// /// assert_eq!(msg, m); /// ``` @@ -252,15 +252,15 @@ impl PKEncryptionScheme for KPKE { /// let (pk, sk) = k_pke.gen(); /// let c = k_pke.enc(&pk, 1); /// - /// let m = k_pke.dec(&sk, c); + /// let m = k_pke.dec(&sk, &c); /// /// assert_eq!(1, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, (u, v): Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, (u, v): &Self::Cipher) -> Z { // 3: 𝐮′ ← Decompress_{𝑑_𝑢}(ByteDecode_{𝑑_𝑢}(𝑐_1)) - let u = MatPolynomialRingZq::lossy_decompress(&u, self.d_u, &self.q); + let u = MatPolynomialRingZq::lossy_decompress(u, self.d_u, &self.q); // 4: 𝑣′ ← Decompress_{𝑑_𝑣}(ByteDecode_{𝑑_𝑣}(𝑐_2)) - let v = PolynomialRingZq::lossy_decompress(&v, self.d_v, &self.q); + let v = PolynomialRingZq::lossy_decompress(v, self.d_v, &self.q); // 6 𝑤 ← 𝑣′ − NTT^−1(𝐬^⊺ ∘ NTT(𝐮′)) let w = v - sk.dot_product(&u).unwrap(); @@ -285,7 +285,7 @@ mod test_kpke { for message in messages { let (pk, sk) = k_pke.gen(); let c = k_pke.enc(&pk, message); - let m = k_pke.dec(&sk, c); + let m = k_pke.dec(&sk, &c); assert_eq!(message, m); } diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index a3b120a..07e4959 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -41,7 +41,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = lpr.enc(&pk, &msg); /// /// // decrypt -/// let m = lpr.dec(&sk, cipher); +/// let m = lpr.dec(&sk, &cipher); /// /// assert_eq!(msg, m); /// ``` @@ -420,15 +420,15 @@ impl PKEncryptionScheme for LPR { /// let (pk, sk) = lpr.gen(); /// let cipher = lpr.enc(&pk, 1); /// - /// let m = lpr.dec(&sk, cipher); + /// let m = lpr.dec(&sk, &cipher); /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { let result = (Z::MINUS_ONE * sk.transpose()) .concat_horizontal(&MatZq::identity(1, 1, &self.q)) .unwrap() - .dot_product(&cipher) + .dot_product(cipher) .unwrap(); let result: Z = result.get_representative_least_absolute_residue().abs(); @@ -533,7 +533,7 @@ mod test_lpr { let (pk, sk) = lpr.gen(); let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, cipher); + let m = lpr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -547,7 +547,7 @@ mod test_lpr { let (pk, sk) = lpr.gen(); let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, cipher); + let m = lpr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -561,7 +561,7 @@ mod test_lpr { let (pk, sk) = lpr.gen(); let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, cipher); + let m = lpr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -575,7 +575,7 @@ mod test_lpr { let (pk, sk) = lpr.gen(); let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, cipher); + let m = lpr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -591,7 +591,7 @@ mod test_lpr { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg_mod, m); } @@ -615,7 +615,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -630,7 +630,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -648,7 +648,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 63eb766..7d4d9e6 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = regev.enc(&pk, &msg); /// /// // decrypt -/// let m = regev.dec(&sk, cipher); +/// let m = regev.dec(&sk, &cipher); /// /// assert_eq!(msg, m); /// ``` @@ -417,15 +417,15 @@ impl PKEncryptionScheme for Regev { /// let (pk, sk) = regev.gen(); /// let cipher = regev.enc(&pk, 1); /// - /// let m = regev.dec(&sk, cipher); + /// let m = regev.dec(&sk, &cipher); /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { let result = (Z::MINUS_ONE * sk) .concat_vertical(&MatZq::identity(1, 1, &self.q)) .unwrap() - .dot_product(&cipher) + .dot_product(cipher) .unwrap(); let result: Z = result.get_representative_least_absolute_residue().abs(); @@ -529,7 +529,7 @@ mod test_regev { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, cipher); + let m = regev.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -542,7 +542,7 @@ mod test_regev { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, cipher); + let m = regev.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -555,7 +555,7 @@ mod test_regev { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, cipher); + let m = regev.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -568,7 +568,7 @@ mod test_regev { let (pk, sk) = regev.gen(); let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, cipher); + let m = regev.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -583,7 +583,7 @@ mod test_regev { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = regev.enc(&pk, msg); - let m = regev.dec(&sk, cipher); + let m = regev.dec(&sk, &cipher); assert_eq!(msg_mod, m); } @@ -607,7 +607,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -622,7 +622,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -640,7 +640,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index 61f9bdb..01b0c19 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -45,7 +45,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = regev.enc(&pk, &msg); /// /// // decrypt -/// let m = regev.dec(&sk, cipher); +/// let m = regev.dec(&sk, &cipher); /// /// assert_eq!(msg, m); /// ``` @@ -430,11 +430,11 @@ impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { /// let (pk, sk) = regev.gen(); /// let cipher = regev.enc(&pk, 1); /// - /// let m = regev.dec(&sk, cipher); + /// let m = regev.dec(&sk, &cipher); /// /// assert_eq!(Z::ONE, m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { let result = &cipher.1 - sk.dot_product(&cipher.0).unwrap(); let result: Z = result.get_representative_least_absolute_residue().abs(); @@ -543,7 +543,7 @@ mod test_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -557,7 +557,7 @@ mod test_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -571,7 +571,7 @@ mod test_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -585,7 +585,7 @@ mod test_regev { let (pk, sk) = dr.gen(); let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, cipher); + let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } @@ -601,7 +601,7 @@ mod test_regev { let msg_mod = Z::from(msg.rem_euclid(2)); let cipher = regev.enc(&pk, msg); - let m = regev.dec(&sk, cipher); + let m = regev.dec(&sk, &cipher); assert_eq!(msg_mod, m); } @@ -627,7 +627,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -642,7 +642,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg, m); } @@ -660,7 +660,7 @@ mod test_multi_bits { let (pk, sk) = scheme.gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, cipher); + let m = scheme.dec_multiple_bits(&sk, &cipher); assert_eq!(msg.abs(), m); } diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index 278ec5f..b343ef7 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -49,7 +49,7 @@ use serde::{Deserialize, Serialize}; /// let cipher = lpr.enc(&pk, &msg); /// /// // decrypt -/// let m = lpr.dec(&sk, cipher); +/// let m = lpr.dec(&sk, &cipher); /// /// assert_eq!(msg, m); /// ``` @@ -439,11 +439,11 @@ impl PKEncryptionScheme for RingLPR { /// let (pk, sk) = lpr.gen(); /// let cipher = lpr.enc(&pk, 212); /// - /// let m = lpr.dec(&sk, cipher); + /// let m = lpr.dec(&sk, &cipher); /// /// assert_eq!(Z::from(212), m); /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: Self::Cipher) -> Z { + fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { // res = v - s * u let result = &cipher.1 - sk * &cipher.0; @@ -547,7 +547,7 @@ mod test_ring_lpr { for message in messages { let cipher = scheme.enc(&pk, message); - let m = scheme.dec(&sk, cipher); + let m = scheme.dec(&sk, &cipher); assert_eq!(Z::from(message), m); } @@ -579,7 +579,7 @@ mod test_ring_lpr { for message in messages { let cipher = scheme.enc(&pk, message); - let m = scheme.dec(&sk, cipher); + let m = scheme.dec(&sk, &cipher); assert_eq!(Z::from(message), m); } @@ -594,7 +594,7 @@ mod test_ring_lpr { for msg in messages { let cipher = scheme.enc(&pk, msg); - let m = scheme.dec(&sk, cipher); + let m = scheme.dec(&sk, &cipher); assert_eq!(Z::ZERO, m); } From 3c56a3d555fe23cef023f3be2d1c3be41ef64859 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 15 Dec 2025 09:04:02 +0000 Subject: [PATCH 20/30] Adapt to changes from tools-crate --- Cargo.toml | 2 +- src/pk_encryption/k_pke.rs | 14 +++++++------- src/pk_encryption/ring_lpr.rs | 8 +++----- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 927c02d..5890495 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-tools = { git = "https://github.com/qfall/tools", branch = "update" } +qfall-tools = { git = "https://github.com/qfall/tools", branch = "dev" } qfall-math = { git = "https://github.com/qfall/math", branch = "dev" } sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index f478899..356272b 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -17,12 +17,12 @@ use qfall_math::{ integer::{MatPolyOverZ, PolyOverZ, Z}, integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq}, }; -use qfall_tools::utils::{ - common_encodings::{ - decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, +use qfall_tools::{ + compression::LossyCompressionFIPS203, + utils::{ + common_encodings::{decode_value_from_polynomialringzq, encode_value_in_polynomialringzq}, + common_moduli::new_anticyclic, }, - common_moduli::new_anticyclic, - lossy_compression::LossyCompressionFIPS203, }; use serde::{Deserialize, Serialize}; @@ -220,7 +220,7 @@ impl PKEncryptionScheme for KPKE { let vec_u = &pk.0 * &vec_y + vec_e_1; // 20 𝜇 ← Decompress_1(ByteDecode_1(𝑚)) - let mu = encode_z_bitwise_in_polynomialringzq(&self.q, &message.into()); + let mu = encode_value_in_polynomialringzq(message, 2, &self.q).unwrap(); // 21 𝑣 ← NTT^−1(𝐭^⊺ ∘ 𝐲) + 𝑒_2 + 𝜇 let v = pk.1.dot_product(&vec_y).unwrap() + e_2 + mu; @@ -266,7 +266,7 @@ impl PKEncryptionScheme for KPKE { let w = v - sk.dot_product(&u).unwrap(); // 7 𝑚 ← ByteEncode_1(Compress_1(𝑤)) - decode_z_bitwise_from_polynomialringzq(self.q.get_q(), &w) + decode_value_from_polynomialringzq(&w, 2).unwrap() } } diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index b343ef7..da4b27b 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -18,9 +18,7 @@ use qfall_math::{ traits::Pow, }; use qfall_tools::utils::{ - common_encodings::{ - decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, - }, + common_encodings::{decode_value_from_polynomialringzq, encode_value_in_polynomialringzq}, common_moduli::new_anticyclic, }; use serde::{Deserialize, Serialize}; @@ -396,7 +394,7 @@ impl PKEncryptionScheme for RingLPR { let message: Z = message.into().abs(); let mu = message % Z::from(2).pow(&self.n).unwrap(); // set mu_q_half to polynomial with n {0,1} coefficients - let mu_q_half = encode_z_bitwise_in_polynomialringzq(&self.q, &mu); + let mu_q_half = encode_value_in_polynomialringzq(mu, 2, &self.q).unwrap(); // r <- χ let r = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q()) @@ -447,7 +445,7 @@ impl PKEncryptionScheme for RingLPR { // res = v - s * u let result = &cipher.1 - sk * &cipher.0; - decode_z_bitwise_from_polynomialringzq(self.q.get_q(), &result) + decode_value_from_polynomialringzq(&result, 2).unwrap() } } From 7b9a4aa9cb72f200c0fc9317df6b00187a64db79 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 15 Dec 2025 12:00:31 +0000 Subject: [PATCH 21/30] Add contributing + revised readme --- CONTRIBUTING.md | 69 +++++++++++++++++++++++ README.md | 146 +++++++++++++++++++++++++++++++----------------- 2 files changed, 163 insertions(+), 52 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..80a0c3a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,69 @@ +# Contributing +This library is designed to prototype lattice-based cryptography. Our intent for this library is to be maintained by the community. We encourage anyone to add missing, frequently used features for lattice-based prototyping to this library, and we are happy to help with that process. + +More generally, all contributions such as bugfixes, documentation and tests are welcome. Please go ahead and submit your pull requests. + +## Choosing the right location +The qFALL library is divided into three repositories: [qFALL-math](https://github.com/qfall/math), [qFALL-tools](https://github.com/qfall/tools) and [qFALL-schemes](https://github.com/qfall/schemes). + +Please add new features to one of these repositories, roughly following these guidelines. +- If your feature implements a general mathematical function, then add your code to [qFALL-math](https://github.com/qfall/math). +- If your feature implements a fundamental primitive or shortcut that is commonly used in the construction of lattice-based schemes, e.g., G-trapdoors, then add your code to [qFALL-tools](https://github.com/qfall/tools). +- If you implement a construction, e.g., Kyber, then add your code to [qFALL-schemes](https://github.com/qfall/schemes). + +When in doubt, just submit your pull request to the repository you feel is best suited for your code. We will sort it. + +## Style Guide +Our style guide is based on the [rust standard](https://github.com/rust-lang/rfcs/blob/master/text/0505-api-comment-conventions.md). These rules summarise our style guidelines. +- Every function should be documented. A doc-comment includes a concise description of the function and an example. In case it receives parameters other than `self`, it also includes a description of every parameter, the output type, and behavior. If applicable, it also includes Error and Panic behavior and references to scientific literature. +- If the code of your function is not self-explanatory from your doc-comment, use inline-comments `//` to briefly describe the steps. +- A file should always have the copyright notice at the top, followed by a very brief inner doc-comment to summarise the purpose of this file, grouped up imports, implementations of all features, and finally tests of each feature in a separate test-module with a brief doc-comment for each test. +- Overall, any feature should get a descriptive but concise name s.t. it can be discovered intuitively. +- Code in our library needs to be formatted using `cargo fmt` and satisfy `clippy`'s standards. +- We aim for multiple tests per function, its unforeseen behavior, panic or error-cases to boost confidence in our implementations and ensure that modifications of a function only introduce intended changes of behavior. +- Last but not least, we would like to minimise the number of dependencies of all crates to keep them as slim and quickly compilable as possible. + +## Documentation +The documentation for each crate is available online and it can be generated locally by running the following command in the root directory of this repository. +```bash +cargo doc --open +``` + +Furthermore, here is an example of a doc-comment of a function that follows our guidelines. +```rust +impl Z { + /// Chooses a [`Z`] instance according to the discrete Gaussian distribution + /// in `[center - ⌈6 * s⌉ , center + ⌊6 * s⌋ ]`. + /// + /// This function samples discrete Gaussians according to the definition of + /// SampleZ in [GPV08](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=d9f54077d568784c786f7b1d030b00493eb3ae35). + /// + /// Parameters: + /// - `n`: specifies the range from which is sampled + /// - `center`: specifies the position of the center with peak probability + /// - `s`: specifies the Gaussian parameter, which is proportional + /// to the standard deviation `sigma * sqrt(2 * pi) = s` + /// + /// Returns new [`Z`] sample chosen according to the specified discrete Gaussian + /// distribution or a [`MathError`] if the specified parameters were not chosen + /// appropriately, i.e. `s < 0`. + /// + /// # Examples + /// ``` + /// use qfall_math::integer::Z; + /// + /// let sample = Z::sample_discrete_gauss(0, 1).unwrap(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if `s < 0`. + /// + /// This function implements SampleZ according to: + /// - \[1\] Gentry, Craig and Peikert, Chris and Vaikuntanathan, Vinod (2008). + /// Trapdoors for hard lattices and new cryptographic constructions. + /// In: Proceedings of the fortieth annual ACM symposium on Theory of computing. + /// + pub fn sample_discrete_gauss(center: impl Into, s: impl Into) -> Result {...} +} +``` diff --git a/README.md b/README.md index 95efff6..261f706 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,113 @@ # qFALL-schemes +[github](https://github.com/qfall/schemes) +[crates.io](https://crates.io/crates/qfall-schemes) +[docs.rs](https://docs.rs/qfall-schemes) +[tutorial](https://qfall.github.io/book) +[build](https://github.com/qfall/schemes/actions/workflows/push.yml) +[license](https://github.com/qfall/schemes/blob/dev/LICENSE) -[![made-with-rust](https://img.shields.io/badge/Made%20with-Rust-1f425f.svg)](https://www.rust-lang.org/) -[![CI](https://github.com/qfall/schemes/actions/workflows/push.yml/badge.svg?branch=dev)](https://github.com/qfall/schemes/actions/workflows/pull_request.yml) -[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0) +`qFALL` is a prototyping library for lattice-based cryptography. +This `schemes`-crate collects implementations of lattice-based constructions to reuse them more easily in more complex constructions or protocols. -This repository is currently being developed by the project group [qFALL - quantum resistant fast lattice library](https://cs.uni-paderborn.de/cuk/lehre/veranstaltungen/ws-2022-23/project-group-qfall) in the winter term 2022 and summer term 2023 by the Codes and Cryptography research group in Paderborn. +## Quick-Start +First, ensure that you use a Unix-like distribution (Linux or MacOS). Setup [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) if you're using Windows. This is required due to this crate's dependency on FLINT. +Then, make sure your `rustc --version` is `1.85` or newer. -The main objective of this project is to provide researchers and students with the possibility to easily and quickly prototype (lattice-based) cryptography. +Furthermore, it's required that `m4`, a C-compiler such as `gcc`, and `make` are installed. +```bash +sudo apt-get install m4 gcc make +``` +Then, add you can add this crate to your project by executing the following command. +```bash +cargo add qfall-schemes +``` +- Find further information on [our website](https://qfall.github.io/). Also check out [`qfall-math`](https://crates.io/crates/qfall-math) and [`qfall-tools`](https://crates.io/crates/qfall-tools). +- Read the [documentation of this crate](https://docs.rs/qfall-schemes). +- We recommend [our tutorial](https://qfall.github.io/book) to start working with qFALL. -## Disclaimer +## What does qFALL-schemes offer? +qFALL-schemes collects prototype implementations of lattice-based constructions to reuse them more easily in more complex constructions or protocols. + +List of prototypes available +- [Public Key Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/index.html) + - [LWE Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/struct.Regev.html) + - [Dual LWE Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/struct.DualRegev.html) + - [LPR Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/struct.LPR.html) + - [Ring-based LPR Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/struct.RingLPR.html) + - [K-PKE](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/struct.KPKE.html), which is the foundation of [CRYSTALS-Kyber](https://pq-crystals.org/kyber/) and [ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) + - [CCA-secure Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/struct.CCSfromIBE.html) +- [Signatures](https://docs.rs/qfall-schemes/latest/qfall_schemes/signature/index.html) + - [Full-Domain Hash (FDH)](https://docs.rs/qfall-schemes/latest/qfall_schemes/signature/fdh/struct.FDHGPV.html) + - [Probabilistic FDH (PFDH)](https://docs.rs/qfall-schemes/latest/qfall_schemes/signature/pfdh/struct.PFDHGPV.html) + - [Ring-based FDH](https://docs.rs/qfall-schemes/latest/qfall_schemes/signature/fdh/struct.FDHGPVRing.html) +- [Identity Based Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/identity_based_encryption/index.html) + - [From Dual LWE Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/identity_based_encryption/struct.DualRegevIBE.html) +- [Hash Functions](https://docs.rs/qfall-schemes/latest/qfall_schemes/hash/index.html) + - [SIS-Hash Function](https://docs.rs/qfall-schemes/latest/qfall_schemes/hash/struct.SISHash.html) + - [SHA-256-based Hash](https://docs.rs/qfall-schemes/latest/qfall_schemes/hash/sha256/index.html) + +## Quick Examples +Kyber's Public-Key Encryption +```rust +use qfall_schemes::pk_encryption::{KPKE, PKEncryptionScheme}; +use qfall_math::integer::Z; + +// setup public parameters +let k_pke = KPKE::ml_kem_512(); + +// generate (pk, sk) pair +let (pk, sk) = k_pke.gen(); + +// encrypt a message +let msg = Z::from_uft8("Hello"); +let cipher = k_pke.enc(&pk, &msg); + +// decrypt the ciphertext +let m = k_pke.dec(&sk, &cipher); + +assert_eq!(msg, m); +``` -Currently, we are in the development phase and interfaces might change. -Feel free to check out the current progress, but be aware, that the content will -change in the upcoming weeks and months. An official release will most likely be published in the second half of 2023. +GPV-based Probabilistic Full-Domain Hash +```rust +use qfall_schemes::signature::{pfdh::PFDHGPV, SignatureScheme}; -## Quick-Start +let mut pfdh = PFDHGPV::setup(4, 113, 17, 128); -Please refer to [our website](https://qfall.github.io/) as central information point. +let msg = "Hello World!"; -To install and add our library to your project, please refer to [our tutorial](https://qfall.github.io/book/index.html). -It provides a step-by-step guide to install the required libraries and gives further insights in the usage of our crates. +let (pk, sk) = pfdh.gen(); +let sigma = pfdh.sign(msg.clone(), &sk, &pk); -## What does qFALL-schemes offer? - -qFALL-schemes offers a variety of implementations of cryptographic schemes, constructions, and primitives. -We provide a brief overview in the following list. -For a more detailed description, please refer to [our tutorial section](https://qfall.github.io/book/crypto/features.html). - -Constructions - -- [Public Key Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption.rs) - - [LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/regev.rs) - - [Dual LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/dual_regev.rs) - - [LPR Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/lpr.rs) - - [Ring-based LPR Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/ring_lpr.rs) - - [Prototype of K-PKE](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/k_pke.rs), which is the foundation of [CRYSTALS-Kyber](https://pq-crystals.org/kyber/) and [ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) - - [CCA-secure Encryption](https://github.com/qfall/schemes/blob/dev/src/pk_encryption/ccs_from_ibe.rs) -- [Signatures](https://github.com/qfall/schemes/blob/dev/src/signature.rs) - - [Full-Domain Hash (FDH)](https://github.com/qfall/schemes/blob/dev/src/signature/fdh.rs) - - [Probabilistic FDH (PFDH)](https://github.com/qfall/schemes/blob/dev/src/signature/pfdh.rs) - - [Ring-based FDH](https://github.com/qfall/schemes/blob/dev/src/signature/fdh/gpv_ring.rs) -- [Identity Based Encryption](https://github.com/qfall/schemes/blob/dev/src/identity_based_encryption.rs) - - [From Dual LWE Encryption](https://github.com/qfall/schemes/blob/dev/src/identity_based_encryption/dual_regev_ibe.rs) -- [Hash Functions](https://github.com/qfall/schemes/blob/dev/src/hash.rs) - - [SIS-Hash Function](https://github.com/qfall/schemes/blob/dev/src/hash/sis.rs) - - [SHA-256-based Hash](https://github.com/qfall/schemes/blob/dev/src/hash/sha256.rs) +assert!(pfdh.vfy(msg.clone(), &sigma, &pk)); +``` -## License +## Bugs +Please report bugs through the [GitHub issue tracker](https://github.com/qfall/schemes/issues). -This library is distributed under the **Mozilla Public License Version 2.0** which can be found here [License](https://github.com/qfall/schemes/blob/dev/LICENSE). -Permissions of this weak copyleft license are conditioned on making available source code of licensed files and modifications of those files under the same license (or in certain cases, one of the GNU licenses). Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. However, a larger work using the licensed work may be distributed under different terms and without source code for files added in the larger work. +## Contributions +Contributors are: +- Marvin Beckmann +- Phil Milewski +- Jan Niklas Siemer -## Citing +A few reasons to merge your prototype into qFALL-schemes. +- In case of API changes, a version update of Rust or adapted formatting requirements, prototypes in this crate be kept executable and up-to-date. +- qFALL may benefit from your contribution as most prototypes are built with some optimisation in mind. We may consider integrating your optimisation into [`qfall-math`](https://crates.io/crates/qfall-math) and [`qfall-tools`](https://crates.io/crates/qfall-tools). +- We ensure that prototypes are properly formatted, modularised, and documented before merging s.t. prototypes yield a reusable resource to the community. +- Researchers and developers may benefit from the public exposure of their prototype (and the often associated paper). -Please use the following bibtex entry to cite [qFALL-schemes](https://github.com/qfall/schemes): +See [Contributing](https://github.com/qfall/schemes/blob/dev/CONTRIBUTING.md) for details on how to contribute. +## Citing +Please use the following bibtex entry to cite [qFALL](https://qfall.github.io). ```text -@misc{qFALL-schemes, - author = {Porzenheim, Laurens and Beckmann, Marvin and Kramer, Paul and Milewski, Phil and Moog, Sven and Schmidt, Marcel and Siemer, Niklas}, - title = {qFALL-schemes v0.0}, - howpublished = {Online: \url{https://github.com/qfall/schemes}}, - month = Mar, - year = 2023, - note = {University Paderborn, Codes and Cryptography} -} +TODO: Update to eprint ``` -## Get in Touch +## Dependencies +This project is based on [qfall-math](https://crates.io/crates/qfall-math) and [qfall-tools](https://crates.io/crates/qfall-tools), which build on top of the C-based, optimised math-library [FLINT](https://flintlib.org/). We utilise [serde](https://crates.io/crates/serde) and [serde_json](https://crates.io/crates/serde_json) to (de-)serialize objects to and from JSON. This crate relies on [criterion](https://crates.io/crates/criterion) for benchmarking purposes. An extensive list can be found in our `Cargo.toml` file. -One can contact the members of the project group with our mailing list `pg-qfall(at)lists.upb.de`. +## License +This library is distributed under the [Mozilla Public License Version 2.0](https://github.com/qfall/schemes/blob/dev/LICENSE). +Permissions of this weak copyleft license are conditioned on making the source code of licensed files and modifications of those files available under the same license (or in certain cases, under one of the GNU licenses). Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. However, a larger work using the licensed work may be distributed under different terms and without source code for files added to the larger work. From bb489485577ca46678596ebb2b5e192264abdcbd Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 15 Dec 2025 12:31:11 +0000 Subject: [PATCH 22/30] Update Cargo.toml --- Cargo.toml | 10 +++- README.md | 9 +++- benches/k_pke.rs | 30 ++++++------ benches/pfdh.rs | 8 ++-- benches/regev.rs | 4 +- src/hash/sha256.rs | 2 +- src/hash/sis.rs | 48 +++++++++---------- .../dual_regev_ibe.rs | 8 ++-- src/pk_encryption.rs | 4 +- src/pk_encryption/ccs_from_ibe.rs | 14 +++--- .../ccs_from_ibe/dual_regev_ibe_pfdh.rs | 8 ++-- src/pk_encryption/dual_regev.rs | 42 ++++++++-------- .../dual_regev_discrete_gauss.rs | 36 +++++++------- src/pk_encryption/k_pke.rs | 16 +++---- src/pk_encryption/lpr.rs | 38 +++++++-------- src/pk_encryption/regev.rs | 42 ++++++++-------- src/pk_encryption/regev_discrete_gauss.rs | 36 +++++++------- src/pk_encryption/ring_lpr.rs | 20 ++++---- src/signature.rs | 4 +- src/signature/fdh.rs | 2 +- src/signature/fdh/gpv.rs | 14 +++--- src/signature/fdh/gpv_ring.rs | 16 +++---- src/signature/pfdh.rs | 2 +- src/signature/pfdh/gpv.rs | 10 ++-- 24 files changed, 220 insertions(+), 203 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5890495..79014b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,15 @@ [package] name = "qfall-schemes" version = "0.1.0" -edition = "2021" +edition = "2024" +rust-version = "1.85" # due to rand and rand_distr dependency +description = "Collection of prototype implementations of lattice-based cryptography" +readme = "README.md" +homepage = "https://qfall.github.io" +repository = "https://github.com/qfall/schemes" +license = "MPL-2.0" +keywords = ["prototype", "lattice", "cryptography"] +categories = ["cryptography", "mathematics", "development-tools::build-utils", "development-tools::testing", "development-tools::profiling"] autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 261f706..3460945 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ use qfall_math::integer::Z; let k_pke = KPKE::ml_kem_512(); // generate (pk, sk) pair -let (pk, sk) = k_pke.gen(); +let (pk, sk) = k_pke.key_gen(); // encrypt a message let msg = Z::from_uft8("Hello"); @@ -76,12 +76,17 @@ let mut pfdh = PFDHGPV::setup(4, 113, 17, 128); let msg = "Hello World!"; -let (pk, sk) = pfdh.gen(); +let (pk, sk) = pfdh.key_gen(); let sigma = pfdh.sign(msg.clone(), &sk, &pk); assert!(pfdh.vfy(msg.clone(), &sigma, &pk)); ``` +## SemVer and Backward Compatibility +As initial implementations of traits and prototypes can sometimes be optimized by changing the API, we give no API/interface stability guarantees for this crate. +We try to be mindful but we may reorganize code without warning in advance. +Therefore, it is recommended to fix the used version `version = "=x.y.z"` in your `Cargo.toml`. + ## Bugs Please report bugs through the [GitHub issue tracker](https://github.com/qfall/schemes/issues). diff --git a/benches/k_pke.rs b/benches/k_pke.rs index 4925a1b..862c526 100644 --- a/benches/k_pke.rs +++ b/benches/k_pke.rs @@ -7,12 +7,12 @@ // Mozilla Foundation. See . use criterion::*; -use qfall_schemes::pk_encryption::PKEncryptionScheme; use qfall_schemes::pk_encryption::KPKE; +use qfall_schemes::pk_encryption::PKEncryptionScheme; -/// Performs a full-cycle of gen, enc, dec with [`KPKE`]. +/// Performs a full-cycle of key_gen, enc, dec with [`KPKE`]. fn kpke_cycle(k_pke: &KPKE) { - let (pk, sk) = k_pke.gen(); + let (pk, sk) = k_pke.key_gen(); let cipher = k_pke.enc(&pk, 1); let _ = k_pke.dec(&sk, &cipher); } @@ -29,17 +29,17 @@ fn bench_kpke_cycle_512(c: &mut Criterion) { c.bench_function("K-PKE cycle 512", |b| b.iter(|| kpke_cycle(&k_pke))); } -/// Benchmark [KPKE::gen] with [KPKE::ml_kem_512]. +/// Benchmark [KPKE::key_gen] with [KPKE::ml_kem_512]. fn bench_kpke_gen_512(c: &mut Criterion) { let k_pke = KPKE::ml_kem_512(); - c.bench_function("K-PKE gen 512", |b| b.iter(|| k_pke.gen())); + c.bench_function("K-PKE key_gen 512", |b| b.iter(|| k_pke.key_gen())); } /// Benchmark [KPKE::enc] with [KPKE::ml_kem_512]. fn bench_kpke_enc_512(c: &mut Criterion) { let k_pke = KPKE::ml_kem_512(); - let (pk, _) = k_pke.gen(); + let (pk, _) = k_pke.key_gen(); let msg = i64::MAX; c.bench_function("K-PKE enc 512", |b| b.iter(|| k_pke.enc(&pk, msg))); @@ -48,7 +48,7 @@ fn bench_kpke_enc_512(c: &mut Criterion) { /// Benchmark [KPKE::dec] with [KPKE::ml_kem_512]. fn bench_kpke_dec_512(c: &mut Criterion) { let k_pke = KPKE::ml_kem_512(); - let (pk, sk) = k_pke.gen(); + let (pk, sk) = k_pke.key_gen(); let cipher = k_pke.enc(&pk, i64::MAX); c.bench_function("K-PKE dec 512", |b| { @@ -72,17 +72,17 @@ fn bench_kpke_cycle_768(c: &mut Criterion) { c.bench_function("K-PKE cycle 768", |b| b.iter(|| kpke_cycle(&k_pke))); } -/// Benchmark [KPKE::gen] with [KPKE::ml_kem_768]. +/// Benchmark [KPKE::key_gen] with [KPKE::ml_kem_768]. fn bench_kpke_gen_768(c: &mut Criterion) { let k_pke = KPKE::ml_kem_768(); - c.bench_function("K-PKE gen 768", |b| b.iter(|| k_pke.gen())); + c.bench_function("K-PKE key_gen 768", |b| b.iter(|| k_pke.key_gen())); } /// Benchmark [KPKE::enc] with [KPKE::ml_kem_768]. fn bench_kpke_enc_768(c: &mut Criterion) { let k_pke = KPKE::ml_kem_768(); - let (pk, _) = k_pke.gen(); + let (pk, _) = k_pke.key_gen(); let msg = i64::MAX; c.bench_function("K-PKE enc 768", |b| b.iter(|| k_pke.enc(&pk, msg))); @@ -91,7 +91,7 @@ fn bench_kpke_enc_768(c: &mut Criterion) { /// Benchmark [KPKE::dec] with [KPKE::ml_kem_768]. fn bench_kpke_dec_768(c: &mut Criterion) { let k_pke = KPKE::ml_kem_768(); - let (pk, sk) = k_pke.gen(); + let (pk, sk) = k_pke.key_gen(); let cipher = k_pke.enc(&pk, i64::MAX); c.bench_function("K-PKE dec 768", |b| { @@ -115,17 +115,17 @@ fn bench_kpke_cycle_1024(c: &mut Criterion) { c.bench_function("K-PKE cycle 1024", |b| b.iter(|| kpke_cycle(&k_pke))); } -/// Benchmark [KPKE::gen] with [KPKE::ml_kem_1024]. +/// Benchmark [KPKE::key_gen] with [KPKE::ml_kem_1024]. fn bench_kpke_gen_1024(c: &mut Criterion) { let k_pke = KPKE::ml_kem_1024(); - c.bench_function("K-PKE gen 1024", |b| b.iter(|| k_pke.gen())); + c.bench_function("K-PKE key_gen 1024", |b| b.iter(|| k_pke.key_gen())); } /// Benchmark [KPKE::enc] with [KPKE::ml_kem_1024]. fn bench_kpke_enc_1024(c: &mut Criterion) { let k_pke = KPKE::ml_kem_1024(); - let (pk, _) = k_pke.gen(); + let (pk, _) = k_pke.key_gen(); let msg = i64::MAX; c.bench_function("K-PKE enc 1024", |b| b.iter(|| k_pke.enc(&pk, msg))); @@ -134,7 +134,7 @@ fn bench_kpke_enc_1024(c: &mut Criterion) { /// Benchmark [KPKE::dec] with [KPKE::ml_kem_1024]. fn bench_kpke_dec_1024(c: &mut Criterion) { let k_pke = KPKE::ml_kem_1024(); - let (pk, sk) = k_pke.gen(); + let (pk, sk) = k_pke.key_gen(); let cipher = k_pke.enc(&pk, i64::MAX); c.bench_function("K-PKE dec 1024", |b| { diff --git a/benches/pfdh.rs b/benches/pfdh.rs index 5dcc793..ba1c5d3 100644 --- a/benches/pfdh.rs +++ b/benches/pfdh.rs @@ -6,8 +6,8 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -use criterion::{criterion_group, Criterion}; -use qfall_schemes::signature::{pfdh::PFDHGPV, SignatureScheme}; +use criterion::{Criterion, criterion_group}; +use qfall_schemes::signature::{SignatureScheme, pfdh::PFDHGPV}; /// Performs a full instantiation with an additional signing and verifying of a signature. fn pfdh_cycle(n: i64) { @@ -15,7 +15,7 @@ fn pfdh_cycle(n: i64) { let m = "Hello World!"; - let (pk, sk) = pfdh.gen(); + let (pk, sk) = pfdh.key_gen(); let sigma = pfdh.sign(m.to_owned(), &sk, &pk); pfdh.vfy(m.to_owned(), &sigma, &pk); @@ -50,7 +50,7 @@ fn bench_pfdh_signature(c: &mut Criterion) { let m = "Hello World!"; - let (pk, sk) = pfdh.gen(); + let (pk, sk) = pfdh.key_gen(); c.bench_function("Signing PFDH n=8", |b| { b.iter(|| pfdh.sign(m.to_owned(), &sk, &pk)) diff --git a/benches/regev.rs b/benches/regev.rs index 10635a4..246e2c9 100644 --- a/benches/regev.rs +++ b/benches/regev.rs @@ -11,12 +11,12 @@ use qfall_math::integer::Z; use qfall_schemes::pk_encryption::PKEncryptionScheme; use qfall_schemes::pk_encryption::Regev; -/// Performs a full-cycle of gen, enc, dec with regev. +/// Performs a full-cycle of key_gen, enc, dec with regev. fn regev_cycle(n: i64) { let msg = Z::ONE; let regev = Regev::new_from_n(n); - let (pk, sk) = regev.gen(); + let (pk, sk) = regev.key_gen(); let cipher = regev.enc(&pk, &msg); let _ = regev.dec(&sk, &cipher); } diff --git a/src/hash/sha256.rs b/src/hash/sha256.rs index 1a6c8a3..ec89fed 100644 --- a/src/hash/sha256.rs +++ b/src/hash/sha256.rs @@ -260,7 +260,7 @@ impl HashInto for HashMatPolynomialRingZq { #[cfg(test)] mod tests_sha { - use super::{hash_to_mat_zq_sha256, hash_to_zq_sha256, sha256, Z}; + use super::{Z, hash_to_mat_zq_sha256, hash_to_zq_sha256, sha256}; use qfall_math::{ integer_mod_q::{MatZq, Zq}, traits::{Distance, Pow}, diff --git a/src/hash/sis.rs b/src/hash/sis.rs index 616e570..e2211af 100644 --- a/src/hash/sis.rs +++ b/src/hash/sis.rs @@ -28,7 +28,7 @@ use serde::{Deserialize, Serialize}; /// use qfall_schemes::hash::SISHash; /// use qfall_math::integer_mod_q::MatZq; /// // setup public parameters and key pair -/// let hash = SISHash::gen(5, 18, 11).unwrap(); +/// let hash = SISHash::key_gen(5, 18, 11).unwrap(); /// /// // check provable collision-resistance of hash /// assert!(hash.check_security().is_ok()); @@ -61,13 +61,13 @@ impl SISHash { /// ``` /// use qfall_schemes::hash::SISHash; /// - /// let hash = SISHash::gen(5, 18, 11).unwrap(); + /// let hash = SISHash::key_gen(5, 18, 11).unwrap(); /// ``` /// /// # Errors and Failures /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) /// if `n <= 0`. - pub fn gen(n: impl Into, m: impl Into, q: impl Into) -> Result { + pub fn key_gen(n: impl Into, m: impl Into, q: impl Into) -> Result { let n: Z = n.into(); let m: Z = m.into(); let q: Z = q.into(); @@ -93,7 +93,7 @@ impl SISHash { /// # Examples /// ``` /// use qfall_schemes::hash::SISHash; - /// let hash = SISHash::gen(5, 18, 11).unwrap(); + /// let hash = SISHash::key_gen(5, 18, 11).unwrap(); /// /// assert!(hash.check_security().is_ok()); /// ``` @@ -139,7 +139,7 @@ impl SISHash { /// use qfall_schemes::hash::SISHash; /// use qfall_math::integer_mod_q::MatZq; /// use std::str::FromStr; - /// let hash = SISHash::gen(1, 3, 7).unwrap(); + /// let hash = SISHash::key_gen(1, 3, 7).unwrap(); /// let value = MatZq::from_str("[[1],[2],[3]] mod 7").unwrap(); /// /// hash.hash(&value); @@ -166,9 +166,9 @@ mod test_gen { /// Checks whether too small chosen `n` results in an error. #[test] fn invalid_n() { - let res_0 = SISHash::gen(0, 2, 2); - let res_1 = SISHash::gen(-1, 2, 2); - let res_2 = SISHash::gen(i64::MIN, 2, 2); + let res_0 = SISHash::key_gen(0, 2, 2); + let res_1 = SISHash::key_gen(-1, 2, 2); + let res_2 = SISHash::key_gen(i64::MIN, 2, 2); assert!(res_0.is_err()); assert!(res_1.is_err()); @@ -178,9 +178,9 @@ mod test_gen { /// Checks whether too small chosen `m` results in an error in the security check. #[test] fn insecure_m() { - let res_0 = SISHash::gen(1, 1, 4).unwrap(); - let res_1 = SISHash::gen(2, 2, 2).unwrap(); - let res_2 = SISHash::gen(4, 5, i64::MAX).unwrap(); + let res_0 = SISHash::key_gen(1, 1, 4).unwrap(); + let res_1 = SISHash::key_gen(2, 2, 2).unwrap(); + let res_2 = SISHash::key_gen(4, 5, i64::MAX).unwrap(); assert!(res_0.check_security().is_err()); assert!(res_1.check_security().is_err()); @@ -190,8 +190,8 @@ mod test_gen { /// Checks whether too small chosen `q` results in an error in the security check. #[test] fn insecure_q() { - let res_0 = SISHash::gen(10, 50, 6).unwrap(); - let res_1 = SISHash::gen(5, 50, 4).unwrap(); + let res_0 = SISHash::key_gen(10, 50, 6).unwrap(); + let res_1 = SISHash::key_gen(5, 50, 4).unwrap(); assert!(res_0.check_security().is_err()); assert!(res_1.check_security().is_err()); @@ -200,7 +200,7 @@ mod test_gen { /// Ensures that a working example returns a proper instance. #[test] fn working_example() { - let hash = SISHash::gen(5, 18, 11).unwrap(); + let hash = SISHash::key_gen(5, 18, 11).unwrap(); assert!(hash.check_security().is_ok()); assert_eq!(5, hash.key.get_num_rows()); @@ -211,12 +211,12 @@ mod test_gen { /// Ensures that the expected availability is provided. #[test] fn availability() { - let _ = SISHash::gen(4i8, 4i8, 4i8); - let _ = SISHash::gen(4i8, 4i16, 4i32); - let _ = SISHash::gen(4u8, 4i64, 4u16); - let _ = SISHash::gen(4u64, 4u32, 4); - let _ = SISHash::gen(Z::ONE, 4i64, 4u16); - let _ = SISHash::gen(Z::ONE, Z::from(2), Z::from(2)); + let _ = SISHash::key_gen(4i8, 4i8, 4i8); + let _ = SISHash::key_gen(4i8, 4i16, 4i32); + let _ = SISHash::key_gen(4u8, 4i64, 4u16); + let _ = SISHash::key_gen(4u64, 4u32, 4); + let _ = SISHash::key_gen(Z::ONE, 4i64, 4u16); + let _ = SISHash::key_gen(Z::ONE, Z::from(2), Z::from(2)); } } @@ -229,7 +229,7 @@ mod test_hash { #[should_panic] #[test] fn not_column_vec() { - let hash = SISHash::gen(1, 3, 7).unwrap(); + let hash = SISHash::key_gen(1, 3, 7).unwrap(); let value = MatZq::new(1, 3, 7); hash.hash(&value); @@ -239,7 +239,7 @@ mod test_hash { #[should_panic] #[test] fn mismatching_dimensions() { - let hash = SISHash::gen(1, 3, 7).unwrap(); + let hash = SISHash::key_gen(1, 3, 7).unwrap(); let value = MatZq::new(4, 1, 7); hash.hash(&value); @@ -249,7 +249,7 @@ mod test_hash { #[should_panic] #[test] fn mismatching_moduli() { - let hash = SISHash::gen(1, 3, 7).unwrap(); + let hash = SISHash::key_gen(1, 3, 7).unwrap(); let value = MatZq::new(3, 1, 8); hash.hash(&value); @@ -258,7 +258,7 @@ mod test_hash { /// Ensures that a working example returns a proper instance. #[test] fn working_example() { - let hash = SISHash::gen(5, 18, 11).unwrap(); + let hash = SISHash::key_gen(5, 18, 11).unwrap(); let value = MatZq::new(18, 1, 11); let res = hash.hash(&value); diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index 78cf26e..d417771 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -462,7 +462,7 @@ mod test_dual_regev_ibe { DualRegevIBE::new_from_n(1); } - /// Checks whether the full-cycle of gen, extract, enc, dec works properly + /// Checks whether the full-cycle of key_gen, extract, enc, dec works properly /// for message 0 and the default. #[test] fn cycle_zero_default() { @@ -478,7 +478,7 @@ mod test_dual_regev_ibe { assert_eq!(msg, m) } - /// Checks whether the full-cycle of gen, extract, enc, dec works properly + /// Checks whether the full-cycle of key_gen, extract, enc, dec works properly /// for message 1 and the default. #[test] fn cycle_one_default() { @@ -494,7 +494,7 @@ mod test_dual_regev_ibe { assert_eq!(msg, m) } - /// Checks whether the full-cycle of gen, extract, enc, dec works properly + /// Checks whether the full-cycle of key_gen, extract, enc, dec works properly /// for message 0 and small n. #[test] fn cycle_zero_small_n() { @@ -509,7 +509,7 @@ mod test_dual_regev_ibe { assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, extract, enc, dec works properly + /// Checks whether the full-cycle of key_gen, extract, enc, dec works properly /// for message 1 and small n. #[test] fn cycle_one_small_n() { diff --git a/src/pk_encryption.rs b/src/pk_encryption.rs index bd12830..da51c03 100644 --- a/src/pk_encryption.rs +++ b/src/pk_encryption.rs @@ -66,7 +66,7 @@ pub trait PKEncryptionScheme { /// Generates a public key pair `(pk, sk)` suitable for the specific scheme. /// /// Returns a tuple `(pk, sk)` consisting of [`Self::PublicKey`] and [`Self::SecretKey`]. - fn gen(&self) -> (Self::PublicKey, Self::SecretKey); + fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey); /// Encrypts the provided `message` using the public key `pk`. /// @@ -97,7 +97,7 @@ pub trait PKEncryptionSchemeMut { /// Generates a public key pair `(pk, sk)` suitable for the specific scheme. /// /// Returns a tuple `(pk, sk)` consisting of [`Self::PublicKey`] and [`Self::SecretKey`]. - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey); + fn key_gen(&mut self) -> (Self::PublicKey, Self::SecretKey); /// Encrypts the provided `message` using the public key `pk`. /// diff --git a/src/pk_encryption/ccs_from_ibe.rs b/src/pk_encryption/ccs_from_ibe.rs index 50cc089..e50cd0f 100644 --- a/src/pk_encryption/ccs_from_ibe.rs +++ b/src/pk_encryption/ccs_from_ibe.rs @@ -30,7 +30,7 @@ pub mod dual_regev_ibe_pfdh; /// use qfall_math::integer::Z; /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); /// -/// let (pk, sk) = scheme.gen(); +/// let (pk, sk) = scheme.key_gen(); /// let cipher = scheme.enc(&pk, 0); /// let m = scheme.dec(&sk, &cipher); /// @@ -68,15 +68,15 @@ where /// use qfall_schemes::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); /// - /// let (pk, sk) = scheme.gen(); + /// let (pk, sk) = scheme.key_gen(); /// ``` - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { let (pk, sk) = self.ibe.setup(); (pk.clone(), (pk, sk)) } /// Generates an encryption of `message` for the provided public key by following these steps: - /// - (vrfy_key, sign_key) = signature.gen() + /// - (vrfy_key, sign_key) = signature.key_gen() /// - c = ibe.enc(mpk, vrfy_key, message), i.e. encrypt `message` with respect to identity `vrfy_key` /// - sigma = signature.sign(c, sign_key, vrfy_key), i.e. sign message `c` /// @@ -93,11 +93,11 @@ where /// use qfall_schemes::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); /// - /// let (pk, sk) = scheme.gen(); + /// let (pk, sk) = scheme.key_gen(); /// let cipher = scheme.enc(&pk, 1); /// ``` fn enc(&mut self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - let (vrfy_key, sign_key) = self.signature.gen(); + let (vrfy_key, sign_key) = self.signature.key_gen(); let c = self.ibe.enc(pk, &vrfy_key.clone().into(), message); let sigma = self.signature.sign(c.to_string(), &sign_key, &vrfy_key); @@ -123,7 +123,7 @@ where /// use qfall_math::integer::Z; /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); /// - /// let (pk, sk) = scheme.gen(); + /// let (pk, sk) = scheme.key_gen(); /// let cipher = scheme.enc(&pk, 1); /// let m = scheme.dec(&sk, &cipher); /// diff --git a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs index b92e38d..50ca8be 100644 --- a/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs +++ b/src/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs @@ -99,27 +99,27 @@ mod test_ccs_from_ibe { use crate::pk_encryption::PKEncryptionSchemeMut; use qfall_math::integer::Z; - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and small n. #[test] fn cycle_zero() { let msg = Z::ZERO; let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc(&pk, &msg); let m = scheme.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and small n. #[test] fn cycle_one() { let msg = Z::ONE; let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc(&pk, &msg); let m = scheme.dec(&sk, &cipher); assert_eq!(msg, m); diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index 8a637b3..a177a4b 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -35,7 +35,7 @@ use serde::{Deserialize, Serialize}; /// use qfall_math::integer::Z; /// // setup public parameters and key pair /// let dual_regev = DualRegev::default(); -/// let (pk, sk) = dual_regev.gen(); +/// let (pk, sk) = dual_regev.key_gen(); /// /// // encrypt a bit /// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 @@ -122,7 +122,9 @@ impl DualRegev { pub fn new_from_n(n: impl Into) -> Self { let n = n.into(); if n < 10 { - panic!("Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise."); + panic!( + "Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise." + ); } let mut m: Z; @@ -238,13 +240,13 @@ impl DualRegev { // α = o (1 / ( sqrt(n) * log n ) ) if self.alpha > 1 / (self.n.sqrt() * self.n.log(2).unwrap()) { return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log n), but α < 1 / (sqrt(n) * log n) is required." + "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log n), but α < 1 / (sqrt(n) * log n) is required.", ))); } // concentration bound with r=5 -> r * sqrt(m) * α > q/4 if 20 * self.m.sqrt() * &self.alpha > q { return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as 5 * sqrt(m) * α > q/4, but 5 * sqrt(m) * α <= q/4 is required." + "Correctness is not guaranteed as 5 * sqrt(m) * α > q/4, but 5 * sqrt(m) * α <= q/4 is required.", ))); } @@ -334,9 +336,9 @@ impl PKEncryptionScheme for DualRegev { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegev}; /// let dual_regev = DualRegev::default(); /// - /// let (pk, sk) = dual_regev.gen(); + /// let (pk, sk) = dual_regev.key_gen(); /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) { // A <- Z_q^{n x m} let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); // x <- Z_2^m @@ -370,7 +372,7 @@ impl PKEncryptionScheme for DualRegev { /// ``` /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegev}; /// let dual_regev = DualRegev::default(); - /// let (pk, sk) = dual_regev.gen(); + /// let (pk, sk) = dual_regev.key_gen(); /// /// let cipher = dual_regev.enc(&pk, 1); /// ``` @@ -418,7 +420,7 @@ impl PKEncryptionScheme for DualRegev { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegev}; /// use qfall_math::integer::Z; /// let dual_regev = DualRegev::default(); - /// let (pk, sk) = dual_regev.gen(); + /// let (pk, sk) = dual_regev.key_gen(); /// let cipher = dual_regev.enc(&pk, 1); /// /// let m = dual_regev.dec(&sk, &cipher); @@ -523,53 +525,53 @@ mod test_dual_regev { use crate::pk_encryption::PKEncryptionScheme; use qfall_math::integer::Z; - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and small n. #[test] fn cycle_zero_small_n() { let msg = Z::ZERO; let dr = DualRegev::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and small n. #[test] fn cycle_one_small_n() { let msg = Z::ONE; let dr = DualRegev::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and larger n. #[test] fn cycle_zero_large_n() { let msg = Z::ZERO; let dr = DualRegev::new_from_n(50); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and larger n. #[test] fn cycle_one_large_n() { let msg = Z::ONE; let dr = DualRegev::new_from_n(50); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); @@ -580,7 +582,7 @@ mod test_dual_regev { fn modulus_application() { let messages = [2, 3, i64::MAX, i64::MIN]; let dr = DualRegev::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); for msg in messages { let msg_mod = Z::from(msg.rem_euclid(2)); @@ -608,7 +610,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = DualRegev::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -623,7 +625,7 @@ mod test_multi_bits { let msg = Z::ZERO; let scheme = DualRegev::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -641,7 +643,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = DualRegev::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index 00b795f..8930259 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -38,7 +38,7 @@ use serde::{Deserialize, Serialize}; /// use qfall_math::integer::Z; /// // setup public parameters and key pair /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); -/// let (pk, sk) = dual_regev.gen(); +/// let (pk, sk) = dual_regev.key_gen(); /// /// // encrypt a bit /// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 @@ -306,7 +306,7 @@ impl DualRegevWithDiscreteGaussianRegularity { // r >= ω( sqrt( log m ) ) if self.r < self.m.log(2).unwrap().sqrt() { return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as r < sqrt( log m ) and r >= ω(sqrt(log m)) is required." + "Security is not guaranteed as r < sqrt( log m ) and r >= ω(sqrt(log m)) is required.", ))); } @@ -355,9 +355,9 @@ impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); /// - /// let (pk, sk) = dual_regev.gen(); + /// let (pk, sk) = dual_regev.key_gen(); /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) { // e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r let vec_e = MatZq::sample_discrete_gauss(&self.m, 1, &self.q, 0, &self.r).unwrap(); // A <- Z_q^{n x m} @@ -388,7 +388,7 @@ impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { /// ``` /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); - /// let (pk, sk) = dual_regev.gen(); + /// let (pk, sk) = dual_regev.key_gen(); /// /// let cipher = dual_regev.enc(&pk, 1); /// ``` @@ -430,7 +430,7 @@ impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; /// use qfall_math::integer::Z; /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); - /// let (pk, sk) = dual_regev.gen(); + /// let (pk, sk) = dual_regev.key_gen(); /// let cipher = dual_regev.enc(&pk, 1); /// /// let m = dual_regev.dec(&sk, &cipher); @@ -538,56 +538,56 @@ mod test_dual_regev { use crate::pk_encryption::PKEncryptionScheme; use qfall_math::integer::Z; - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and small n. #[test] fn cycle_zero_small_n() { let msg = Z::ZERO; let dr = DualRegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and small n. #[test] fn cycle_one_small_n() { let msg = Z::ONE; let dr = DualRegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and larger n. #[test] fn cycle_zero_large_n() { let msg = Z::ZERO; let dr = DualRegevWithDiscreteGaussianRegularity::new_from_n(30); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and larger n. #[test] fn cycle_one_large_n() { let msg = Z::ONE; let dr = DualRegevWithDiscreteGaussianRegularity::new_from_n(30); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); @@ -599,7 +599,7 @@ mod test_dual_regev { fn modulus_application() { let messages = [2, 3, i64::MAX, i64::MIN]; let dr = DualRegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); for msg in messages { let msg_mod = Z::from(msg.rem_euclid(2)); @@ -629,7 +629,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = DualRegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -644,7 +644,7 @@ mod test_multi_bits { let msg = Z::ZERO; let scheme = DualRegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -662,7 +662,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = DualRegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index 356272b..3ba3d9f 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -47,7 +47,7 @@ use serde::{Deserialize, Serialize}; /// let k_pke = KPKE::ml_kem_512(); /// /// // generate (pk, sk) pair -/// let (pk, sk) = k_pke.gen(); +/// let (pk, sk) = k_pke.key_gen(); /// /// // encrypt a message /// let msg = 250; @@ -62,7 +62,7 @@ use serde::{Deserialize, Serialize}; pub struct KPKE { q: ModulusPolynomialRingZq, // modulus (X^n + 1) mod p k: i64, // defines both dimensions of matrix A - eta_1: i64, // defines the binomial distribution of the secret and error drawn in `gen` + eta_1: i64, // defines the binomial distribution of the secret and error drawn in `key_gen` eta_2: i64, // defines the binomial distribution of the error drawn in `enc` d_u: i64, // defines the number of kept upper-order bits per entry of vector `u` d_v: i64, // defines the number of kept upper-order bits per entry of `v` @@ -127,9 +127,9 @@ impl PKEncryptionScheme for KPKE { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, KPKE}; /// let k_pke = KPKE::ml_kem_512(); /// - /// let (pk, sk) = k_pke.gen(); + /// let (pk, sk) = k_pke.key_gen(); /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) { // 5 𝐀[𝑖,𝑗] ← SampleNTT(𝜌‖𝑗‖𝑖) // Reminder: NTT-representation, sampling and multiplication are not part of this prototype let mat_a = MatPolynomialRingZq::sample_uniform(self.k, self.k, &self.q); @@ -182,7 +182,7 @@ impl PKEncryptionScheme for KPKE { /// ``` /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, KPKE}; /// let k_pke = KPKE::ml_kem_512(); - /// let (pk, sk) = k_pke.gen(); + /// let (pk, sk) = k_pke.key_gen(); /// /// let c = k_pke.enc(&pk, 1); /// ``` @@ -249,7 +249,7 @@ impl PKEncryptionScheme for KPKE { /// ``` /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, KPKE}; /// let k_pke = KPKE::ml_kem_512(); - /// let (pk, sk) = k_pke.gen(); + /// let (pk, sk) = k_pke.key_gen(); /// let c = k_pke.enc(&pk, 1); /// /// let m = k_pke.dec(&sk, &c); @@ -272,7 +272,7 @@ impl PKEncryptionScheme for KPKE { #[cfg(test)] mod test_kpke { - use crate::pk_encryption::{k_pke::KPKE, PKEncryptionScheme}; + use crate::pk_encryption::{PKEncryptionScheme, k_pke::KPKE}; /// Ensures that [`KPKE`] works for all ML-KEM specifications by /// performing a round trip of several messages. @@ -283,7 +283,7 @@ mod test_kpke { let messages = [0, 1, 13, 255, 2047, 4294967295_u32]; for message in messages { - let (pk, sk) = k_pke.gen(); + let (pk, sk) = k_pke.key_gen(); let c = k_pke.enc(&pk, message); let m = k_pke.dec(&sk, &c); diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index 07e4959..aa985fd 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -34,7 +34,7 @@ use serde::{Deserialize, Serialize}; /// use qfall_math::integer::Z; /// // setup public parameters and key pair /// let lpr = LPR::default(); -/// let (pk, sk) = lpr.gen(); +/// let (pk, sk) = lpr.key_gen(); /// /// // encrypt a bit /// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 @@ -240,7 +240,7 @@ impl LPR { // α = o (1 / sqrt(n) * log^3 n )) if self.alpha > 1 / (factor * self.n.sqrt() * self.n.log(2).unwrap().pow(3).unwrap()) { return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log^3 n), but α < 1 / (sqrt(n) * log^3 n) is required. Please check the documentation!" + "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log^3 n), but α < 1 / (sqrt(n) * log^3 n) is required. Please check the documentation!", ))); } @@ -325,9 +325,9 @@ impl PKEncryptionScheme for LPR { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, LPR}; /// let lpr = LPR::default(); /// - /// let (pk, sk) = lpr.gen(); + /// let (pk, sk) = lpr.key_gen(); /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) { // A <- Z_q^{n x n} let mat_a = MatZq::sample_uniform(&self.n, &self.n, &self.q); // s <- χ^n @@ -367,7 +367,7 @@ impl PKEncryptionScheme for LPR { /// ``` /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, LPR}; /// let lpr = LPR::default(); - /// let (pk, sk) = lpr.gen(); + /// let (pk, sk) = lpr.key_gen(); /// /// let cipher = lpr.enc(&pk, 1); /// ``` @@ -417,7 +417,7 @@ impl PKEncryptionScheme for LPR { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, LPR}; /// use qfall_math::integer::Z; /// let lpr = LPR::default(); - /// let (pk, sk) = lpr.gen(); + /// let (pk, sk) = lpr.key_gen(); /// let cipher = lpr.enc(&pk, 1); /// /// let m = lpr.dec(&sk, &cipher); @@ -524,56 +524,56 @@ mod test_lpr { use crate::pk_encryption::PKEncryptionScheme; use qfall_math::integer::Z; - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and small n. #[test] fn cycle_zero_small_n() { let msg = Z::ZERO; let lpr = LPR::default(); - let (pk, sk) = lpr.gen(); + let (pk, sk) = lpr.key_gen(); let cipher = lpr.enc(&pk, &msg); let m = lpr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and small n. #[test] fn cycle_one_small_n() { let msg = Z::ONE; let lpr = LPR::default(); - let (pk, sk) = lpr.gen(); + let (pk, sk) = lpr.key_gen(); let cipher = lpr.enc(&pk, &msg); let m = lpr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and larger n. #[test] fn cycle_zero_large_n() { let msg = Z::ZERO; let lpr = LPR::new_from_n(50); - let (pk, sk) = lpr.gen(); + let (pk, sk) = lpr.key_gen(); let cipher = lpr.enc(&pk, &msg); let m = lpr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and larger n. #[test] fn cycle_one_large_n() { let msg = Z::ONE; let lpr = LPR::new_from_n(50); - let (pk, sk) = lpr.gen(); + let (pk, sk) = lpr.key_gen(); let cipher = lpr.enc(&pk, &msg); let m = lpr.dec(&sk, &cipher); @@ -585,7 +585,7 @@ mod test_lpr { fn modulus_application() { let messages = [2, 3, i64::MAX, i64::MIN]; let dr = LPR::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); for msg in messages { let msg_mod = Z::from(msg.rem_euclid(2)); @@ -600,7 +600,7 @@ mod test_lpr { #[cfg(test)] mod test_multi_bits { - use super::{GenericMultiBitEncryption, PKEncryptionScheme, LPR}; + use super::{GenericMultiBitEncryption, LPR, PKEncryptionScheme}; use qfall_math::integer::Z; /// Checks whether the multi-bit encryption cycle works properly @@ -613,7 +613,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = LPR::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -628,7 +628,7 @@ mod test_multi_bits { let msg = Z::ZERO; let scheme = LPR::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -646,7 +646,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = LPR::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 7d4d9e6..012333d 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -35,7 +35,7 @@ use serde::{Deserialize, Serialize}; /// use qfall_math::integer::Z; /// // setup public parameters and key pair /// let regev = Regev::default(); -/// let (pk, sk) = regev.gen(); +/// let (pk, sk) = regev.key_gen(); /// /// // encrypt a bit /// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 @@ -122,7 +122,9 @@ impl Regev { pub fn new_from_n(n: impl Into) -> Self { let n = n.into(); if n < 10 { - panic!("Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise."); + panic!( + "Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise." + ); } let mut m: Z; @@ -239,13 +241,13 @@ impl Regev { // α = o (1 / ( sqrt(n) * log n ) ) if self.alpha > 1 / (self.n.sqrt() * self.n.log(2).unwrap()) { return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log n), but α < 1 / (sqrt(n) * log n) is required." + "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log n), but α < 1 / (sqrt(n) * log n) is required.", ))); } // concentration bound with r=5 -> r * sqrt(m) * α > q/4 if 20 * self.m.sqrt() * &self.alpha > q { return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as 5 * sqrt(m) * α > q/4, but 5 * sqrt(m) * α <= q/4 is required." + "Correctness is not guaranteed as 5 * sqrt(m) * α > q/4, but 5 * sqrt(m) * α <= q/4 is required.", ))); } @@ -337,9 +339,9 @@ impl PKEncryptionScheme for Regev { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, Regev}; /// let regev = Regev::default(); /// - /// let (pk, sk) = regev.gen(); + /// let (pk, sk) = regev.key_gen(); /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) { // A <- Z_q^{n x m} let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); // s <- Z_q^n @@ -375,7 +377,7 @@ impl PKEncryptionScheme for Regev { /// ``` /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, Regev}; /// let regev = Regev::default(); - /// let (pk, sk) = regev.gen(); + /// let (pk, sk) = regev.key_gen(); /// /// let cipher = regev.enc(&pk, 1); /// ``` @@ -414,7 +416,7 @@ impl PKEncryptionScheme for Regev { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, Regev}; /// use qfall_math::integer::Z; /// let regev = Regev::default(); - /// let (pk, sk) = regev.gen(); + /// let (pk, sk) = regev.key_gen(); /// let cipher = regev.enc(&pk, 1); /// /// let m = regev.dec(&sk, &cipher); @@ -520,53 +522,53 @@ mod test_regev { use crate::pk_encryption::PKEncryptionScheme; use qfall_math::integer::Z; - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and small n. #[test] fn cycle_zero_small_n() { let msg = Z::ZERO; let regev = Regev::default(); - let (pk, sk) = regev.gen(); + let (pk, sk) = regev.key_gen(); let cipher = regev.enc(&pk, &msg); let m = regev.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and small n. #[test] fn cycle_one_small_n() { let msg = Z::ONE; let regev = Regev::default(); - let (pk, sk) = regev.gen(); + let (pk, sk) = regev.key_gen(); let cipher = regev.enc(&pk, &msg); let m = regev.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and larger n. #[test] fn cycle_zero_large_n() { let msg = Z::ZERO; let regev = Regev::new_from_n(50); - let (pk, sk) = regev.gen(); + let (pk, sk) = regev.key_gen(); let cipher = regev.enc(&pk, &msg); let m = regev.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and larger n. #[test] fn cycle_one_large_n() { let msg = Z::ONE; let regev = Regev::new_from_n(50); - let (pk, sk) = regev.gen(); + let (pk, sk) = regev.key_gen(); let cipher = regev.enc(&pk, &msg); let m = regev.dec(&sk, &cipher); assert_eq!(msg, m); @@ -577,7 +579,7 @@ mod test_regev { fn modulus_application() { let messages = [2, 3, i64::MAX, i64::MIN]; let regev = Regev::default(); - let (pk, sk) = regev.gen(); + let (pk, sk) = regev.key_gen(); for msg in messages { let msg_mod = Z::from(msg.rem_euclid(2)); @@ -605,7 +607,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = Regev::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -620,7 +622,7 @@ mod test_multi_bits { let msg = Z::ZERO; let scheme = Regev::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -638,7 +640,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = Regev::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index 01b0c19..0719cdf 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -38,7 +38,7 @@ use serde::{Deserialize, Serialize}; /// use qfall_math::integer::Z; /// // setup public parameters and key pair /// let regev = RegevWithDiscreteGaussianRegularity::default(); -/// let (pk, sk) = regev.gen(); +/// let (pk, sk) = regev.key_gen(); /// /// // encrypt a bit /// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 @@ -306,7 +306,7 @@ impl RegevWithDiscreteGaussianRegularity { // r >= ω( sqrt( log m ) ) if self.r < self.m.log(2).unwrap().sqrt() { return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as r < sqrt( log m ) and r >= ω(sqrt(log m)) is required." + "Security is not guaranteed as r < sqrt( log m ) and r >= ω(sqrt(log m)) is required.", ))); } @@ -356,9 +356,9 @@ impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; /// let regev = RegevWithDiscreteGaussianRegularity::default(); /// - /// let (pk, sk) = regev.gen(); + /// let (pk, sk) = regev.key_gen(); /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) { // s <- Z_q^n let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); @@ -392,7 +392,7 @@ impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { /// ``` /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; /// let regev = RegevWithDiscreteGaussianRegularity::default(); - /// let (pk, sk) = regev.gen(); + /// let (pk, sk) = regev.key_gen(); /// /// let cipher = regev.enc(&pk, 1); /// ``` @@ -427,7 +427,7 @@ impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; /// use qfall_math::integer::Z; /// let regev = RegevWithDiscreteGaussianRegularity::default(); - /// let (pk, sk) = regev.gen(); + /// let (pk, sk) = regev.key_gen(); /// let cipher = regev.enc(&pk, 1); /// /// let m = regev.dec(&sk, &cipher); @@ -534,56 +534,56 @@ mod test_regev { use crate::pk_encryption::PKEncryptionScheme; use qfall_math::integer::Z; - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and small n. #[test] fn cycle_zero_small_n() { let msg = Z::ZERO; let dr = RegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and small n. #[test] fn cycle_one_small_n() { let msg = Z::ONE; let dr = RegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 0 and larger n. #[test] fn cycle_zero_large_n() { let msg = Z::ZERO; let dr = RegevWithDiscreteGaussianRegularity::new_from_n(30); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); assert_eq!(msg, m); } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for message 1 and larger n. #[test] fn cycle_one_large_n() { let msg = Z::ONE; let dr = RegevWithDiscreteGaussianRegularity::new_from_n(30); - let (pk, sk) = dr.gen(); + let (pk, sk) = dr.key_gen(); let cipher = dr.enc(&pk, &msg); let m = dr.dec(&sk, &cipher); @@ -595,7 +595,7 @@ mod test_regev { fn modulus_application() { let messages = [2, 3, i64::MAX, i64::MIN]; let regev = RegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = regev.gen(); + let (pk, sk) = regev.key_gen(); for msg in messages { let msg_mod = Z::from(msg.rem_euclid(2)); @@ -625,7 +625,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = RegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -640,7 +640,7 @@ mod test_multi_bits { let msg = Z::ZERO; let scheme = RegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); @@ -658,7 +658,7 @@ mod test_multi_bits { let msg = Z::from(value); let scheme = RegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let cipher = scheme.enc_multiple_bits(&pk, &msg); let m = scheme.dec_multiple_bits(&sk, &cipher); diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index da4b27b..9401377 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -40,7 +40,7 @@ use serde::{Deserialize, Serialize}; /// use qfall_math::integer::Z; /// // setup public parameters and key pair /// let lpr = RingLPR::default(); -/// let (pk, sk) = lpr.gen(); +/// let (pk, sk) = lpr.key_gen(); /// /// // encrypt a bit /// let msg = Z::from(15); // must be at most n bits, i.e. for default 2^16 - 1 @@ -345,9 +345,9 @@ impl PKEncryptionScheme for RingLPR { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RingLPR}; /// let lpr = RingLPR::default(); /// - /// let (pk, sk) = lpr.gen(); + /// let (pk, sk) = lpr.key_gen(); /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) { // a <- R_q let a = PolynomialRingZq::sample_uniform(&self.q); // s <- χ @@ -385,7 +385,7 @@ impl PKEncryptionScheme for RingLPR { /// ``` /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RingLPR}; /// let lpr = RingLPR::default(); - /// let (pk, sk) = lpr.gen(); + /// let (pk, sk) = lpr.key_gen(); /// /// let cipher = lpr.enc(&pk, 15); /// ``` @@ -434,7 +434,7 @@ impl PKEncryptionScheme for RingLPR { /// use qfall_schemes::pk_encryption::{PKEncryptionScheme, RingLPR}; /// use qfall_math::integer::Z; /// let lpr = RingLPR::default(); - /// let (pk, sk) = lpr.gen(); + /// let (pk, sk) = lpr.key_gen(); /// let cipher = lpr.enc(&pk, 212); /// /// let m = lpr.dec(&sk, &cipher); @@ -535,12 +535,12 @@ mod test_ring_lpr { use crate::pk_encryption::PKEncryptionScheme; use qfall_math::integer::Z; - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for several messages and small n. #[test] fn cycle_small_n() { let scheme = RingLPR::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let messages = [0, 1, 2, 15, 70, 256, 580, 1000, 4000, 8000, 65535]; for message in messages { @@ -551,12 +551,12 @@ mod test_ring_lpr { } } - /// Checks whether the full-cycle of gen, enc, dec works properly + /// Checks whether the full-cycle of key_gen, enc, dec works properly /// for several messages and larger n. #[test] fn cycle_large_n() { let scheme = RingLPR::new_from_n(64); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); let messages = [ 0, 1, @@ -588,7 +588,7 @@ mod test_ring_lpr { fn modulus_application() { let messages = [65536]; let scheme = RingLPR::default(); - let (pk, sk) = scheme.gen(); + let (pk, sk) = scheme.key_gen(); for msg in messages { let cipher = scheme.enc(&pk, msg); diff --git a/src/signature.rs b/src/signature.rs index 9992f85..1f4d42f 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -21,7 +21,7 @@ pub mod pfdh; /// This trait should be implemented by every signature scheme. /// It captures the essential functionalities each signature scheme has to support. /// -/// Note: The gen does not take in the parameter `1^n`, as this is a public parameter, +/// Note: [`SignatureScheme::key_gen`] does not take in the parameter `1^n`, as this is a public parameter, /// which shall be defined by the struct implementing this trait. pub trait SignatureScheme { /// The type of the secret key. @@ -35,7 +35,7 @@ pub trait SignatureScheme { /// struct has, which implements this trait. /// /// Returns the public key and the secret key. - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey); + fn key_gen(&mut self) -> (Self::PublicKey, Self::SecretKey); /// Signs a message using the secret key (and potentially the public key). /// diff --git a/src/signature/fdh.rs b/src/signature/fdh.rs index 59fc222..820046b 100644 --- a/src/signature/fdh.rs +++ b/src/signature/fdh.rs @@ -29,7 +29,7 @@ //! //! let m = "Hello World!"; //! -//! let (pk, sk) = fdh.gen(); +//! let (pk, sk) = fdh.key_gen(); //! let sigma = fdh.sign(m.to_owned(), &sk, &pk); //! //! assert!(fdh.vfy(m.to_owned(), &sigma, &pk)); diff --git a/src/signature/fdh/gpv.rs b/src/signature/fdh/gpv.rs index 93154a9..b2fba1c 100644 --- a/src/signature/fdh/gpv.rs +++ b/src/signature/fdh/gpv.rs @@ -10,7 +10,7 @@ //! according to [\[1\]](<../index.html#:~:text=[1]>). use crate::{ - hash::{sha256::HashMatZq, HashInto}, + hash::{HashInto, sha256::HashMatZq}, signature::SignatureScheme, }; use qfall_math::{ @@ -45,7 +45,7 @@ use std::collections::HashMap; /// let m = "Hello World!"; /// /// let mut fdh = FDHGPV::setup(4, 113, 17); -/// let (pk, sk) = fdh.gen(); +/// let (pk, sk) = fdh.key_gen(); /// /// let sigma = fdh.sign(m.to_string(), &sk, &pk); /// @@ -110,7 +110,7 @@ impl SignatureScheme for FDHGPV { type Signature = MatZ; /// Generates a trapdoor by calling the `trap_gen` of the psf - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { self.psf.trap_gen() } /// Firstly checks if the message has been signed before, and if, return that @@ -146,7 +146,7 @@ impl SignatureScheme for FDHGPV { #[cfg(test)] mod test_fdh { - use crate::signature::{fdh::gpv::FDHGPV, SignatureScheme}; + use crate::signature::{SignatureScheme, fdh::gpv::FDHGPV}; use qfall_math::{integer::Z, rational::Q, traits::Pow}; /// Ensure that the generated signature is valid. @@ -160,7 +160,7 @@ mod test_fdh { let q = Z::from(2).pow(&k).unwrap(); let mut fdh = FDHGPV::setup(n, &q, &s); - let (pk, sk) = fdh.gen(); + let (pk, sk) = fdh.key_gen(); for i in 0..10 { let m = format!("Hello World! {i}"); @@ -178,7 +178,7 @@ mod test_fdh { let mut fdh = FDHGPV::setup(5, 1024, 10); let m = "Hello World!"; - let (pk, sk) = fdh.gen(); + let (pk, sk) = fdh.key_gen(); let _ = fdh.sign(m.to_owned(), &sk, &pk); assert!(fdh.storage.contains_key(m)) @@ -191,7 +191,7 @@ mod test_fdh { // fill one entry in the HashMap let m = "Hello World!"; - let (pk, sk) = fdh.gen(); + let (pk, sk) = fdh.key_gen(); let _ = fdh.sign(m.to_owned(), &sk, &pk); let fdh_string = serde_json::to_string(&fdh).expect("Unable to create a json object"); diff --git a/src/signature/fdh/gpv_ring.rs b/src/signature/fdh/gpv_ring.rs index 0dc4793..802f217 100644 --- a/src/signature/fdh/gpv_ring.rs +++ b/src/signature/fdh/gpv_ring.rs @@ -10,7 +10,7 @@ //! according to [\[1\]](<../index.html#:~:text=[1]>). use crate::{ - hash::{sha256::HashMatPolynomialRingZq, HashInto}, + hash::{HashInto, sha256::HashMatPolynomialRingZq}, signature::SignatureScheme, }; use qfall_math::{ @@ -19,7 +19,7 @@ use qfall_math::{ rational::Q, }; use qfall_tools::{ - primitive::psf::{PSFGPVRing, PSF}, + primitive::psf::{PSF, PSFGPVRing}, sample::g_trapdoor::gadget_parameters::GadgetParametersRing, }; use serde::{Deserialize, Serialize}; @@ -54,7 +54,7 @@ use std::collections::HashMap; /// } /// /// let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); -/// let (pk, sk) = fdh.gen(); +/// let (pk, sk) = fdh.key_gen(); /// let m = &format!("Hello World!"); /// let sigma = fdh.sign(m.to_owned(), &sk, &pk); /// assert!( @@ -130,7 +130,7 @@ impl SignatureScheme for FDHGPVRing { type Signature = MatPolyOverZ; /// Generates a trapdoor by calling the `trap_gen` of the psf - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { self.psf.trap_gen() } @@ -167,7 +167,7 @@ impl SignatureScheme for FDHGPVRing { #[cfg(test)] mod test_fdh { - use crate::signature::{fdh::gpv_ring::FDHGPVRing, SignatureScheme}; + use crate::signature::{SignatureScheme, fdh::gpv_ring::FDHGPVRing}; use qfall_math::rational::Q; const MODULUS: i64 = 512; @@ -180,7 +180,7 @@ mod test_fdh { #[test] fn ensure_valid_signature_is_generated() { let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); - let (pk, sk) = fdh.gen(); + let (pk, sk) = fdh.key_gen(); for i in 0..10 { let m = &format!("Hello World! {i}"); @@ -202,7 +202,7 @@ mod test_fdh { let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); let m = "Hello World!"; - let (pk, sk) = fdh.gen(); + let (pk, sk) = fdh.key_gen(); let sign_1 = fdh.sign(m.to_owned(), &sk, &pk); let sign_2 = fdh.sign(m.to_owned(), &sk, &pk); @@ -217,7 +217,7 @@ mod test_fdh { // fill one entry in the HashMap let m = "Hello World!"; - let (pk, sk) = fdh.gen(); + let (pk, sk) = fdh.key_gen(); let _ = fdh.sign(m.to_owned(), &sk, &pk); let fdh_string = serde_json::to_string(&fdh).expect("Unable to create a json object"); diff --git a/src/signature/pfdh.rs b/src/signature/pfdh.rs index 8d6f0ec..e2defe1 100644 --- a/src/signature/pfdh.rs +++ b/src/signature/pfdh.rs @@ -31,7 +31,7 @@ //! //! let m = "Hello World!"; //! -//! let (pk, sk) = pfdh.gen(); +//! let (pk, sk) = pfdh.key_gen(); //! let sigma = pfdh.sign(m.to_owned(), &sk, &pk); //! //! assert!(pfdh.vfy(m.to_owned(), &sigma, &pk)); diff --git a/src/signature/pfdh/gpv.rs b/src/signature/pfdh/gpv.rs index 44c5ec0..3227e69 100644 --- a/src/signature/pfdh/gpv.rs +++ b/src/signature/pfdh/gpv.rs @@ -10,7 +10,7 @@ //! according to [\[1\]](<../index.html#:~:text=[1]>). use crate::{ - hash::{sha256::HashMatZq, HashInto}, + hash::{HashInto, sha256::HashMatZq}, signature::SignatureScheme, }; use qfall_math::{ @@ -45,7 +45,7 @@ use qfall_tools::{ /// /// let m = "Hello World!"; /// -/// let (pk, sk) = pfdh.gen(); +/// let (pk, sk) = pfdh.key_gen(); /// let sigma = pfdh.sign(m.to_owned(), &sk, &pk); /// /// assert!(pfdh.vfy(m.to_owned(), &sigma, &pk)); @@ -115,7 +115,7 @@ impl SignatureScheme for PFDHGPV { type Signature = (MatZ, Z); /// Generates a trapdoor by calling the `trap_gen` of the psf - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { + fn key_gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { self.psf.trap_gen() } @@ -146,7 +146,7 @@ impl SignatureScheme for PFDHGPV { #[cfg(test)] mod test_pfdh { - use crate::signature::{pfdh::gpv::PFDHGPV, SignatureScheme}; + use crate::signature::{SignatureScheme, pfdh::gpv::PFDHGPV}; use qfall_math::{integer::Z, rational::Q, traits::Pow}; /// Ensure that the generated signature is valid. @@ -160,7 +160,7 @@ mod test_pfdh { let q = Z::from(2).pow(&k).unwrap(); let mut pfdh = PFDHGPV::setup(n, &q, &s, 128); - let (pk, sk) = pfdh.gen(); + let (pk, sk) = pfdh.key_gen(); for i in 0..10 { let m = format!("Hello World! {i}"); From 6a80653e8b282bd21928f64dabe4e25954a3018f Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 15 Dec 2025 13:25:52 +0000 Subject: [PATCH 23/30] Revise documentation --- README.md | 4 +- src/hash.rs | 4 +- src/hash/sha256.rs | 2 +- src/hash/sis.rs | 3 +- src/identity_based_encryption.rs | 7 +-- .../dual_regev_ibe.rs | 4 +- src/lib.rs | 48 ++++++++++++------- src/pk_encryption.rs | 7 +-- src/pk_encryption/ccs_from_ibe.rs | 3 +- src/pk_encryption/dual_regev.rs | 3 +- .../dual_regev_discrete_gauss.rs | 5 +- src/pk_encryption/k_pke.rs | 3 +- src/pk_encryption/lpr.rs | 3 +- src/pk_encryption/regev.rs | 3 +- src/pk_encryption/regev_discrete_gauss.rs | 5 +- src/pk_encryption/ring_lpr.rs | 3 +- src/signature.rs | 5 +- src/signature/fdh.rs | 4 +- src/signature/pfdh.rs | 2 +- 19 files changed, 55 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 3460945..2e6b08e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [license](https://github.com/qfall/schemes/blob/dev/LICENSE) `qFALL` is a prototyping library for lattice-based cryptography. -This `schemes`-crate collects implementations of lattice-based constructions to reuse them more easily in more complex constructions or protocols. +This `schemes`-crate collects implementations of lattice-based constructions s.t. the community can reuse them in more complex constructions or protocols. ## Quick-Start First, ensure that you use a Unix-like distribution (Linux or MacOS). Setup [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) if you're using Windows. This is required due to this crate's dependency on FLINT. @@ -59,7 +59,7 @@ let k_pke = KPKE::ml_kem_512(); let (pk, sk) = k_pke.key_gen(); // encrypt a message -let msg = Z::from_uft8("Hello"); +let msg = Z::from_utf8("Hello"); let cipher = k_pke.enc(&pk, &msg); // decrypt the ciphertext diff --git a/src/hash.rs b/src/hash.rs index cfe7d6d..ff3013f 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -6,9 +6,9 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains implementations of hash functions. +//! Contains traits and implementations related to hash functions. //! -//! The main references are listed in the following: +//! References: //! - \[1\] Peikert, Chris (2016). //! A decade of lattice cryptography. //! In: Theoretical Computer Science 10.4. diff --git a/src/hash/sha256.rs b/src/hash/sha256.rs index ec89fed..f9005d5 100644 --- a/src/hash/sha256.rs +++ b/src/hash/sha256.rs @@ -6,7 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains sha256 hashes into different domains. +//! Contains sha256-based hashes that hash into different domains. use super::HashInto; use qfall_math::traits::FromCoefficientEmbedding; diff --git a/src/hash/sis.rs b/src/hash/sis.rs index e2211af..41ca51c 100644 --- a/src/hash/sis.rs +++ b/src/hash/sis.rs @@ -6,8 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains an implementation of the collision-resistant -//! SIS-based hash function. +//! Contains an implementation of the collision-resistant SIS hash function. use qfall_math::{error::MathError, integer::Z, integer_mod_q::MatZq, traits::MatrixDimensions}; use serde::{Deserialize, Serialize}; diff --git a/src/identity_based_encryption.rs b/src/identity_based_encryption.rs index 5606c3e..1c84d0f 100644 --- a/src/identity_based_encryption.rs +++ b/src/identity_based_encryption.rs @@ -6,12 +6,9 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module provides the trait a struct should implement if it is an -//! instance of a identity based public key encryption scheme. Furthermore, -//! it contains cryptographic schemes implementing the [`IBEScheme`] trait. +//! Contains traits and implementations related to Identity-Based Encryption (IBE) schemes. //! -//! The main references are listed in the following -//! and will be further referenced in submodules by these numbers: +//! References: //! - \[1\] Gentry, Craig and Peikert, Chris and Vaikuntanathan, Vinod (2008). //! Trapdoors for hard lattices and new cryptographic constructions. //! In: Proceedings of the fortieth annual ACM symposium on Theory of computing. diff --git a/src/identity_based_encryption/dual_regev_ibe.rs b/src/identity_based_encryption/dual_regev_ibe.rs index d417771..dde4ecc 100644 --- a/src/identity_based_encryption/dual_regev_ibe.rs +++ b/src/identity_based_encryption/dual_regev_ibe.rs @@ -6,9 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains an implementation of the IND-CPA secure -//! identity based public key encryption scheme. The encryption scheme is based -//! on [`DualRegevIBE`]. +//! Contains an implementation of the IND-CPA secure IBE scheme based on [`DualRegevIBE`]. use super::IBEScheme; use crate::{ diff --git a/src/lib.rs b/src/lib.rs index 6d324e0..03fa3e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,28 +6,40 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! # What is qFALL-schemes? -//! qFall-schemes provides lattice-based cryptographic constructions to enable prototyping -//! based on the existing constructions. +//! `qFALL` is a prototyping library for lattice-based cryptography. +//! `qFALL-schemes` collects prototype implementations of lattice-based cryptography +//! s.t. the community can reuse them in more complex constructions or protocols. +//! Among these are traits and implemented constructions of: +//! - [Public-Key Encryption schemes](pk_encryption) implementations such as [Regev's Encryption](pk_encryption::Regev), [its dual version](pk_encryption::DualRegev), [LPR](pk_encryption::LPR), or [K-PKE](pk_encryption::KPKE), +//! - [Signature schemes](signature) implementations such as GPV-based [FDH](signature::fdh) or [PFDH](signature::fdh), +//! - an [Identity-based Encryption](identity_based_encryption) from [Dual Regev](identity_based_encryption::DualRegevIBE), as well as +//! - [Hash functions](hash) such as the [SIS hash](hash::SISHash) or a [SHA256-based hash](hash::sha256). //! -//! Currently qFALL-schemes supports 3 main construction types: -//! - [Identity-Based Encryptions](identity_based_encryption::IBEScheme) -//! - [Public-Key Encryptions](pk_encryption::PKEncryptionScheme) -//! - [Signatures](signature::SignatureScheme) +//! The `qFALL` project contains two more crates called [`qFALL-math`](https://crates.io/crates/qfall-math) +//! and [`qFALL-tools`](https://crates.io/crates/qfall-tools) to support prototyping. +//! - Find further information on [our website](https://qfall.github.io/). +//! - We recommend [our tutorial](https://qfall.github.io/book) to start working with qFALL. //! -//! These are identified by traits and then implemented for specific constructions, e.g. -//! [`RingLPR`](pk_encryption::RingLPR). +//! ## Quick Example +//! ``` +//! use qfall_schemes::pk_encryption::{KPKE, PKEncryptionScheme}; +//! use qfall_math::integer::Z; //! -//! qfall-schemes is free software: you can redistribute it and/or modify it under -//! the terms of the Mozilla Public License Version 2.0 as published by the -//! Mozilla Foundation. See . +//! // setup public parameters +//! let k_pke = KPKE::ml_kem_512(); //! -//! ## Tutorial + Website -//! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-schemes on our [website](https://qfall.github.io/). -//! The tutorial explains the basic steps starting from installation and -//! continues with basic usage. -//! qfall-schemes is co-developed together with qFALL-math and qFALL-tools which provide the -//! foundation that is used to implement the cryptographic constructions. +//! // generate (pk, sk) pair +//! let (pk, sk) = k_pke.key_gen(); +//! +//! // encrypt a message +//! let msg = Z::from_utf8("Hello"); +//! let cipher = k_pke.enc(&pk, &msg); +//! +//! // decrypt the ciphertext +//! let m = k_pke.dec(&sk, &cipher); +//! +//! assert_eq!(msg, m); +//! ``` pub mod hash; pub mod identity_based_encryption; diff --git a/src/pk_encryption.rs b/src/pk_encryption.rs index da51c03..4fc84e2 100644 --- a/src/pk_encryption.rs +++ b/src/pk_encryption.rs @@ -6,12 +6,9 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module provides the trait a struct should implement if it is an -//! instance of a public key encryption scheme. Furthermore, it contains -//! cryptographic schemes implementing the [`PKEncryptionScheme`] or [`PKEncryptionSchemeMut`] trait. +//! Contains traits and implementations related to Public-Key Encryption (PKE) schemes. //! -//! The main references are listed in the following -//! and will be further referenced in submodules by these numbers: +//! References: //! - \[1\] Peikert, Chris (2016). //! A decade of lattice cryptography. //! In: Theoretical Computer Science 10.4. diff --git a/src/pk_encryption/ccs_from_ibe.rs b/src/pk_encryption/ccs_from_ibe.rs index e50cd0f..597d8df 100644 --- a/src/pk_encryption/ccs_from_ibe.rs +++ b/src/pk_encryption/ccs_from_ibe.rs @@ -6,8 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains a general implementation of an IND-CCA secure -//! public key encryption scheme constructed +//! Contains a generic implementation of an IND-CCA secure PKE scheme constructed //! via an [`IBEScheme`] and a [`SignatureScheme`]. use super::PKEncryptionSchemeMut; diff --git a/src/pk_encryption/dual_regev.rs b/src/pk_encryption/dual_regev.rs index a177a4b..6ddf0c9 100644 --- a/src/pk_encryption/dual_regev.rs +++ b/src/pk_encryption/dual_regev.rs @@ -6,8 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains an implementation of the IND-CPA secure -//! public key Dual Regev encryption scheme. +//! Contains an implementation of the IND-CPA PKE scheme refered to as Dual Regev encryption. use super::{GenericMultiBitEncryption, PKEncryptionScheme}; use qfall_math::{ diff --git a/src/pk_encryption/dual_regev_discrete_gauss.rs b/src/pk_encryption/dual_regev_discrete_gauss.rs index 8930259..04ca904 100644 --- a/src/pk_encryption/dual_regev_discrete_gauss.rs +++ b/src/pk_encryption/dual_regev_discrete_gauss.rs @@ -6,9 +6,8 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains an implementation of the IND-CPA secure -//! public key Dual Regev encryption scheme with an instantiation of the regularity lemma -//! via a discrete Gaussian distribution. +//! Contains an implementation of the IND-CPA PKE scheme refered to as Dual Regev encryption +//! with an instantiation of the regularity lemma using discrete Gaussians. use super::{GenericMultiBitEncryption, PKEncryptionScheme}; use qfall_math::{ diff --git a/src/pk_encryption/k_pke.rs b/src/pk_encryption/k_pke.rs index 3ba3d9f..7bc81d1 100644 --- a/src/pk_encryption/k_pke.rs +++ b/src/pk_encryption/k_pke.rs @@ -6,8 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains a naive implementation of the K-PKE scheme -//! used as foundation for ML-KEM. +//! Contains a naive implementation of the K-PKE scheme used as foundation for ML-KEM and Kyber. //! //! **WARNING:** This implementation is a toy implementation of the basics below //! ML-KEM and mostly supposed to showcase the prototyping capabilities of the `qFALL`-library. diff --git a/src/pk_encryption/lpr.rs b/src/pk_encryption/lpr.rs index aa985fd..e362e4f 100644 --- a/src/pk_encryption/lpr.rs +++ b/src/pk_encryption/lpr.rs @@ -6,8 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains an implementation of the IND-CPA secure -//! public key LPR encryption scheme. +//! Contains an implementation of the IND-CPA PKE refered to as LPR encryption. use super::{GenericMultiBitEncryption, PKEncryptionScheme}; use qfall_math::{ diff --git a/src/pk_encryption/regev.rs b/src/pk_encryption/regev.rs index 012333d..d38d254 100644 --- a/src/pk_encryption/regev.rs +++ b/src/pk_encryption/regev.rs @@ -6,8 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains an implementation of the IND-CPA secure -//! public key Regev encryption scheme. +//! Contains an implementation of the IND-CPA PKE refered to as Regev encryption. use super::{GenericMultiBitEncryption, PKEncryptionScheme}; use qfall_math::{ diff --git a/src/pk_encryption/regev_discrete_gauss.rs b/src/pk_encryption/regev_discrete_gauss.rs index 0719cdf..7446e13 100644 --- a/src/pk_encryption/regev_discrete_gauss.rs +++ b/src/pk_encryption/regev_discrete_gauss.rs @@ -6,9 +6,8 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains an implementation of the IND-CPA secure -//! public key Regev encryption scheme with an instantiation of the regularity lemma -//! via a discrete Gaussian distribution. +//! Contains an implementation of the IND-CPA PKE refered to as Regev encryption +//! with an instantiation of the regularity lemma using discrete Gaussians. use super::{GenericMultiBitEncryption, PKEncryptionScheme}; use qfall_math::{ diff --git a/src/pk_encryption/ring_lpr.rs b/src/pk_encryption/ring_lpr.rs index 9401377..ce30996 100644 --- a/src/pk_encryption/ring_lpr.rs +++ b/src/pk_encryption/ring_lpr.rs @@ -6,8 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains an implementation of the IND-CPA secure -//! public key Ring-LPR encryption scheme. +//! Contains an implementation of the IND-CPA PKE refered to as Ring-LPR encryption. use super::PKEncryptionScheme; use qfall_math::{ diff --git a/src/signature.rs b/src/signature.rs index 1f4d42f..814a5e4 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -6,10 +6,9 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module provides the trait a struct should implement if it is an -//! instance of a signature scheme. Furthermore, it contains cryptographic signatures -//! implementing the [`SignatureScheme`] trait. +//! Contains traits and implementations related to signature schemes. //! +//! References: //! - \[1\] Gentry, Craig, Chris Peikert, and Vinod Vaikuntanathan. //! "Trapdoors for hard lattices and new cryptographic constructions." //! Proceedings of the fortieth annual ACM symposium on Theory of computing. 2008. diff --git a/src/signature/fdh.rs b/src/signature/fdh.rs index 820046b..a9eeb5d 100644 --- a/src/signature/fdh.rs +++ b/src/signature/fdh.rs @@ -6,9 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This Module contains a implementations of the full domain hash signature scheme, -//! which only has to be instantiated with a corresponding PSF, a storage and -//! a corresponding hash function. +//! Contains implementations of the (stateful) full domain hash signature scheme. //! //! The constructions follow the general definition of a hash-then-sign signature scheme //! that uses a hash function as in [\[1\]](<../index.html#:~:text=[1]>) and a PSF. diff --git a/src/signature/pfdh.rs b/src/signature/pfdh.rs index e2defe1..9a1c1b7 100644 --- a/src/signature/pfdh.rs +++ b/src/signature/pfdh.rs @@ -6,7 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This Module contains a general implementation of the probabilistic full domain +//! Contains a generic implementation of the probabilistic full domain //! hash signature scheme. //! //! The constructions follow the general definition of a hash-then-sign signature scheme From 4819d205bedf1e72668f3bf1ae3b8107bfdf9725 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 15 Dec 2025 13:29:39 +0000 Subject: [PATCH 24/30] Minor --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e6b08e..648cc9b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ cargo add qfall-schemes ## What does qFALL-schemes offer? qFALL-schemes collects prototype implementations of lattice-based constructions to reuse them more easily in more complex constructions or protocols. -List of prototypes available +List of prototypes - [Public Key Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/index.html) - [LWE Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/struct.Regev.html) - [Dual LWE Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/struct.DualRegev.html) From 35f52f7d96f603952696fc12339102a9a79f561f Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 16 Dec 2025 09:08:45 +0000 Subject: [PATCH 25/30] Minor --- README.md | 4 ++-- src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 648cc9b..077955f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [license](https://github.com/qfall/schemes/blob/dev/LICENSE) `qFALL` is a prototyping library for lattice-based cryptography. -This `schemes`-crate collects implementations of lattice-based constructions s.t. the community can reuse them in more complex constructions or protocols. +This `schemes`-crate collects implementations of lattice-based constructions s.t. anyone can audit, modify, extend, or build on top of them to prototype more involved constructions or protocols. ## Quick-Start First, ensure that you use a Unix-like distribution (Linux or MacOS). Setup [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) if you're using Windows. This is required due to this crate's dependency on FLINT. @@ -26,7 +26,7 @@ cargo add qfall-schemes - We recommend [our tutorial](https://qfall.github.io/book) to start working with qFALL. ## What does qFALL-schemes offer? -qFALL-schemes collects prototype implementations of lattice-based constructions to reuse them more easily in more complex constructions or protocols. +qFALL-schemes collects prototype implementations of lattice-based constructions to audit, modify, extend, and reuse them more easily in more involved constructions or protocols. List of prototypes - [Public Key Encryption](https://docs.rs/qfall-schemes/latest/qfall_schemes/pk_encryption/index.html) diff --git a/src/lib.rs b/src/lib.rs index 03fa3e8..d0600fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! `qFALL` is a prototyping library for lattice-based cryptography. //! `qFALL-schemes` collects prototype implementations of lattice-based cryptography -//! s.t. the community can reuse them in more complex constructions or protocols. +//! s.t. anyone can audit, modify, extend, or build on top of them to prototype more involved constructions or protocols. //! Among these are traits and implemented constructions of: //! - [Public-Key Encryption schemes](pk_encryption) implementations such as [Regev's Encryption](pk_encryption::Regev), [its dual version](pk_encryption::DualRegev), [LPR](pk_encryption::LPR), or [K-PKE](pk_encryption::KPKE), //! - [Signature schemes](signature) implementations such as GPV-based [FDH](signature::fdh) or [PFDH](signature::fdh), From 1a789c79f890868da389a560442625aca4e8ba61 Mon Sep 17 00:00:00 2001 From: Phil Milewski Date: Thu, 8 Jan 2026 14:45:22 +0100 Subject: [PATCH 26/30] add bench readme wsl --- benches/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/benches/README.md b/benches/README.md index 90523c7..7a3e66d 100644 --- a/benches/README.md +++ b/benches/README.md @@ -43,7 +43,17 @@ You can also run the benchmarks using the profiler flamegraph. Details can be fo - [Flamegraph GitHub](https://github.com/flamegraph-rs/flamegraph). This provides insights on the execution time of the executed functions and their subroutines. -Note: Flamegraph does not work in WSL +### Installing Flamegraph + +Installing Flamegraph in Linux and macOS is easy, since you only need to install flamegraph using `cargo install flamegraph`. +But in WSL you need some more steps, since you need to install "perf" manually. + +So after `cargo install flamegraph`, you need to update "apt" with `sudo apt update` and install "build-essential's" by `sudo apt install -y build-essential libelf-dev libnuma-dev flex bison libdw-dev libunwind-dev libaudit-dev libslang2-dev libperl-dev python3-dev binutils-dev liblzma-dev libiberty-dev`.
+Then you go into the home directory with `cd ~` and install the linux kernel with
+`wget - https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.5.tar.xz` (Adjust the version as needed).
+After that you extract it with `tar -xvf linux-5.15.5.tar.xz` and move into "perf" with `cd linux-5.15.5/tools/perf` +there you use `make` to compile it. +The last step is to make "perf" visible by e.g. `sudo cp ./perf /usr/local/bin/`. ### Command From 1cd562404e89bd0da8637b6b27206aca836e19a3 Mon Sep 17 00:00:00 2001 From: Phil Milewski Date: Thu, 8 Jan 2026 14:52:26 +0100 Subject: [PATCH 27/30] adding contribution file --- CONTRIBUTING.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8cd3bf9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,69 @@ +# Contributing +This library is designed to prototype lattice-based cryptography. Our intent for this library is to be maintained by the community. We encourage anyone to add missing, frequently used features for lattice-based prototyping to this library, and we are happy to help with that process. + +More generally, all contributions such as bugfixes, documentation and tests are welcome. Please go ahead and submit your pull requests. + +## Choosing the right location +The qFALL library is divided into three repositories: [qFALL-math](https://github.com/qfall/math), [qFALL-tools](https://github.com/qfall/tools) and [qFALL-schemes](https://github.com/qfall/schemes). + +Please add new features to one of these repositories, roughly following these guidelines. +- If your feature implements a general mathematical function, then add your code to [qFALL-math](https://github.com/qfall/math). +- If your feature implements a fundamental primitive or shortcut that is commonly used in the construction of lattice-based schemes, e.g., G-trapdoors, then add your code to [qFALL-tools](https://github.com/qfall/tools). +- If you implement a construction, e.g., Kyber, then add your code to [qFALL-schemes](https://github.com/qfall/schemes). + +When in doubt, just submit your pull request to the repository you feel is best suited for your code. We will sort it. + +## Style Guide +Our style guide is based on the [rust standard](https://github.com/rust-lang/rfcs/blob/master/text/0505-api-comment-conventions.md). These rules summarise our style guidelines. +- Every function should be documented. A doc-comment includes a concise description of the function and an example. In case it receives parameters other than `self`, it also includes a description of every parameter, the output type, and behavior. If applicable, it also includes Error and Panic behavior and references to scientific literature. +- If the code of your function is not self-explanatory from your doc-comment, use inline-comments `//` to briefly describe the steps. +- A file should always have the copyright notice at the top, followed by a very brief inner doc-comment to summarise the purpose of this file, grouped up imports, implementations of all features, and finally tests of each feature in a separate test-module with a brief doc-comment for each test. +- Overall, any feature should get a descriptive but concise name s.t. it can be discovered intuitively. +- Code in our library needs to be formatted using `cargo fmt` and satisfy `clippy`'s standards. +- We aim for multiple tests per function, its unforeseen behavior, panic or error-cases to boost confidence in our implementations and ensure that modifications of a function only introduce intended changes of behavior. +- Last but not least, we would like to minimise the number of dependencies of all crates to keep them as slim and quickly compilable as possible. + +## Documentation +The documentation for each crate is available online and it can be generated locally by running the following command in the root directory of this repository. +```bash +cargo doc --open +``` + +Furthermore, here is an example of a doc-comment of a function that follows our guidelines. +```rust +impl Z { + /// Chooses a [`Z`] instance according to the discrete Gaussian distribution + /// in `[center - ⌈6 * s⌉ , center + ⌊6 * s⌋ ]`. + /// + /// This function samples discrete Gaussians according to the definition of + /// SampleZ in [GPV08](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=d9f54077d568784c786f7b1d030b00493eb3ae35). + /// + /// Parameters: + /// - `n`: specifies the range from which is sampled + /// - `center`: specifies the position of the center with peak probability + /// - `s`: specifies the Gaussian parameter, which is proportional + /// to the standard deviation `sigma * sqrt(2 * pi) = s` + /// + /// Returns new [`Z`] sample chosen according to the specified discrete Gaussian + /// distribution or a [`MathError`] if the specified parameters were not chosen + /// appropriately, i.e. `s < 0`. + /// + /// # Examples + /// ``` + /// use qfall_math::integer::Z; + /// + /// let sample = Z::sample_discrete_gauss(0, 1).unwrap(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) + /// if `s < 0`. + /// + /// This function implements SampleZ according to: + /// - \[1\] Gentry, Craig and Peikert, Chris and Vaikuntanathan, Vinod (2008). + /// Trapdoors for hard lattices and new cryptographic constructions. + /// In: Proceedings of the fortieth annual ACM symposium on Theory of computing. + /// + pub fn sample_discrete_gauss(center: impl Into, s: impl Into) -> Result {...} +} +``` \ No newline at end of file From 4e6a7049accf63be2fb894b44357f77fa9ec7674 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 20 Jan 2026 14:10:47 +0000 Subject: [PATCH 28/30] Minor updates --- CITATION.cff | 24 ------------------------ Cargo.toml | 8 ++++---- README.md | 10 ++++++++-- 3 files changed, 12 insertions(+), 30 deletions(-) delete mode 100644 CITATION.cff diff --git a/CITATION.cff b/CITATION.cff deleted file mode 100644 index b1125c4..0000000 --- a/CITATION.cff +++ /dev/null @@ -1,24 +0,0 @@ -# This CITATION.cff file was generated with cffinit. -# Visit https://bit.ly/cffinit to generate yours today! - -cff-version: 1.2.0 -title: qFALL-schemes -message: "University Paderborn, Codes and Cryptography" -type: software -authors: - - given-names: Laurens - family-names: Porzenheim - - given-names: Marvin - family-names: Beckmann - - given-names: Paul - family-names: Kramer - - given-names: Phil - family-names: Milewski - - given-names: Sven - family-names: Moog - - given-names: Marcel - family-names: Schmidt - - given-names: Niklas - family-names: Siemer -repository-code: "https://github.com/qfall/schemes" -license: MPL-2.0 diff --git a/Cargo.toml b/Cargo.toml index 79014b3..76cc018 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,10 +17,10 @@ autobenches = false [dependencies] qfall-tools = { git = "https://github.com/qfall/tools", branch = "dev" } qfall-math = { git = "https://github.com/qfall/math", branch = "dev" } -sha2 = "0.10.6" -serde = {version="1.0", features=["derive"]} -serde_json = "1.0" -typetag = "0.2" +sha2 = "0" +serde = {version="1", features=["derive"]} +serde_json = "1" +typetag = "0" criterion = { version = "0.8", features = ["html_reports"] } [profile.bench] diff --git a/README.md b/README.md index 077955f..9f6f58d 100644 --- a/README.md +++ b/README.md @@ -106,8 +106,14 @@ See [Contributing](https://github.com/qfall/schemes/blob/dev/CONTRIBUTING.md) fo ## Citing Please use the following bibtex entry to cite [qFALL](https://qfall.github.io). -```text -TODO: Update to eprint +```bibtex +@misc{qfall, + author = {Marvin Beckmann and Phil Milewski and Laurens Porzenheim and Marcel Luca Schmidt and Jan Niklas Siemer}, + title = {{qFALL} – {Rapid Prototyping of Lattice-based Cryptography}}, + howpublished = {Cryptology {ePrint} Archive, Paper 2026/069}, + year = {2026}, + url = {https://eprint.iacr.org/2026/069} +} ``` ## Dependencies From 60d4f84155478b8e4cbcd2dbce361fc01b061245 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 20 Jan 2026 14:14:33 +0000 Subject: [PATCH 29/30] Remove contributing file --- CONTRIBUTING.md | 69 ------------------------------------------------- 1 file changed, 69 deletions(-) delete mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 8cd3bf9..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,69 +0,0 @@ -# Contributing -This library is designed to prototype lattice-based cryptography. Our intent for this library is to be maintained by the community. We encourage anyone to add missing, frequently used features for lattice-based prototyping to this library, and we are happy to help with that process. - -More generally, all contributions such as bugfixes, documentation and tests are welcome. Please go ahead and submit your pull requests. - -## Choosing the right location -The qFALL library is divided into three repositories: [qFALL-math](https://github.com/qfall/math), [qFALL-tools](https://github.com/qfall/tools) and [qFALL-schemes](https://github.com/qfall/schemes). - -Please add new features to one of these repositories, roughly following these guidelines. -- If your feature implements a general mathematical function, then add your code to [qFALL-math](https://github.com/qfall/math). -- If your feature implements a fundamental primitive or shortcut that is commonly used in the construction of lattice-based schemes, e.g., G-trapdoors, then add your code to [qFALL-tools](https://github.com/qfall/tools). -- If you implement a construction, e.g., Kyber, then add your code to [qFALL-schemes](https://github.com/qfall/schemes). - -When in doubt, just submit your pull request to the repository you feel is best suited for your code. We will sort it. - -## Style Guide -Our style guide is based on the [rust standard](https://github.com/rust-lang/rfcs/blob/master/text/0505-api-comment-conventions.md). These rules summarise our style guidelines. -- Every function should be documented. A doc-comment includes a concise description of the function and an example. In case it receives parameters other than `self`, it also includes a description of every parameter, the output type, and behavior. If applicable, it also includes Error and Panic behavior and references to scientific literature. -- If the code of your function is not self-explanatory from your doc-comment, use inline-comments `//` to briefly describe the steps. -- A file should always have the copyright notice at the top, followed by a very brief inner doc-comment to summarise the purpose of this file, grouped up imports, implementations of all features, and finally tests of each feature in a separate test-module with a brief doc-comment for each test. -- Overall, any feature should get a descriptive but concise name s.t. it can be discovered intuitively. -- Code in our library needs to be formatted using `cargo fmt` and satisfy `clippy`'s standards. -- We aim for multiple tests per function, its unforeseen behavior, panic or error-cases to boost confidence in our implementations and ensure that modifications of a function only introduce intended changes of behavior. -- Last but not least, we would like to minimise the number of dependencies of all crates to keep them as slim and quickly compilable as possible. - -## Documentation -The documentation for each crate is available online and it can be generated locally by running the following command in the root directory of this repository. -```bash -cargo doc --open -``` - -Furthermore, here is an example of a doc-comment of a function that follows our guidelines. -```rust -impl Z { - /// Chooses a [`Z`] instance according to the discrete Gaussian distribution - /// in `[center - ⌈6 * s⌉ , center + ⌊6 * s⌋ ]`. - /// - /// This function samples discrete Gaussians according to the definition of - /// SampleZ in [GPV08](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=d9f54077d568784c786f7b1d030b00493eb3ae35). - /// - /// Parameters: - /// - `n`: specifies the range from which is sampled - /// - `center`: specifies the position of the center with peak probability - /// - `s`: specifies the Gaussian parameter, which is proportional - /// to the standard deviation `sigma * sqrt(2 * pi) = s` - /// - /// Returns new [`Z`] sample chosen according to the specified discrete Gaussian - /// distribution or a [`MathError`] if the specified parameters were not chosen - /// appropriately, i.e. `s < 0`. - /// - /// # Examples - /// ``` - /// use qfall_math::integer::Z; - /// - /// let sample = Z::sample_discrete_gauss(0, 1).unwrap(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if `s < 0`. - /// - /// This function implements SampleZ according to: - /// - \[1\] Gentry, Craig and Peikert, Chris and Vaikuntanathan, Vinod (2008). - /// Trapdoors for hard lattices and new cryptographic constructions. - /// In: Proceedings of the fortieth annual ACM symposium on Theory of computing. - /// - pub fn sample_discrete_gauss(center: impl Into, s: impl Into) -> Result {...} -} -``` \ No newline at end of file From 3b5eef8dedac14a1f06eed19afca73db4b41dee0 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 20 Jan 2026 16:37:05 +0000 Subject: [PATCH 30/30] Minor update of build badge + Cargo.toml --- Cargo.toml | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 76cc018..4cec7eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-tools = { git = "https://github.com/qfall/tools", branch = "dev" } -qfall-math = { git = "https://github.com/qfall/math", branch = "dev" } +qfall-tools = "0" +qfall-math = "0" sha2 = "0" serde = {version="1", features=["derive"]} serde_json = "1" diff --git a/README.md b/README.md index 9f6f58d..d3786c4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [crates.io](https://crates.io/crates/qfall-schemes) [docs.rs](https://docs.rs/qfall-schemes) [tutorial](https://qfall.github.io/book) -[build](https://github.com/qfall/schemes/actions/workflows/push.yml) +[build](https://github.com/qfall/schemes/actions/workflows/main.yml) [license](https://github.com/qfall/schemes/blob/dev/LICENSE) `qFALL` is a prototyping library for lattice-based cryptography.