diff --git a/Cargo.lock b/Cargo.lock index 8c86fea..e1c28a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,46 +10,15 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", @@ -63,15 +32,14 @@ dependencies = [ "num-traits", "rand", "rand_core", - "rayon", "zeroize", ] [[package]] name = "libc" -version = "0.2.175" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "num-traits" @@ -93,18 +61,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -139,31 +107,11 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "syn" -version = "2.0.106" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -172,9 +120,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "wasi" @@ -184,18 +132,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", @@ -204,6 +152,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/Cargo.toml b/Cargo.toml index 7570ae1..3665f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,15 @@ [package] name = "gwrizienn" version = "0.1.0" +authors = ["tuxmain "] edition = "2024" +license = "AGPL-3.0-only" +description = "Easy and fast modular arithmetic, polynomial quotient rings" +categories = ["mathematics", "no-std"] +keywords = ["arithmetic", "polynomial", "ntt", "lattice-crypto"] +homepage = "https://git.zoai.re/tuxmain/gwrizienn" +repository = "https://git.zoai.re/tuxmain/gwrizienn" +documentation = "https://docs.rs/gwrizienn" [dependencies] num-traits = "0.2" @@ -9,9 +17,6 @@ rand = { version = "0.8", optional = true } rand_core = { version = "0.6", optional = true } zeroize = { version = "1", optional = true, default-features = false } -[dev-dependencies] -rayon = "1.8" - [features] default = ["rand", "zeroize"] diff --git a/README.md b/README.md index df07f47..ccb111f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ Modular arithmetic, polynomial quotient rings in Rust. -Early development, really. - Goals: * Easy to use, obvious when you look at the documentation and type system * Fast, most possible work is done at compile time @@ -13,12 +11,20 @@ Non-goals: * Generic (it's generic but only for primitive types) * Complete (it's simple because it's not a complete implementation of modern algebra) * Dynamic (vector dimensions and moduli are strongly typed) +* Footgun-free (if you give incoherent arguments to the macros, it may produce unsafe code) Supported: * ring Zq * ring Zq/(x^N+1) with additive operations * ring Zq/(x^N+1) with multiplicative operations if q=p or q=2p with p prime and 2N divides p-1 * vectors and matrices of the above rings +* lift between different rings (see example ntwe) (lifting may lack some features or be unsafe) + +**Warning**: There are some TODOs in the code. The code has not been audited nor proven. Please don't use it in production yet. + +## Why + +When implementing Dilithium and other similar lattice-based schemes, I found no crate that was both easy and fast, so I made one with the exact set of features I needed. ## Name @@ -28,7 +34,7 @@ Pronounce _grizienn_. It's Breton for "root", because we use roots of unity to c [Support me via LiberaPay](https://liberapay.com/tuxmain/donate) -GNU AGPL v3, CopyLeft 2025 Pascal Engélibert [(why copyleft?)](https://txmn.tk/blog/why-copyleft/) +GNU AGPL v3, CopyLeft 2025-2026 Pascal Engélibert [(why copyleft?)](https://txmn.tk/blog/why-copyleft/) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. diff --git a/src/lib.rs b/src/lib.rs index da33889..9f9b40a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,8 @@ #![allow(clippy::tabs_in_doc_comments)] //! Modular and polynomial arithmetic. +//! +//! See the examples in the repo. pub mod matrix; pub mod ntt; diff --git a/src/r7681.rs b/src/r7681.rs deleted file mode 100644 index fb975b8..0000000 --- a/src/r7681.rs +++ /dev/null @@ -1,232 +0,0 @@ -crate::ring!(Z7681, u16, u32, i32, 7681); - -/// Element of (Z/2Z)[x]/(x^N+1) - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] - pub struct R7681(pub [Z7681; N]); - - impl crate::poly::Poly for R7681 { - type Element = Z7681; - fn modulus() -> u16 { - 7681 - } - fn new(v: [Z7681; N]) -> Self { - Self(v) - } - } - - impl R7681 { - pub const fn one() -> Self { - let mut x = Self([Z7681(0); N]); - x.0[0].0 = 1; - x - } - pub fn is_one(&self) -> bool { - self.0[0].0 == 1 && self.0.iter().skip(1).all(|x| x.0 == 0) - } - pub const fn zetas() -> [u16; N] { - let mut zetas = [0; N]; - let mut i: usize = 1; - let mut z = 62; - while i < N { - zetas[crate::poly::bitrev(i, N)] = z; - z = (z as u32 * 62 % 7681) as u16; - i += 1; - } - zetas - } - } - - impl std::ops::Add for R7681 { - type Output = Self; - fn add(mut self, rhs: Self) -> Self { - self.0 - .iter_mut() - .zip(rhs.0.iter()) - .for_each(|(x, y)| *x += *y); - self - } - } - - impl std::ops::Add<&Self> for R7681 { - type Output = Self; - fn add(mut self, rhs: &Self) -> Self { - self.0 - .iter_mut() - .zip(rhs.0.iter()) - .for_each(|(x, y)| *x += *y); - self - } - } - - impl std::ops::AddAssign for R7681 { - fn add_assign(&mut self, rhs: Self) { - self.0 - .iter_mut() - .zip(rhs.0.iter()) - .for_each(|(x, y)| *x += *y); - } - } - - impl std::ops::AddAssign<&Self> for R7681 { - fn add_assign(&mut self, rhs: &Self) { - self.0 - .iter_mut() - .zip(rhs.0.iter()) - .for_each(|(x, y)| *x += *y); - } - } - - impl std::ops::Sub for R7681 { - type Output = Self; - fn sub(mut self, rhs: Self) -> Self { - self.0 - .iter_mut() - .zip(rhs.0.iter()) - .for_each(|(x, y)| *x -= *y); - self - } - } - - impl std::ops::Sub<&Self> for R7681 { - type Output = Self; - fn sub(mut self, rhs: &Self) -> Self { - self.0 - .iter_mut() - .zip(rhs.0.iter()) - .for_each(|(x, y)| *x -= *y); - self - } - } - - impl std::ops::SubAssign for R7681 { - fn sub_assign(&mut self, rhs: Self) { - self.0 - .iter_mut() - .zip(rhs.0.iter()) - .for_each(|(x, y)| *x -= *y); - } - } - - impl std::ops::SubAssign<&Self> for R7681 { - fn sub_assign(&mut self, rhs: &Self) { - self.0 - .iter_mut() - .zip(rhs.0.iter()) - .for_each(|(x, y)| *x -= *y); - } - } - - impl num_traits::Zero for R7681 { - fn zero() -> Self { - Self([Z7681(0); N]) - } - fn is_zero(&self) -> bool { - self.0.iter().all(num_traits::Zero::is_zero) - } - } - - /*impl num_traits::One for R7681 { - fn one() -> Self { - let mut x = Self([$Zq(0); $N]); - x.0[0].0 = 1; - x - } - fn is_one(&self) -> bool { - self.0[0].0 == 1 && self.0.iter().skip(1).all(|x| x.0 == 0) - } - }*/ - - impl crate::ntt::Ntt for R7681 { - type Output = crate::ntt::NttDomain>; - fn ntt(mut self) -> crate::ntt::NttDomain> { - let mut m = 0; - let mut len = N / 2; - while len >= 1 { - for start in (0..N).step_by(2 * len) { - m += 1; - let z = Z7681(R7681::::zetas()[m]); - for j in start..start + len { - let t = z * self.0[j + len]; - self.0[j + len] = self.0[j] - t; - self.0[j] += t; - } - } - len /= 2; - } - crate::ntt::NttDomain(self) - } - } - - impl crate::ntt::NttInv for crate::ntt::NttDomain> { - type Output = R7681; - fn ntt_inv(mut self) -> R7681 { - let mut m = N; - let mut len = 1; - while len < N { - for start in (0..N).step_by(2 * len) { - m -= 1; - let z = Z7681(7681 - R7681::::zetas()[m]); - for j in start..start + len { - let t = self.0.0[j]; - self.0.0[j] += self.0.0[j + len]; - self.0.0[j + len] = z * (t - self.0.0[j + len]); - } - } - len *= 2; - } - self.0.0.iter_mut().for_each(|wj| *wj *= Z7681(N as u16).inverse()); - self.0 - } - } - - impl std::ops::Mul for crate::ntt::NttDomain> { - type Output = Self; - fn mul(mut self, rhs: Self) -> Self { - self.0 - .0 - .iter_mut() - .zip(rhs.0.0.iter()) - .for_each(|(x, y)| *x *= *y); - self - } - } - - impl std::ops::Mul<&Self> for crate::ntt::NttDomain> { - type Output = Self; - fn mul(mut self, rhs: &Self) -> Self { - self.0 - .0 - .iter_mut() - .zip(rhs.0.0.iter()) - .for_each(|(x, y)| *x *= *y); - self - } - } - - impl std::ops::MulAssign for crate::ntt::NttDomain> { - fn mul_assign(&mut self, rhs: Self) { - self.0 - .0 - .iter_mut() - .zip(rhs.0.0.iter()) - .for_each(|(x, y)| *x *= *y); - } - } - - impl std::ops::MulAssign<&Self> for crate::ntt::NttDomain> { - fn mul_assign(&mut self, rhs: &Self) { - self.0 - .0 - .iter_mut() - .zip(rhs.0.0.iter()) - .for_each(|(x, y)| *x *= *y); - } - } - - impl num_traits::Inv for crate::ntt::NttDomain> { - type Output = Self; - fn inv(mut self) -> Self { - self.0.0.iter_mut().for_each(|x| *x = x.inverse()); - self - } - }