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/Cargo.toml b/Cargo.toml index fb6e8e1..9f41347 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,15 @@ [package] name = "qfall-tools" version = "0.1.0" -edition = "2021" +edition = "2024" +rust-version = "1.85" # due to rand and rand_distr dependency +description = "Common sub-modules and procedures in lattice-based constructions" +readme = "README.md" +homepage = "https://qfall.github.io" +repository = "https://github.com/qfall/tools" +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 @@ -9,7 +17,7 @@ autobenches = false [dependencies] qfall-math = { git = "https://github.com/qfall/math", branch = "dev" } flint-sys = "0.7.3" -sha2 = "0.10.6" +sha2 = "0.10" serde = {version="1.0", features=["derive"]} serde_json = "1.0" typetag = "0.2" diff --git a/README.md b/README.md index df38823..eb7b95e 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,104 @@ # qFALL-tools +[github](https://github.com/qfall/tools) +[crates.io](https://crates.io/crates/qfall-tools) +[docs.rs](https://docs.rs/qfall-tools) +[tutorial](https://qfall.github.io/book) +[build](https://github.com/qfall/tools/actions/workflows/push.yml) +[license](https://github.com/qfall/tools/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/tools/actions/workflows/push.yml/badge.svg?branch=dev)](https://github.com/qfall/tools/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. +`qFALL` is a prototyping library for lattice-based cryptography. +This `tools`-crate collects common sub-modules and features used by lattice-based constructions to simplify and accelerate the development of such. ## 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. -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. +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-tools +``` +- Find further information on [our website](https://qfall.github.io/). Also check out [`qfall-math`](https://crates.io/crates/qfall-math) and [`qfall-schemes`](https://crates.io/crates/qfall-schemes). +- Read the [documentation of this crate](https://docs.rs/qfall-tools). +- We recommend [our tutorial](https://qfall.github.io/book) to start working with qFALL. ## What does qFALL-tools offer? +qFALL-tools offers several commonly used sub-modules specific to lattice-based cryptography. +- [Compression Techniques](https://docs.rs/qfall-tools/latest/qfall_tools/compression/index.html) + - [Lossy Compression according to FIPS 203](https://docs.rs/qfall-tools/latest/qfall_tools/compression/trait.LossyCompressionFIPS203.html) +- [Preimage Samplable Functions (PSF)](https://docs.rs/qfall-tools/latest/qfall_tools/primitive/psf/index.html) + - [GPV-based PSF over Z_q](https://docs.rs/qfall-tools/latest/qfall_tools/primitive/psf/struct.PSFGPV.html) + - [GPV-based PSF over R_q](https://docs.rs/qfall-tools/latest/qfall_tools/primitive/psf/struct.PSFGPVRing.html) + - [MP12 / Perturbation-based PSF over Z_q](https://docs.rs/qfall-tools/latest/qfall_tools/primitive/psf/struct.PSFPerturbation.html) +- [Trapdoors](https://docs.rs/qfall-tools/latest/qfall_tools/sample/g_trapdoor/index.html) + - [G-trapdoor incl. short basis](https://docs.rs/qfall-tools/latest/qfall_tools/sample/g_trapdoor/gadget_classical/index.html) + - [Ring-based G-trapdoor incl. short basis](https://docs.rs/qfall-tools/latest/qfall_tools/sample/g_trapdoor/gadget_ring/index.html) + +Furthermore, this crate simplifies the implementation of your prototype by supporting a range of [utility functions](https://docs.rs/qfall-tools/latest/qfall_tools/utils/index.html) to quickly instantiate [commonly used moduli](https://docs.rs/qfall-tools/latest/qfall_tools/utils/common_moduli/index.html), [rotation matrices](https://docs.rs/qfall-tools/latest/qfall_tools/utils/rotation_matrix/index.html), and [encodings](https://docs.rs/qfall-tools/latest/qfall_tools/utils/common_encodings/index.html). + +## Quick Examples +From String to Encoding for Encryption +```rust +use qfall_tools::utils::{common_moduli::new_anticyclic, common_encodings::encode_value_in_polynomialringzq}; +use qfall_math::integer::Z; + +// Create X^256 + 1 mod 3329 +let poly_mod = new_anticyclic(256, 3329).unwrap(); +// Generate integer from string +let message = Z::from_utf8("Hello!"); +// Turn string into encoding q/2 and 0 for each 1 and 0 bit respectively +let mu_q_half = encode_value_in_polynomialringzq(message, 2, &poly_mod).unwrap(); +``` -qFALL-tools offers a variety of implementations of commonly used tools in lattice-based cryptography. -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). +Preimage Sampling using a PSF +```rust +use qfall_tools::primitive::psf::{PSF, PSFPerturbation}; +use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; +use qfall_math::rational::Q; + +let psf = PSFPerturbation { + gp: GadgetParameters::init_default(8, 64), + r: Q::from(3), + s: Q::from(25), +}; + +// Generate matrix A with a trapdoor +let (a, td) = psf.trap_gen(); +// Choose a random target +let domain_sample = psf.samp_d(); +let target = psf.f_a(&a, &domain_sample); +// Sample a preimage for the given target +let preimage = psf.samp_p(&a, &td, &target); + +assert!(psf.check_domain(&preimage)); +assert_eq!(a * preimage, target); +``` -- [Preimage Samplable Functions (PSF)](https://github.com/qfall/tools/blob/dev/src/primitive/psf.rs) -- [Trapdoors](https://github.com/qfall/tools/blob/dev/src/sample/g_trapdoor.rs) - - [G-trapdoor incl. short basis](https://github.com/qfall/tools/blob/dev/src/sample/g_trapdoor/gadget_classical.rs) - - [Ring-based G-trapdoor incl. short basis](https://github.com/qfall/tools/blob/dev/src/sample/g_trapdoor/gadget_ring.rs) -- [Utility functions for quick instantiations](https://github.com/qfall/tools/blob/dev/src/utils/) - - [Common moduli](https://github.com/qfall/tools/blob/dev/src/utils/common_moduli.rs) - - [Rotation matrices](https://github.com/qfall/tools/blob/dev/src/utils/rotation_matrix.rs) - - [Common encodings](https://github.com/qfall/tools/blob/dev/src/utils/common_encodings.rs) +## Bugs +Please report bugs through the [GitHub issue tracker](https://github.com/qfall/tools/issues). -## License +## Contributions +Contributors are: +- Marvin Beckmann +- Jan Niklas Siemer -This library is distributed under the **Mozilla Public License Version 2.0** which can be found here [License](https://github.com/qfall/tools/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. +See [Contributing](https://github.com/qfall/tools/blob/dev/CONTRIBUTING.md) for details on how to contribute. ## Citing -Please use the following bibtex entry to cite [qFALL-tools](https://github.com/qfall/tools): +Please use the following bibtex entry to cite [qFALL](https://qfall.github.io). ```text -@misc{qFALL-tools, - author = {Porzenheim, Laurens and Beckmann, Marvin and Kramer, Paul and Milewski, Phil and Moog, Sven and Schmidt, Marcel and Siemer, Niklas}, - title = {qFALL-tools v0.0}, - howpublished = {Online: \url{https://github.com/qfall/tools}}, - 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), which builds 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. + +## License -One can contact the members of the project group with our mailing list `pg-qfall(at)lists.upb.de`. +This library is distributed under the [Mozilla Public License Version 2.0](https://github.com/qfall/tools/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. diff --git a/benches/psf.rs b/benches/psf.rs index 4a44b09..4a5712d 100644 --- a/benches/psf.rs +++ b/benches/psf.rs @@ -6,10 +6,10 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -use criterion::{criterion_group, Criterion}; +use criterion::{Criterion, criterion_group}; use qfall_math::{integer_mod_q::MatZq, rational::Q}; use qfall_tools::{ - primitive::psf::{PSFPerturbation, PSF, PSFGPV}, + primitive::psf::{PSF, PSFGPV, PSFPerturbation}, sample::g_trapdoor::gadget_parameters::GadgetParameters, }; diff --git a/src/compression.rs b/src/compression.rs new file mode 100644 index 0000000..ea45564 --- /dev/null +++ b/src/compression.rs @@ -0,0 +1,19 @@ +// Copyright © 2025 Niklas Siemer +// +// This file is part of qFALL-tools. +// +// qFALL-tools 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 . + +//! Contains commonly used compression techniques in lattice-based cryptography. +//! +//! References: +//! - \[1\] National Institute of Standards and Technology (2024). +//! Module-Lattice-Based Key-Encapsulation Mechanism Standard. +//! Federal Information Processing Standards Publication (FIPS 203). +//! + +mod lossy_compression_fips203; + +pub use lossy_compression_fips203::LossyCompressionFIPS203; diff --git a/src/utils/lossy_compression.rs b/src/compression/lossy_compression_fips203.rs similarity index 91% rename from src/utils/lossy_compression.rs rename to src/compression/lossy_compression_fips203.rs index f393ecc..56deb88 100644 --- a/src/utils/lossy_compression.rs +++ b/src/compression/lossy_compression_fips203.rs @@ -6,13 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! Implements lossy (de-)compression as specified in ML-KEM. -//! -//! Reference: -//! - \[1\] National Institute of Standards and Technology (2024). -//! Module-Lattice-Based Key-Encapsulation Mechanism Standard. -//! Federal Information Processing Standards Publication (FIPS 203). -//! +//! Implements lossy (de-)compression as specified in FIPS 203, i.e. ML-KEM / Kyber. use flint_sys::fmpz_poly::fmpz_poly_set_coeff_fmpz; use qfall_math::{ @@ -82,7 +76,7 @@ impl LossyCompressionFIPS203 for PolynomialRingZq { /// # Examples /// ``` /// use qfall_math::integer_mod_q::PolynomialRingZq; - /// use qfall_tools::utils::{common_moduli::new_anticyclic, lossy_compression::LossyCompressionFIPS203}; + /// use qfall_tools::{utils::common_moduli::new_anticyclic, compression::LossyCompressionFIPS203}; /// /// let modulus = new_anticyclic(16, 257).unwrap(); /// let mut poly = PolynomialRingZq::sample_uniform(&modulus); @@ -94,7 +88,10 @@ impl LossyCompressionFIPS203 for PolynomialRingZq { /// - if `d` is smaller than `1`. fn lossy_compress(&self, d: impl Into) -> Self::CompressedType { let d = d.into(); - assert!(d >= Z::ONE, "Performing this function with d < 1 implies reducing mod 1, leaving no information to recover. Choose a larger parameter d."); + assert!( + d >= Z::ONE, + "Performing this function with d < 1 implies reducing mod 1, leaving no information to recover. Choose a larger parameter d." + ); let two_pow_d = Z::from(2).pow(d).unwrap(); let q = self.get_mod().get_q(); let q_div_2 = q.div_floor(2); @@ -132,7 +129,7 @@ impl LossyCompressionFIPS203 for PolynomialRingZq { /// # Examples /// ``` /// use qfall_math::integer_mod_q::PolynomialRingZq; - /// use qfall_tools::utils::{common_moduli::new_anticyclic, lossy_compression::LossyCompressionFIPS203}; + /// use qfall_tools::{utils::common_moduli::new_anticyclic, compression::LossyCompressionFIPS203}; /// /// let modulus = new_anticyclic(16, 257).unwrap(); /// let mut poly = PolynomialRingZq::sample_uniform(&modulus); @@ -149,7 +146,10 @@ impl LossyCompressionFIPS203 for PolynomialRingZq { modulus: &Self::ModulusType, ) -> Self { let d = d.into(); - assert!(d >= Z::ONE, "Performing this function with d < 1 implies reducing mod 1, leaving no information to recover. Choose a larger parameter d."); + assert!( + d >= Z::ONE, + "Performing this function with d < 1 implies reducing mod 1, leaving no information to recover. Choose a larger parameter d." + ); let two_pow_d_minus_1 = Z::from(2).pow(d - 1).unwrap(); let two_pow_d = &two_pow_d_minus_1 * 2; let q = modulus.get_q(); @@ -190,7 +190,7 @@ impl LossyCompressionFIPS203 for MatPolynomialRingZq { /// # Examples /// ``` /// use qfall_math::integer_mod_q::MatPolynomialRingZq; - /// use qfall_tools::utils::{common_moduli::new_anticyclic, lossy_compression::LossyCompressionFIPS203}; + /// use qfall_tools::{utils::common_moduli::new_anticyclic, compression::LossyCompressionFIPS203}; /// /// let modulus = new_anticyclic(16, 257).unwrap(); /// let mut poly = MatPolynomialRingZq::sample_uniform(2, 3, &modulus); @@ -232,7 +232,7 @@ impl LossyCompressionFIPS203 for MatPolynomialRingZq { /// # Examples /// ``` /// use qfall_math::integer_mod_q::MatPolynomialRingZq; - /// use qfall_tools::utils::{common_moduli::new_anticyclic, lossy_compression::LossyCompressionFIPS203}; + /// use qfall_tools::{utils::common_moduli::new_anticyclic, compression::LossyCompressionFIPS203}; /// /// let modulus = new_anticyclic(16, 257).unwrap(); /// let mut poly = MatPolynomialRingZq::sample_uniform(2, 3, &modulus); @@ -270,7 +270,7 @@ impl LossyCompressionFIPS203 for MatPolynomialRingZq { #[cfg(test)] mod test_compression_poly { - use crate::utils::{common_moduli::new_anticyclic, lossy_compression::LossyCompressionFIPS203}; + use crate::{compression::LossyCompressionFIPS203, utils::common_moduli::new_anticyclic}; use qfall_math::{ integer::Z, integer_mod_q::PolynomialRingZq, @@ -340,7 +340,7 @@ mod test_compression_poly { #[cfg(test)] mod test_compression_matrix { - use crate::utils::{common_moduli::new_anticyclic, lossy_compression::LossyCompressionFIPS203}; + use crate::{compression::LossyCompressionFIPS203, utils::common_moduli::new_anticyclic}; use qfall_math::{ integer::Z, integer_mod_q::{MatPolynomialRingZq, PolynomialRingZq}, diff --git a/src/lib.rs b/src/lib.rs index 76f5a71..db099c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,25 +6,37 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! # What is qFALL-tools? -//! qFall-tools provides cryptographic basics such as commonly used tools in lattice-based cryptography, -//! mathematical primitives, and samplable distributions to prototype -//! lattice-based cryptographic constructions and more. -//! Actual constructions can be found in [qfall-schemes](https://github.com/qfall/schemes) +//! `qFALL` is a prototyping library for lattice-based cryptography. +//! `qFALL-tools` collects common sub-modules and features used by lattice-based constructions +//! to simplify and accelerate the development of such. +//! Among these are: +//! - [Compression techniques](crate::compression), +//! - [Primitives such as Preimage Samplable Functions (PSF)](crate::primitive), +//! - [Sampling algorithm using trapdoors](crate::sample), and +//! - [common functions for efficient prototyping](crate::utils) such as +//! - [common message encodings for encryption](crate::utils::common_encodings), +//! - [quick instantiations of common moduli for rings](crate::utils::common_moduli), as well as +//! - [rotation matrices](crate::utils::rotation_matrix). //! -//! Our library provides further primitives useful for prototyping such as -//! [`PSFs`](primitive::psf::PSF) that can be used to implement constructions. +//! The `qFALL` project contains two more crates called [`qFALL-math`](https://crates.io/crates/qfall-math) +//! and [`qFALL-schemes`](https://crates.io/crates/qfall-schemes) 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. //! -//! qFALL-tools 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 . +//! ## Quick Example +//! ``` +//! use qfall_tools::utils::{common_moduli::new_anticyclic, common_encodings::encode_value_in_polynomialringzq}; +//! use qfall_math::integer::Z; //! -//! ## Tutorial + Website -//! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-tools on our [website](https://qfall.github.io/). -//! The tutorial explains the basic steps starting from installation and -//! continues with basic usage. -//! qFALL-tools is co-developed together with qFALL-math and qFALL-schemes. +//! // Create X^256 + 1 mod 3329 +//! let poly_mod = new_anticyclic(256, 3329).unwrap(); +//! // Generate integer from string +//! let message = Z::from_utf8("Hello!"); +//! // Turn string into encoding q/2 and 0 for each 1 and 0 bit respectively +//! let mu_q_half = encode_value_in_polynomialringzq(message, 2, &poly_mod).unwrap(); +//! ``` +pub mod compression; pub mod primitive; pub mod sample; pub mod utils; diff --git a/src/primitive.rs b/src/primitive.rs index fed18a4..0e80ac8 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -6,8 +6,10 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains mathematical primitives that are useful for cryptographic -//! constructions/purposes, but themselves do not provide security guarantees like -//! confidentiality, integrity, ... +//! Contains primitives that are useful for cryptographic +//! constructions, but are solely targeted to be used in other constructions. +//! +//! More specifically, this module contains primitives that do not provide security +//! guarantees targeted at end-users such as confidentiality, integrity, etc. pub mod psf; diff --git a/src/primitive/psf.rs b/src/primitive/psf.rs index 0894c6f..8415c94 100644 --- a/src/primitive/psf.rs +++ b/src/primitive/psf.rs @@ -6,8 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! Contains the [`PSF`] trait, which is then subsequently implemented by -//! explicit constructions such as [`PSFGPV`]. +//! Contains implementations of Preimage Samplable Functions, short [`PSF`]. //! //! The main references are listed in the following //! and will be further referenced in submodules by these numbers: diff --git a/src/primitive/psf/gpv.rs b/src/primitive/psf/gpv.rs index 2f22162..cc9ef71 100644 --- a/src/primitive/psf/gpv.rs +++ b/src/primitive/psf/gpv.rs @@ -6,8 +6,8 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! Implements a GPV PSF according to [\[1\]](<../index.html#:~:text=[1]>) -//! using G-Trapdoors to generate a short basis and corresponding trapdoor. +//! Implements a GPV [`PSF`] according to [\[1\]](<../index.html#:~:text=[1]>) +//! using G-Trapdoors to generate a short basis trapdoors. use super::PSF; use crate::sample::g_trapdoor::{ diff --git a/src/primitive/psf/gpv_ring.rs b/src/primitive/psf/gpv_ring.rs index 60a77bc..e0611e8 100644 --- a/src/primitive/psf/gpv_ring.rs +++ b/src/primitive/psf/gpv_ring.rs @@ -6,9 +6,9 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! Implements a GPV PSF over the polynomial ring according to +//! Implements a GPV [`PSF`] over the polynomial ring according to //! [\[1\]](<../index.html#:~:text=[1]>) and [\[2\]](<../index.html#:~:text=[2]>) -//! using G-Trapdoors to generate a short basis and corresponding trapdoor. +//! using G-trapdoors to generate a short basis trapdoors. use super::PSF; use crate::{ diff --git a/src/primitive/psf/mp_perturbation.rs b/src/primitive/psf/mp_perturbation.rs index c5c65e4..f2b6a60 100644 --- a/src/primitive/psf/mp_perturbation.rs +++ b/src/primitive/psf/mp_perturbation.rs @@ -6,8 +6,8 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! Implements a Perturbation MP12 PSF according to [\[1\]](<../index.html#:~:text=[1]>) -//! using G-Trapdoors and corresponding trapdoor. +//! Implements a [`PSF`] based on perturbation sampling desribed in [\[1\]](<../index.html#:~:text=[1]>) +//! using G-trapdoors. use super::PSF; use crate::sample::g_trapdoor::{ @@ -404,8 +404,8 @@ impl PSF for PSFPerturbation { #[cfg(test)] mod test_psf_perturbation { - use super::PSFPerturbation; use super::PSF; + use super::PSFPerturbation; use crate::sample::g_trapdoor::gadget_parameters::GadgetParameters; use qfall_math::integer::MatZ; use qfall_math::rational::Q; diff --git a/src/sample.rs b/src/sample.rs index 7050e78..fc3c177 100644 --- a/src/sample.rs +++ b/src/sample.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 anything that should be easily samplable for lattice-based -//! cryptography. +//! Contains anything that should be easily samplable for lattice-based +//! cryptography, which is more complex than sampling from a distribution. pub mod g_trapdoor; diff --git a/src/sample/g_trapdoor.rs b/src/sample/g_trapdoor.rs index a5fd540..307a3cf 100644 --- a/src/sample/g_trapdoor.rs +++ b/src/sample/g_trapdoor.rs @@ -6,10 +6,8 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! A G-Trapdoor is a form of a trapdoor for lattices -//! that allows for very efficient sampling. -//! This module contains implementations for G-Trapdoors in the classical and -//! in the ring setting. +//! Contains implementations for G-trapdoors in the classical and +//! ring setting, enabling efficient preimage sampling. //! //! The main references are listed in the following //! and will be further referenced in submodules by these numbers: diff --git a/src/sample/g_trapdoor/gadget_default.rs b/src/sample/g_trapdoor/gadget_default.rs index 565f7ac..74b5883 100644 --- a/src/sample/g_trapdoor/gadget_default.rs +++ b/src/sample/g_trapdoor/gadget_default.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 functions to generate G-Trapdoors with default parameters. +//! This module contains functions to generate G-trapdoors with default parameters. use crate::sample::g_trapdoor::{ gadget_classical::gen_trapdoor, diff --git a/src/sample/g_trapdoor/gadget_ring.rs b/src/sample/g_trapdoor/gadget_ring.rs index a5eee10..b775020 100644 --- a/src/sample/g_trapdoor/gadget_ring.rs +++ b/src/sample/g_trapdoor/gadget_ring.rs @@ -7,7 +7,7 @@ // Mozilla Foundation. See . //! This module contains an implementation to generate a gadget trapdoor in a -//! ring-lwe setting. +//! Ring-LWE setting. use super::{gadget_classical::find_solution_gadget_mat, gadget_parameters::GadgetParametersRing}; use qfall_math::{ diff --git a/src/sample/g_trapdoor/short_basis_classical.rs b/src/sample/g_trapdoor/short_basis_classical.rs index 26ffb9d..dfec7c1 100644 --- a/src/sample/g_trapdoor/short_basis_classical.rs +++ b/src/sample/g_trapdoor/short_basis_classical.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 an implementation to generate a short basis from a G-Trapdoor +//! This module contains an implementation to generate a short basis from a G-trapdoor //! and its parity check matrix. use super::{ diff --git a/src/sample/g_trapdoor/short_basis_ring.rs b/src/sample/g_trapdoor/short_basis_ring.rs index 07fc7bd..a9de5ae 100644 --- a/src/sample/g_trapdoor/short_basis_ring.rs +++ b/src/sample/g_trapdoor/short_basis_ring.rs @@ -7,7 +7,7 @@ // Mozilla Foundation. See . //! This module contains an implementation to generate a short basis from a ring-based -//! G-Trapdoor and its parity check matrix. +//! G-trapdoor and its parity check matrix. use super::{gadget_parameters::GadgetParametersRing, gadget_ring::find_solution_gadget_ring}; use qfall_math::{ diff --git a/src/sample/g_trapdoor/trapdoor_distribution.rs b/src/sample/g_trapdoor/trapdoor_distribution.rs index 4c73539..ada5e63 100644 --- a/src/sample/g_trapdoor/trapdoor_distribution.rs +++ b/src/sample/g_trapdoor/trapdoor_distribution.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 all implementation of TrapdoorDistributions from +//! This module contains all implementation of trapdoor distributions from //! which the matrix `A_bar` is sampled in the trapdoor generation algorithm. use qfall_math::{ diff --git a/src/utils.rs b/src/utils.rs index 4177430..a29a2dc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,11 +6,8 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains common functions that are used by sub functions. -//! -//! This can include specialized implementations for certain parameter sets, such as rotation matrices. +//! This module contains commonly functions in lattice-based cryptography. pub mod common_encodings; pub mod common_moduli; -pub mod lossy_compression; pub mod rotation_matrix; diff --git a/src/utils/common_encodings.rs b/src/utils/common_encodings.rs index ca3e6e8..2f7db20 100644 --- a/src/utils/common_encodings.rs +++ b/src/utils/common_encodings.rs @@ -6,53 +6,282 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module contains functions to encode and decode data with commonly used -//! encodings. +//! Contains message encoding and decoding functions commonly used in lattice-based +//! encryption schemes. use qfall_math::{ + error::MathError, integer::{PolyOverZ, Z}, - integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq, Zq}, - traits::{Distance, GetCoefficient, SetCoefficient}, + integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq}, + traits::{GetCoefficient, SetCoefficient}, }; -/// Turns a [`Z`] instance into its bit representation, converts this bit representation -/// into a [`PolynomialRingZq`] with entries q/2 for any 1-bit and 0 as coefficient for any 0-bit. -pub fn encode_z_bitwise_in_polynomialringzq( +/// Takes any non-negative integer value, represents it with respect to `base` +/// and generates a [`PolynomialRingZq`] containing the previous representation w.r.t. `base` +/// across its coefficients multiplied by `q/base`. +/// This function is commonly used in encryption algorithms of lattice-based PKE schemes +/// and described as `⌊q/base * μ⌋`, where `μ ∈ R_{base}^n`. +/// +/// Parameters: +/// - `value`: the non-negative integer value to encode +/// - `base`: defines the encoded representation, usually chosen as 2 for binary representation +/// - `modulus`: specifies the modulus of the returned struct and `q` +/// +/// Returns a [`PolynomialRingZq`] containing `⌊q/base * μ⌋` as described above or a [`MathError`] +/// if `base < 2`, `value < 0`, or `value` represented w.r.t. `base` requires more than `modulus.get_degree()` coefficients. +/// +/// # Examples +/// ``` +/// use qfall_tools::utils::common_encodings::encode_value_in_polynomialringzq; +/// use qfall_tools::utils::common_moduli::new_anticyclic; +/// +/// let modulus = new_anticyclic(16, 257).unwrap(); +/// let base = 2; +/// let value = u16::MAX; +/// +/// let encoded = encode_value_in_polynomialringzq(value, base, &modulus).unwrap(); +/// ``` +/// +/// # Errors and Failures +/// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) +/// if `value` is negative, `value` represented w.r.t. `base` has more digits than coefficients available in `modulus.get_degree()`, +/// or `base < 2`. +pub fn encode_value_in_polynomialringzq( + value: impl Into, + base: impl Into, modulus: &ModulusPolynomialRingZq, - mu: &Z, -) -> PolynomialRingZq { +) -> Result { + let mut value = value.into(); + let base = base.into(); let modulus: ModulusPolynomialRingZq = modulus.into(); - let q_half = modulus.get_q().div_floor(2); - let bits = mu.to_bits(); - let mut mu_q_half = PolynomialRingZq::from((PolyOverZ::default(), &modulus)); - for (i, bit) in bits.iter().enumerate() { - if *bit { - mu_q_half.set_coeff(i, &q_half).unwrap(); + if value < Z::ZERO { + return Err(MathError::InvalidIntegerInput(format!( + "The given value {value} needs to be non-negative." + ))); + } + + let min_req_degree = u64::try_from((&value + Z::ONE).log_ceil(&base)?)?; + if min_req_degree > modulus.get_degree() as u64 { + return Err(MathError::InvalidIntegerInput(format!( + "The given value requires {min_req_degree} digits represented w.r.t. base {base}. Your modulus only provides space for {} digits.", + modulus.get_degree() + ))); + } + + // get representation of value w.r.t. base as base + let mut base_repr = Vec::with_capacity(min_req_degree as usize); + while value > Z::ZERO { + let digit = &value % &base; + base_repr.push(digit); + value = value.div_floor(&base); + } + + let mut res = PolyOverZ::default(); + for (i, digit) in base_repr.iter().enumerate() { + if digit != &Z::ZERO { + unsafe { res.set_coeff_unchecked(i as i64, digit) }; } } - mu_q_half + // spread out each represented value by factor `q / base` + let q_div_base = modulus.get_q().div_floor(&base); + res *= q_div_base; + + Ok(PolynomialRingZq::from((res, modulus))) } -/// Checks for each coefficient of `poly` whether its value is closer to q/2 or 0 -/// and sets the corresponding bit in the returned [`Z`] value to 1 or 0 respectively. -pub fn decode_z_bitwise_from_polynomialringzq(modulus: impl Into, poly: &PolynomialRingZq) -> Z { - let q_half = modulus.into().div_floor(2); - - // check for each coefficient whether it's closer to 0 or q/2 - // if closer to q/2 -> add 2^i to result - let mut vec = vec![]; - for i in 0..=poly.get_degree() { - let coeff: Zq = poly.get_coeff(i).unwrap(); - let coeff: Z = coeff.get_representative_least_absolute_residue(); - - if coeff.distance(&q_half) < coeff.distance(Z::ZERO) { - vec.push(true); - } else { - vec.push(false); - } +/// Takes an encoded [`PolynomialRingZq`] and decodes it w.r.t. `base` and `q`, effectively performing +/// `μ = ⌈base/q * poly⌋ mod base` for `poly ∈ R_q^n`. Then, it takes any value of `μ ∈ R_{base}^n` and +/// turns it into a non-negative integer of type [`Z`]. +/// This function is commonly used in decryption algorithms of lattice-based PKE schemes and invers to +/// [`encode_value_in_polynomialringzq`]. +/// +/// Parameters: +/// - `poly`: the [`PolynomialRingZq`] containing the encoded value +/// - `base`: defines the encoded representation, usually chosen as 2 for binary representation +/// +/// Returns a [`Z`] containing the value of the vector `⌈base/q * poly⌋ mod base ∈ R_{base}^n` as a decimal number +/// as described above or a [`MathError`] if `base < 2`. +/// +/// # Examples +/// ``` +/// use qfall_tools::utils::common_encodings::{encode_value_in_polynomialringzq, decode_value_from_polynomialringzq}; +/// use qfall_tools::utils::common_moduli::new_anticyclic; +/// +/// let modulus = new_anticyclic(16, 257).unwrap(); +/// let base = 2; +/// let value = u16::MAX; +/// +/// let encoded = encode_value_in_polynomialringzq(value, base, &modulus).unwrap(); +/// let decoded = decode_value_from_polynomialringzq(&encoded, base).unwrap(); +/// +/// assert_eq!(value, decoded); +/// ``` +/// +/// # Errors and Failures +/// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) +/// if `base < 2`. +pub fn decode_value_from_polynomialringzq( + poly: &PolynomialRingZq, + base: impl Into, +) -> Result { + let base = base.into(); + let q = poly.get_mod().get_q(); + let q_div_2base = q.div_floor(2 * &base); + + if base <= Z::ONE { + return Err(MathError::InvalidIntegerInput(format!( + "The given base {base} is smaller than 2, which does not allow the encoding of any information." + ))); } - Z::from_bits(&vec) + let mut poly = poly.get_representative_least_nonnegative_residue(); + poly *= &base; + + let mut out = Z::default(); + + for i in (0..=poly.get_degree()).rev() { + let mut coeff = unsafe { poly.get_coeff_unchecked(i) }; + coeff += &q_div_2base; + let res = coeff.div_floor(&q) % &base; + out *= &base; + out += res; + } + + Ok(out) +} + +#[cfg(test)] +mod test_encode_value_in_polynomialringzq { + use crate::utils::{ + common_encodings::encode_value_in_polynomialringzq, common_moduli::new_anticyclic, + }; + use qfall_math::{integer::Z, traits::GetCoefficient}; + + /// Ensures that [`encode_value_in_polynomialringzq`] works properly for `base = 2`. + #[test] + fn binary() { + let q = 257; + let q_half = q / 2; + let modulus = new_anticyclic(16, q).unwrap(); + + let res0 = encode_value_in_polynomialringzq(1, 2, &modulus).unwrap(); + let res1 = encode_value_in_polynomialringzq(2, 2, &modulus).unwrap(); + let res2 = encode_value_in_polynomialringzq(3, 2, &modulus).unwrap(); + + assert_eq!(GetCoefficient::::get_coeff(&res0, 0).unwrap(), q_half); + assert_eq!(res0.get_degree(), 0); + + assert_eq!(GetCoefficient::::get_coeff(&res1, 0).unwrap(), 0); + assert_eq!(GetCoefficient::::get_coeff(&res1, 1).unwrap(), q_half); + assert_eq!(res1.get_degree(), 1); + + assert_eq!(GetCoefficient::::get_coeff(&res2, 0).unwrap(), q_half); + assert_eq!(GetCoefficient::::get_coeff(&res2, 1).unwrap(), q_half); + assert_eq!(res2.get_degree(), 1); + } + + /// Ensures that [`encode_value_in_polynomialringzq`] works properly for `base = 3`. + #[test] + fn ternary() { + let q = 257; + let q_third = q / 3; + let modulus = new_anticyclic(16, q).unwrap(); + + let res0 = encode_value_in_polynomialringzq(1, 3, &modulus).unwrap(); + let res1 = encode_value_in_polynomialringzq(2, 3, &modulus).unwrap(); + let res2 = encode_value_in_polynomialringzq(3, 3, &modulus).unwrap(); + + assert_eq!(GetCoefficient::::get_coeff(&res0, 0).unwrap(), q_third); + assert_eq!(res0.get_degree(), 0); + + assert_eq!( + GetCoefficient::::get_coeff(&res1, 0).unwrap(), + 2 * q_third + ); + assert_eq!(res1.get_degree(), 0); + + assert_eq!(GetCoefficient::::get_coeff(&res2, 0).unwrap(), 0); + assert_eq!(GetCoefficient::::get_coeff(&res2, 1).unwrap(), q_third); + assert_eq!(res2.get_degree(), 1); + } + + /// Ensures that [`encode_value_in_polynomialringzq`] returns an error if there is not enough space due to modulus size constraints. + #[test] + fn not_enough_space() { + let modulus = new_anticyclic(16, 257).unwrap(); + + let res = encode_value_in_polynomialringzq(u16::MAX as u32 + 1, 2, &modulus); + + assert!(res.is_err()); + } + + /// Ensures that [`encode_value_in_polynomialringzq`] returns an error if `base < 2`. + #[test] + fn too_small_base() { + let modulus = new_anticyclic(16, 257).unwrap(); + + let res = encode_value_in_polynomialringzq(4, 1, &modulus); + + assert!(res.is_err()); + } + + /// Ensures that [`encode_value_in_polynomialringzq`] returns an error if `value < 0`. + #[test] + fn neagive_value() { + let modulus = new_anticyclic(16, 257).unwrap(); + + let res = encode_value_in_polynomialringzq(-1, 1, &modulus); + + assert!(res.is_err()); + } +} + +#[cfg(test)] +mod test_decode_value_from_polynomialringzq { + use crate::utils::{ + common_encodings::{decode_value_from_polynomialringzq, encode_value_in_polynomialringzq}, + common_moduli::new_anticyclic, + }; + use qfall_math::{integer::Z, integer_mod_q::PolynomialRingZq}; + + /// Ensures that encoded information can be decoded without losing information for `base = 2`. + #[test] + fn round_trip_binary() { + let q = 257; + let base = 2; + let modulus = new_anticyclic(17, q).unwrap(); + let msg = Z::sample_uniform(0, u16::MAX).unwrap(); + + let encoding = encode_value_in_polynomialringzq(&msg, base, &modulus).unwrap(); + let decoding = decode_value_from_polynomialringzq(&encoding, base).unwrap(); + + assert_eq!(msg, decoding); + } + + /// Ensures that encoded information can be decoded without losing information for `base = 3`. + #[test] + fn round_trip_ternary() { + let q = 257; + let base = 3; + let modulus = new_anticyclic(16, q).unwrap(); + let msg = Z::sample_uniform(0, u16::MAX).unwrap(); + + let encoding = encode_value_in_polynomialringzq(&msg, base, &modulus).unwrap(); + let decoding = decode_value_from_polynomialringzq(&encoding, base).unwrap(); + + assert_eq!(msg, decoding); + } + + /// Ensures that [`decode_value_from_polynomialringzq`] returns an error if `base < 2`. + #[test] + fn too_small_base() { + let modulus = new_anticyclic(16, 257).unwrap(); + let poly = PolynomialRingZq::sample_uniform(&modulus); + + let res = decode_value_from_polynomialringzq(&poly, 1); + + assert!(res.is_err()); + } } diff --git a/src/utils/common_moduli.rs b/src/utils/common_moduli.rs index 9d6ede9..3e9064c 100644 --- a/src/utils/common_moduli.rs +++ b/src/utils/common_moduli.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 functions to quickly instantiate +//! Contains functions to quickly instantiate //! common moduli for ring-based lattice cryptography. use qfall_math::{ diff --git a/src/utils/rotation_matrix.rs b/src/utils/rotation_matrix.rs index 4a6f617..eace492 100644 --- a/src/utils/rotation_matrix.rs +++ b/src/utils/rotation_matrix.rs @@ -6,9 +6,8 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! This module includes a specialized implementation called rotation matrices, -//! which find application in ring-based implementations for the special rings of the form -//! `Z[X]/(X^n + 1)`. +//! Contains functions for generating rotation matrices used +//! when working over the ring `Z[X]/(X^n + 1)`. use qfall_math::{integer::MatZ, traits::*};