diff --git a/.gitignore b/.gitignore index d051a2f..b152106 100644 --- a/.gitignore +++ b/.gitignore @@ -25,8 +25,8 @@ dump *.swp *.swo -# coverage -lcov.info +## macOS +.DS_Store # sage *.sage.py diff --git a/Cargo.toml b/Cargo.toml index d443d09..c991542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,6 @@ ark-std ={ git="https://github.com/arkworks-rs/std/" } [[example]] name="aes_chained_cbc" + +[[example]] +name="symmetric_group" diff --git a/README.md b/README.md index c9007af..33c184f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Ronkathon is a rust implementation of a collection of cryptographic primitives. It is inspired by the common python plonkathon repository, and plonk-by-hand. We use the same curve and field as plonk-by-hand (not secure), and are working towards building everything from scratch to understand everything from first principles. ## Primitives +- [Finite Group](src/field/group.rs) - [Fields and Their Extensions](src/field/README.md) - [Binary Fields](src/field/binary_towers/README.md) - [Curves and Their Pairings](src/curve/README.md) @@ -27,6 +28,7 @@ Ronkathon is a rust implementation of a collection of cryptographic primitives. - [KZG Commitments](src/kzg/README.md) - [Reed-Solomon Codes](src/codes/README.md) - [Merkle Proofs](src/tree/README.md) +- [DSL](src/compiler/README.md) ### Signatures - [Tiny ECDSA](src/ecdsa.rs) @@ -34,6 +36,8 @@ Ronkathon is a rust implementation of a collection of cryptographic primitives. ### Encryption - [RSA](src/encryption/asymmetric/rsa/README.md) - [DES](src/encryption/symmetric/des/README.md) +- [AES](src/encryption/symmetric/aes/README.md) +- [ChaCha](src/encryption/symmetric/chacha/README.md) ### Hash - [Sha256 Hash](src/hashes/README.md) diff --git a/examples/symmetric_group.rs b/examples/symmetric_group.rs new file mode 100644 index 0000000..ff55063 --- /dev/null +++ b/examples/symmetric_group.rs @@ -0,0 +1,135 @@ +//! Implements [dihedral][dihedral] group of degree 3 and order 6 using ronkathon's [`Group`] and +//! [`FiniteGroup`] trait. +//! +//! Consider a symmetric group containing all permutation of 3 distinct +//! elements: `[a, b, c]`. Total number of elements is 3! = 6. Each element of the group is a +//! permutation operation. +//! +//! ## Example +//! Let `a=[2, 1, 3]` be an element of the group, when applied to any 3-length vector performs the +//! swap of 1st and 2nd element. So, `RGB->GRB`. +//! +//! ## Operation +//! Group operation is defined as combined action of performing permutation twice, i.e. take `x,y` +//! two distinct element of the group. `a·b` is applying permutation `b` first then `a`. +//! +//! [dihedral]: https://en.wikipedia.org/wiki/Dihedral_group_of_order_6 +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use ronkathon::algebra::{ + group::{FiniteGroup, Group}, + Finite, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct DihedralGroup { + mapping: [usize; 3], +} + +impl Finite for DihedralGroup { + const ORDER: usize = 6; +} + +impl Group for DihedralGroup { + type Scalar = usize; + + const IDENTITY: Self = Self::new([0, 1, 2]); + + fn op(&self, rhs: &Self) -> Self { + let mut new_mapping = [0; 3]; + for (i, &j) in self.mapping.iter().enumerate() { + new_mapping[i] = rhs.mapping[j]; + } + + Self::new(new_mapping) + } + + fn inverse(&self) -> Option { + let mut inverse_mapping = [0; 3]; + for (i, &j) in self.mapping.iter().enumerate() { + inverse_mapping[j] = i; + } + + Some(Self::new(inverse_mapping)) + } + + fn scalar_mul(&self, scalar: Self::Scalar) -> Self { + let mut res = *self; + for _ in 0..scalar { + res = res.op(self); + } + res + } +} + +impl DihedralGroup { + const fn new(mapping: [usize; 3]) -> Self { Self { mapping } } +} + +impl FiniteGroup for DihedralGroup {} + +impl Default for DihedralGroup { + fn default() -> Self { Self::IDENTITY } +} + +impl Add for DihedralGroup { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { Self::op(&self, &rhs) } +} + +impl AddAssign for DihedralGroup { + fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } +} + +impl Neg for DihedralGroup { + type Output = Self; + + fn neg(self) -> Self::Output { Self::inverse(&self).expect("inverse does not exist") } +} + +impl Sub for DihedralGroup { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { self + -rhs } +} + +impl SubAssign for DihedralGroup { + fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } +} + +impl Mul for DihedralGroup { + type Output = Self; + + fn mul(self, rhs: usize) -> Self::Output { Self::scalar_mul(&self, rhs) } +} + +impl MulAssign for DihedralGroup { + fn mul_assign(&mut self, rhs: usize) { *self = *self * rhs; } +} + +fn main() { + let ident = DihedralGroup::default(); + let a = DihedralGroup::new([1, 0, 2]); + let b = DihedralGroup::new([0, 2, 1]); + + // closure + let ab = a.op(&b); + let ba = b.op(&a); + + // identity + assert_eq!(a, ident.op(&a)); + assert_eq!(b, ident.op(&b)); + + // non-abelian property + assert_ne!(ab, ba); + + // group element order + assert_eq!(a.order(), 2); + assert_eq!(ab.order(), 3); + assert_eq!(ba.order(), 3); + + // inverse + assert_eq!(a.op(&a.inverse().unwrap()), ident); + assert_eq!(ab.inverse().unwrap(), ba); +} diff --git a/src/algebra/README.md b/src/algebra/README.md new file mode 100644 index 0000000..1113b6a --- /dev/null +++ b/src/algebra/README.md @@ -0,0 +1,46 @@ +# Algebra + +[Groups](https://en.wikipedia.org/wiki/Group_(mathematics)) $G$ are algebraic structures which are set and has a binary operation $\cdot$ that combines two elements $a, b$ of the set to produce a third element $a\cdot b$ in the set. +The operation is said to have following properties: +1. Closure: $a\cdot b=c\in G$ +2. Associative: $(a\cdot b)\cdot c = a\cdot(b\cdot c)$ +3. Existence of Identity element: $a\cdot 0 = 0\cdot a = a$ +4. Existence of inverse element for every element of the set: $a\cdot b=0$ +5. Commutativity: Groups which satisfy an additional property: *commutativity* on the set of + elements are known as **Abelian groups**, $a\cdot b=b\cdot a$. + +Examples: +- $Z$ is a group under addition defined as $(Z,+)$ +- $(Z/nZ)^{\times}$ is a finite group under multiplication +- Equilateral triangles for a non-abelian group of order 6 under symmetry. +- Invertible $n\times n$ form a non-abelian group under multiplication. + +[Rings](https://en.wikipedia.org/wiki/Ring_(mathematics)) are algebraic structures with two binary operations $R(+, \cdot)$ satisfying the following properties: +1. Abelian group under + +2. Monoid under $\cdot$ +3. Distributive with respect to $\cdot$ + +Examples: +- $Z/nZ$ form a ring +- a polynomial with integer coefficients: $Z[x]$ satisfy ring axioms + +[Fields](https://en.wikipedia.org/wiki/Field_(mathematics)) are algebraic structures with two binary operations $F(+,\cdot)$ such that: +- Abelian group under $+$ +- Non-zero numbers $F-\{0\}$ form abelian group under $\cdot$ +- Multiplicative distribution over $+$ + +> Fields can also be defined as commutative ring $(R,+,\cdot)$ with existence of inverse under $\cdot$. Thus, every Field can be classified as Ring. + +Examples: +- $\mathbb{Q,R,C}$, i.e. set of rational, real and complex numbers are all Fields. +- $\mathbb{Z}$ is **not** a field. +- $Z/nZ$ form a finite field under modulo addition and multiplication. + +[Finite fields](https://en.wikipedia.org/wiki/Finite_field) are field with a finite order and are instrumental in cryptographic systems. They are used in elliptic curve cryptography, RSA, and many other cryptographic systems. +This module provides a generic implementation of finite fields via two traits and two structs. +It is designed to be easy to use and understand, and to be flexible enough to extend yourself. + +## Constant (Compile Time) Implementations +Note that traits defined in this module are tagged with `#[const_trait]` which implies that each method and associated constant is implemented at compile time. +This is done purposefully as it allows for generic implementations of these fields to be constructed when you compile `ronkathon` rather than computed at runtime. +In principle, this means the code runs faster, but will compile slower, but the tradeoff is that the cryptographic system is faster and extensible. \ No newline at end of file diff --git a/src/field/README.md b/src/algebra/field/README.md similarity index 74% rename from src/field/README.md rename to src/algebra/field/README.md index 774757b..34e1d66 100644 --- a/src/field/README.md +++ b/src/algebra/field/README.md @@ -1,13 +1,15 @@ -# Fields -[Finite fields](https://en.wikipedia.org/wiki/Finite_field) are instrumental in cryptographic systems. -They are used in elliptic curve cryptography, RSA, and many other cryptographic systems. -This module provides a generic implementation of finite fields via two traits and two structs. -It is designed to be easy to use and understand, and to be flexible enough to extend yourself. +# Field ## Traits + +- `FiniteField`: a field $(\mathbb{F}_p, +,\cdot)$ where $p$ is a prime number. +- `ExtensionField`: an extension of a field $(\mathbb{F}_{p^k}, +,\cdot)$ where $p$ is a prime number and $\mathbb{F}_{p^k}$ is an extension of $\mathbb{F}_p$. + + The two traits used in this module are `FiniteField` and `ExtensionField` which are located in the `field` and `field::extension` modules respectively. These traits are interfacial components that provide the necessary functionality for field-like objects to adhere to to be used in cryptographic systems. + ### `FiniteField` The `FiniteField` trait is used to define a finite field in general. The trait itself mostly requires functionality from traits in the Rust [`core::ops`](https://doc.rust-lang.org/core/ops/) module such as `Add`, `Sub`, `Mul`, and `Div` (and their corresponding assignment, iterator, and related operations). @@ -18,7 +20,7 @@ A bit more specifically, the `FiniteField` trait requires the following associat - `const ZERO: Self` - The additive identity. - `const ONE: Self` - The multiplicative identity. - `const PRIMITIVE_ELEMENT: Self` - A [primitive element](https://en.wikipedia.org/wiki/Primitive_element_(finite_field)) of the field, that is, a generator of the multiplicative subgroup of the field. -- `inverse(&self) -> Option` - The multiplicative inverse of a nonzero field element. +- `inverse(&self) -> Option` - The multiplicative inverse of a nonzero field element. Returns `None` if the element is zero. - `pow(&self, power: usize) -> Self` - Multiply a field element by itself `power` times. - `primitive_root_of_unity(n: usize) -> Self` - The primitive $n$th root of unity of the field. @@ -32,16 +34,15 @@ The only additional constraint aside from the `FiniteField` trait and adherance We will discuss `PrimeField

` momentarily. -### Constant (Compile Time) Implementations -Note that both of these traits are tagged with `#[const_trait]` which implies that each method and associated constant is implemented at compile time. -This is done purposefully as it allows for generic implementations of these fields to be constructed when you compile `ronkathon` rather than computed at runtime. -In principle, this means the code runs faster, but will compile slower, but the tradeoff is that the cryptographic system is faster and extensible. - -We will see examples of this usage next. ## Structs -The two structs that implement these traits are `PrimeField` and `GaloisField`, which, in principal, could be combined into just `GaloisField` but are separated for clarity at the moment. -These structs are both generic over the prime `P` of the field, but `GaloisField` is also generic over the degree `N` of the extension field. +The structs that implement these traits are +- `PrimeField` +- `GaloisField` + +> [!NOTE] +> In principal, `PrimeField` and `GaloisField` could be combined into just `GaloisField` but are separated for clarity at the moment. +> These structs are both generic over the prime `P` of the field, but `GaloisField` is also generic over the degree `N` of the extension field. ### `PrimeField` The `PrimeField` struct is a wrapper around a `usize` by: @@ -50,11 +51,11 @@ pub struct PrimeField { value: usize, } ``` -that implements the `FiniteField` trait and has some compile-time constructions. -For example, upon creation of an element of `PrimeField

` we utilize the `const fn is_prime()` function which will `panic!` if `P` is not prime. +that implements the `FiniteField` trait and has some compile-time constructions. +For example, upon creation of an element of `PrimeField

` we utilize the `const fn is_prime()` function which will `panic!` if `P` is not prime. **Hence, it is impossible to compile a program for which you construct `PrimeField

` where `P` is not prime.** -Furthermore, it is possible to determine the `PRIMITIVE_ELEMENT` of the field at compile time so that we may implement `FiniteField` for any prime `P` without any runtime overhead. +Furthermore, it is possible to determine the `PRIMITIVE_ELEMENT` of the field at compile time so that we may implement `FiniteField` for any prime `P` without any runtime overhead. The means to do so is done in the `field::prime::find_primitive_element` function which is a brute force search for a primitive element of the field that occurs as Rust compiles `ronkathon`. All of the relevant arithmetic operations for `PrimeField

` are implemented in `field::prime::arithmetic`. @@ -62,6 +63,7 @@ All of the relevant arithmetic operations for `PrimeField

` are implemented in ### `GaloisField` The `GaloisField` struct is a wrapper around a `PrimeField

` by: ```rust +use ronkathon::algebra::field::prime::PrimeField; pub struct GaloisField { value: [PrimeField

; N], } @@ -70,5 +72,5 @@ where the `[PrimeField

; N]` is the representation of the field element as coe We implement `ExtensionField` for specific instances of `GaloisField` as, at the moment, we do not have a general compile-time-based implementation of extension fields as we do with `PrimeField

`, though it is possible to do so. Instead, we have implemented much of the arithmetic operations for `GaloisField` in `field::extension::arithmetic`, but left some that needs to be computed by hand for the user to implement (for now). -See, for instance, `field::extension::gf_101_2` implements the `IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS` for `GaloisField<2, 101>` as well as the remaining arithmetic operations. +See, for instance, `field::extension::gf_101_2` implements the `IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS` for `GaloisField<2, 101>` as well as the remaining arithmetic operations. There is a method to compute both the `IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS` at compile time as well as the `PRIMITIVE_ELEMENT` of the field, but it is not implemented at the moment. \ No newline at end of file diff --git a/src/field/binary_towers/README.md b/src/algebra/field/binary_towers/README.md similarity index 100% rename from src/field/binary_towers/README.md rename to src/algebra/field/binary_towers/README.md diff --git a/src/field/binary_towers/extension.rs b/src/algebra/field/binary_towers/extension.rs similarity index 98% rename from src/field/binary_towers/extension.rs rename to src/algebra/field/binary_towers/extension.rs index ea588bd..fd435cf 100644 --- a/src/field/binary_towers/extension.rs +++ b/src/algebra/field/binary_towers/extension.rs @@ -10,8 +10,7 @@ use rand::{ Rng, }; -use super::BinaryField; -use crate::field::FiniteField; +use super::{BinaryField, Field, Finite, FiniteField}; /// Binary extension field GF_{2^{2^K}} using binary towers arithmetic as explained in Section 2.3 of [DP23b](https://eprint.iacr.org/2023/1784.pdf) /// represented as vector of 2^K [`BinaryField`] components in multilinear basis, @@ -36,13 +35,16 @@ where [(); 1 << K]: } } -impl FiniteField for BinaryTowers +impl Finite for BinaryTowers where [(); 1 << K]: { - const ONE: Self = Self::one(); const ORDER: usize = 1 << (1 << K); - // TODO: incorrect - const PRIMITIVE_ELEMENT: Self = Self::ONE; +} + +impl Field for BinaryTowers +where [(); 1 << K]: +{ + const ONE: Self = Self::one(); const ZERO: Self = Self::new([BinaryField::ZERO; 1 << K]); fn pow(self, power: usize) -> Self { @@ -69,6 +71,13 @@ where [(); 1 << K]: } } +impl FiniteField for BinaryTowers +where [(); 1 << K]: +{ + // TODO: incorrect + const PRIMITIVE_ELEMENT: Self = Self::ONE; +} + impl Default for BinaryTowers where [(); 1 << K]: { diff --git a/src/field/binary_towers/mod.rs b/src/algebra/field/binary_towers/mod.rs similarity index 97% rename from src/field/binary_towers/mod.rs rename to src/algebra/field/binary_towers/mod.rs index 1e9b755..53bcdad 100644 --- a/src/field/binary_towers/mod.rs +++ b/src/algebra/field/binary_towers/mod.rs @@ -4,7 +4,7 @@ use std::{ ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}, }; -use crate::field::FiniteField; +use super::*; pub mod extension; pub use extension::BinaryTowers; @@ -21,10 +21,12 @@ pub enum BinaryField { One, } -impl FiniteField for BinaryField { - const ONE: Self = BinaryField::One; +impl Finite for BinaryField { const ORDER: usize = 2; - const PRIMITIVE_ELEMENT: Self = Self::ONE; +} + +impl Field for BinaryField { + const ONE: Self = BinaryField::One; const ZERO: Self = BinaryField::Zero; fn inverse(&self) -> Option { @@ -37,6 +39,10 @@ impl FiniteField for BinaryField { fn pow(self, _: usize) -> Self { self } } +impl FiniteField for BinaryField { + const PRIMITIVE_ELEMENT: Self = Self::ONE; +} + impl From for BinaryField { fn from(value: usize) -> Self { match value { diff --git a/src/field/binary_towers/tests.rs b/src/algebra/field/binary_towers/tests.rs similarity index 98% rename from src/field/binary_towers/tests.rs rename to src/algebra/field/binary_towers/tests.rs index feebcf1..97d7d8f 100644 --- a/src/field/binary_towers/tests.rs +++ b/src/algebra/field/binary_towers/tests.rs @@ -2,7 +2,7 @@ use rand::{thread_rng, Rng}; use rstest::rstest; use super::*; -use crate::field::prime::PrimeField; +use crate::PrimeField; type TestBinaryField = PrimeField<2>; diff --git a/src/field/extension/arithmetic.rs b/src/algebra/field/extension/arithmetic.rs similarity index 100% rename from src/field/extension/arithmetic.rs rename to src/algebra/field/extension/arithmetic.rs diff --git a/src/field/extension/gf_101_2.rs b/src/algebra/field/extension/gf_101_2.rs similarity index 98% rename from src/field/extension/gf_101_2.rs rename to src/algebra/field/extension/gf_101_2.rs index 329d71c..a1ee314 100644 --- a/src/field/extension/gf_101_2.rs +++ b/src/algebra/field/extension/gf_101_2.rs @@ -8,6 +8,7 @@ //! verified by finding out embedding degree of the curve, i.e. smallest k such that r|q^k-1. use super::*; +use crate::{Distribution, Monomial, Polynomial, Rng, Standard}; impl ExtensionField<2, 101> for PlutoBaseFieldExtension { /// irreducible polynomial used to reduce field polynomials to second degree: @@ -78,20 +79,8 @@ impl PlutoBaseFieldExtension { } } -impl FiniteField for PlutoBaseFieldExtension { +impl Field for PlutoBaseFieldExtension { const ONE: Self = Self::new([PlutoBaseField::ONE, PlutoBaseField::ZERO]); - const ORDER: usize = PlutoExtensions::QuadraticBase as usize; - /// Retrieves a multiplicative generator for GF(101) inside of [`GaloisField<2, GF101>`]. - /// This can be verified using sage script - /// ```sage - /// F = GF(101) - /// Ft. = F[] - /// P = Ft(t ^ 2 + 2) - /// F_2 = GF(101 ^ 2, name="t", modulus=P) - /// f_2_primitive_element = F_2([14, 9]) - /// assert f_2_primitive_element.multiplicative_order() == 101^2-1 - /// ``` - const PRIMITIVE_ELEMENT: Self = Self::new([PlutoBaseField::new(14), PlutoBaseField::new(9)]); const ZERO: Self = Self::new([PlutoBaseField::ZERO, PlutoBaseField::ZERO]); /// Computes the multiplicative inverse of `a`, i.e. 1 / (a0 + a1 * t). @@ -124,6 +113,20 @@ impl FiniteField for PlutoBaseFieldExtension { } } +impl FiniteField for PlutoBaseFieldExtension { + /// Retrieves a multiplicative generator for GF(101) inside of [`GaloisField<2, GF101>`]. + /// This can be verified using sage script + /// ```sage + /// F = GF(101) + /// Ft. = F[] + /// P = Ft(t ^ 2 + 2) + /// F_2 = GF(101 ^ 2, name="t", modulus=P) + /// f_2_primitive_element = F_2([14, 9]) + /// assert f_2_primitive_element.multiplicative_order() == 101^2-1 + /// ``` + const PRIMITIVE_ELEMENT: Self = Self::new([PlutoBaseField::new(14), PlutoBaseField::new(9)]); +} + impl Distribution> for Standard { #[inline] fn sample(&self, rng: &mut R) -> GaloisField { @@ -178,6 +181,8 @@ impl Rem for PlutoBaseFieldExtension { #[cfg(test)] mod tests { + use rstest::rstest; + use super::*; #[test] diff --git a/src/field/extension/gf_2_8.rs b/src/algebra/field/extension/gf_2_8.rs similarity index 96% rename from src/field/extension/gf_2_8.rs rename to src/algebra/field/extension/gf_2_8.rs index 0fb2f67..a4c145b 100644 --- a/src/field/extension/gf_2_8.rs +++ b/src/algebra/field/extension/gf_2_8.rs @@ -4,10 +4,10 @@ //! (X^2-K). //! //! This extension field is used for our [AES implementation][`crate::encryption::symmetric::aes`]. -use self::field::prime::AESField; -use super::*; +use super::{prime::AESField, *}; +use crate::{Monomial, Polynomial}; -impl FiniteField for GaloisField<8, 2> { +impl Field for GaloisField<8, 2> { const ONE: Self = Self::new([ AESField::ONE, AESField::ZERO, @@ -18,17 +18,6 @@ impl FiniteField for GaloisField<8, 2> { AESField::ZERO, AESField::ZERO, ]); - const ORDER: usize = AESField::ORDER.pow(8); - const PRIMITIVE_ELEMENT: Self = Self::new([ - AESField::ONE, - AESField::ONE, - AESField::ZERO, - AESField::ZERO, - AESField::ONE, - AESField::ZERO, - AESField::ZERO, - AESField::ZERO, - ]); const ZERO: Self = Self::new([ AESField::ZERO, AESField::ZERO, @@ -63,6 +52,19 @@ impl FiniteField for GaloisField<8, 2> { } } +impl FiniteField for GaloisField<8, 2> { + const PRIMITIVE_ELEMENT: Self = Self::new([ + AESField::ONE, + AESField::ONE, + AESField::ZERO, + AESField::ZERO, + AESField::ONE, + AESField::ZERO, + AESField::ZERO, + AESField::ZERO, + ]); +} + impl ExtensionField<8, 2> for GaloisField<8, 2> { /// Represents the irreducible polynomial x^8 + x^4 + x^3 + x + 1. const IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS: [AESField; 9] = [ diff --git a/src/field/extension/mod.rs b/src/algebra/field/extension/mod.rs similarity index 96% rename from src/field/extension/mod.rs rename to src/algebra/field/extension/mod.rs index 583f1dc..f968455 100644 --- a/src/field/extension/mod.rs +++ b/src/algebra/field/extension/mod.rs @@ -9,7 +9,7 @@ use std::array; -use super::*; +use super::{prime::*, *}; mod arithmetic; pub mod gf_101_2; @@ -43,7 +43,7 @@ pub enum PlutoExtensions { /// polynomial to the original field. #[const_trait] pub trait ExtensionField: - FiniteField + Field + From> + Add, Output = Self> + AddAssign> @@ -65,6 +65,10 @@ pub struct GaloisField { pub(crate) coeffs: [PrimeField

; N], } +impl Finite for GaloisField { + const ORDER: usize = PrimeField::

::ORDER.pow(N as u32); +} + // impl FiniteField for GaloisField { // const GENERATOR: Self = panic!(); // const NEG_ONE: Self = Self::new(single_instance_array(PrimeField::

::NEG_ONE)); @@ -184,3 +188,7 @@ impl From for GaloisField { impl From for GaloisField { fn from(val: usize) -> Self { Self::from(PrimeField::

::from(val)) } } + +impl From> for usize { + fn from(value: GaloisField) -> Self { value.coeffs[0].value } +} diff --git a/src/field/mod.rs b/src/algebra/field/mod.rs similarity index 81% rename from src/field/mod.rs rename to src/algebra/field/mod.rs index d9034ae..71303f5 100644 --- a/src/field/mod.rs +++ b/src/algebra/field/mod.rs @@ -1,15 +1,21 @@ -//! This module contains the definition of finite fields and their extension fields. -use super::*; +//! This module contains the definition of groups, finite fields, and their extension fields. +#![doc = include_str!("./README.md")] pub mod binary_towers; pub mod extension; pub mod prime; +use std::{ + hash::Hash, + iter::{Product, Sum}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}, +}; + +use super::Finite; /// A field is a set of elements on which addition, subtraction, multiplication, and division are /// defined. -/// -/// We restrict to finite fields, which are fields with a finite number of elements. + #[const_trait] -pub trait FiniteField: +pub trait Field: std::fmt::Debug + From + Default @@ -32,20 +38,22 @@ pub trait FiniteField: + Rem + Hash + 'static { - /// The order of the field, i.e., the number of elements in the field. - const ORDER: usize; /// The additive identity element. const ZERO: Self; /// The multiplicative identity element. const ONE: Self; - /// Returns a multiplicative generator of the field. - const PRIMITIVE_ELEMENT: Self; /// Gets the multiplicative inverse of the field element (if it exists). fn inverse(&self) -> Option; /// Computes the power of the field element. fn pow(self, power: usize) -> Self; +} + +/// fields with a finite number of elements. +pub trait FiniteField: Finite + Field { + /// Returns a multiplicative generator of the field. + const PRIMITIVE_ELEMENT: Self; /// Returns the primitive n-th root of unity in the field. /// diff --git a/src/field/prime/arithmetic.rs b/src/algebra/field/prime/arithmetic.rs similarity index 99% rename from src/field/prime/arithmetic.rs rename to src/algebra/field/prime/arithmetic.rs index ddce55b..f2f32ef 100644 --- a/src/field/prime/arithmetic.rs +++ b/src/algebra/field/prime/arithmetic.rs @@ -72,6 +72,8 @@ impl Rem for PrimeField

{ #[cfg(test)] mod tests { + use rstest::rstest; + use super::*; #[rstest] diff --git a/src/field/prime/mod.rs b/src/algebra/field/prime/mod.rs similarity index 95% rename from src/field/prime/mod.rs rename to src/algebra/field/prime/mod.rs index 2c3ad78..8794f80 100644 --- a/src/field/prime/mod.rs +++ b/src/algebra/field/prime/mod.rs @@ -3,9 +3,12 @@ //! [`FiniteField`] trait is implemented. This module asserts at compile time that the order of the //! field is a prime number and allows for creation of generic prime order fields. -use std::str::FromStr; +use std::{fmt, str::FromStr}; + +use rand::{distributions::Standard, prelude::Distribution, Rng}; use super::*; +use crate::algebra::Finite; mod arithmetic; @@ -132,10 +135,12 @@ impl PrimeField

{ } } -impl const FiniteField for PrimeField

{ - const ONE: Self = Self { value: 1 }; +impl const Finite for PrimeField

{ const ORDER: usize = P; - const PRIMITIVE_ELEMENT: Self = if P == 2 { Self::ONE } else { find_primitive_element::

() }; +} + +impl const Field for PrimeField

{ + const ONE: Self = Self { value: 1 }; const ZERO: Self = Self { value: 0 }; fn inverse(&self) -> Option { @@ -163,6 +168,11 @@ impl const FiniteField for PrimeField

{ } } +impl FiniteField for PrimeField

{ + const PRIMITIVE_ELEMENT: Self = + if P == 2 { Self::ONE } else { Self::new(find_primitive_element::

()) }; +} + const fn is_prime(n: usize) { let mut i = 2; while i * i <= n { @@ -181,14 +191,14 @@ const fn is_prime(n: usize) { /// the non-zero elements of the field, so we check here that `g` raised to the power of `(P-1)/g` /// where this division is integer division, is not equal to 1. In other words, we are checking that /// `g` is coprime to `P-1`. This follows from [Lagrange's theorem](https://en.wikipedia.org/wiki/Lagrange%27s_theorem_(group_theory)). -const fn find_primitive_element() -> PrimeField

{ +pub const fn find_primitive_element() -> usize { let mut i = 2; while i * i <= P { if (P - 1) % i == 0 { if PrimeField::

::new(i).pow((P - 1) / i).value != PrimeField::

::ONE.value { - return PrimeField::

::new(i); + return i; } else if PrimeField::

::new(P + 1 - i).pow(i).value != PrimeField::

::ONE.value { - return PrimeField::

::new(P + 1 - i); + return P + 1 - i; } } i += 1; @@ -234,6 +244,10 @@ impl From for usize { } } +impl From> for usize { + fn from(value: PrimeField

) -> Self { value.value } +} + impl From for PrimeField

{ fn from(value: i32) -> Self { let abs = Self::new(value.unsigned_abs() as usize); @@ -257,6 +271,8 @@ impl FromStr for PrimeField

{ #[cfg(test)] mod tests { + use rstest::rstest; + use super::*; #[rstest] diff --git a/src/algebra/group/README.md b/src/algebra/group/README.md new file mode 100644 index 0000000..d1a7db6 --- /dev/null +++ b/src/algebra/group/README.md @@ -0,0 +1,30 @@ +# Group + +## Traits +- `Group`: a generic group $(G, \cdot)$ where $G$ is a set and $\cdot$ is a binary operation. +- `FiniteGroup`: a group with finite elements defined using `Finite` trait +- `AbelianGroup`: group with commutative operation +- `FiniteCyclicGroup`: a finite group with a generator. + +### `Group` +[`Group`](./group.rs) represents a group with finite elements. It defines a binary operation on the set of elements of the group. +- `IDENTITY`: The identity element of the group. +- `inverse(&self) -> Self`: inverse of the element. +- `operation(a: &Self, b: &Self) -> Self`: the operation of the group. +- `scalar_mul(&self, scalar: &Self::Scalar)`: multiplication of the element by a scalar. + +## Structs +The structs that implement these traits are +- `MultiplicativePrimeGroup` + +### `MultiplicativePrimeGroup` +The `MultiplicativePrimeGroup` struct is a wrapper around a `usize` that defines $(Z/nZ)^{*}$ for a prime power $n=p^k$ with binary operation as $\times$: +```rust +pub struct MultiplicativePrimeGroup(usize); +``` + +It uses compile time assertions to check that $P$ is prime. + +## Examples + +[PermutationGroup](/examples/permutation_group.rs) example showcases how `Group` trait is implemented for any struct. \ No newline at end of file diff --git a/src/algebra/group/mod.rs b/src/algebra/group/mod.rs new file mode 100644 index 0000000..9b9cc37 --- /dev/null +++ b/src/algebra/group/mod.rs @@ -0,0 +1,71 @@ +#![doc = include_str!("./README.md")] +pub mod prime; + +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use super::Finite; + +#[const_trait] +/// Group trait defined by a binary operation, identity element and inverse. +pub trait Group: + std::fmt::Debug + + Sized + + Add + + AddAssign + + Sub + + SubAssign + + Neg + + Mul + + MulAssign + + Clone + + Copy + + Default + + Eq { + /// Scalar defined in the group + type Scalar; + /// Identity element of group + const IDENTITY: Self; + + /// operation defined for the group, can be `+` for additive group and `·` for multiplicative + /// group + fn op(&self, rhs: &Self) -> Self; + + /// Inverse of group element: a·i = [`FiniteGroup::IDENTITY`] + fn inverse(&self) -> Option; + + /// Multiplication with the scalar element of the group, i.e. repeatedly applying group + /// [`FiniteGroup::operation`] `scalar` number of times. + fn scalar_mul(&self, scalar: Self::Scalar) -> Self; +} + +/// Group trait with finite number of elements +pub trait FiniteGroup: Finite + Group { + /// order of group element, i.e. order of finite cyclic subgroup that the element belongs to + fn order(&self) -> usize { + let mut order = 1; + let mut elem = *self; + for _ in 0..Self::ORDER { + // check if elem is the identity + if elem == Self::IDENTITY { + return order; + } + // apply operation and increment order + elem = Self::op(&elem, self); + order += 1; + } + order + } +} + +/// Defines a group with commutative operation: `a·b=b·a` +pub trait AbelianGroup: Group { + /// Returns whether the group is an abelian group + fn is_abelian(a: &Self, b: &Self) { assert!(Self::op(a, b) == Self::op(b, a)) } +} + +#[const_trait] +/// Finite cyclic group trait defined by a generator element and order of the group +pub trait FiniteCyclicGroup: FiniteGroup + AbelianGroup { + /// primtive element of group + const GENERATOR: Self; +} diff --git a/src/algebra/group/prime.rs b/src/algebra/group/prime.rs new file mode 100644 index 0000000..b762050 --- /dev/null +++ b/src/algebra/group/prime.rs @@ -0,0 +1,118 @@ +//! Defines a multiplicative group under multiplication modulo prime `r`. +use super::*; +use crate::{ + algebra::field::prime::find_primitive_element, + encryption::asymmetric::rsa::{gcd, is_prime}, +}; + +/// [`FiniteGroup`] under multiplication implemented as integer, $(Z/nZ)*$ modulo any prime power +/// number `n=p^k`. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct MultiplicativePrimeGroup(usize); +impl MultiplicativePrimeGroup { + #[allow(dead_code)] + const IS_PRIME: () = assert!(is_prime(P)); + + /// create new value in group `Z/nZ` + pub fn new(value: usize) -> Self { Self(value % (P ^ K)) } +} + +impl Finite for MultiplicativePrimeGroup { + /// P^K - P^{K-1} + const ORDER: usize = (P ^ K) - (P ^ (K - 1)); +} + +impl Group for MultiplicativePrimeGroup { + type Scalar = usize; + + const IDENTITY: Self = Self(1); + + fn op(&self, rhs: &Self) -> Self { Self(self.0 * rhs.0 % (P ^ K)) } + + fn inverse(&self) -> Option { + if gcd(self.0 as u64, P as u64) != 1 { + return None; + } + Some(self.scalar_mul(Self::ORDER - 1)) + } + + fn scalar_mul(&self, b: Self::Scalar) -> Self { + let mut res = Self(1); + for _ in 0..b { + res = Self::op(&res, self); + } + res + } +} + +impl FiniteGroup for MultiplicativePrimeGroup { + fn order(&self) -> usize { Self::ORDER } +} + +impl AbelianGroup for MultiplicativePrimeGroup {} + +impl FiniteCyclicGroup for MultiplicativePrimeGroup { + const GENERATOR: Self = Self(find_primitive_element::

()); +} + +impl Add for MultiplicativePrimeGroup { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { Self::op(&self, &rhs) } +} + +impl AddAssign for MultiplicativePrimeGroup { + fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } +} + +impl Neg for MultiplicativePrimeGroup { + type Output = Self; + + fn neg(self) -> Self::Output { Self::inverse(&self).expect("inverse does not exist") } +} + +impl Sub for MultiplicativePrimeGroup { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { self + -rhs } +} + +impl SubAssign for MultiplicativePrimeGroup { + fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } +} + +impl Mul for MultiplicativePrimeGroup { + type Output = Self; + + fn mul(self, rhs: usize) -> Self::Output { Self::scalar_mul(&self, rhs) } +} + +impl MulAssign for MultiplicativePrimeGroup { + fn mul_assign(&mut self, rhs: usize) { *self = *self * rhs; } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn mul_group_properties() { + type MulGroup = MultiplicativePrimeGroup<5, 2>; + + let gen = MultiplicativePrimeGroup::<5, 2>::GENERATOR; + + let ident = MulGroup::IDENTITY; + + println!("{:?}, {:?}, {}", gen, gen.inverse().unwrap(), MulGroup::ORDER); + // commutativity + assert_eq!(gen + ident, ident + gen); + // inverse + assert_eq!(gen + gen.inverse().unwrap(), ident); + // associativity + assert_eq!(gen + (ident + gen), (gen + gen) + ident); + // scalar multiplication + assert_eq!(gen * 2, gen + gen); + // order + assert_eq!(gen.order(), MulGroup::ORDER); + } +} diff --git a/src/algebra/mod.rs b/src/algebra/mod.rs new file mode 100644 index 0000000..b735380 --- /dev/null +++ b/src/algebra/mod.rs @@ -0,0 +1,13 @@ +#![doc = include_str!("./README.md")] +//! Defines algebraic types: +//! - [`group::Group`]: Group +//! - [`field::Field`]: Field +pub mod field; +pub mod group; + +#[const_trait] +/// Trait defining order of algebraic structure +pub trait Finite { + /// total number of elements + const ORDER: usize; +} diff --git a/src/codes/reed_solomon.rs b/src/codes/reed_solomon.rs index 60be4f8..35774f1 100644 --- a/src/codes/reed_solomon.rs +++ b/src/codes/reed_solomon.rs @@ -5,6 +5,7 @@ use std::array; use itertools::Itertools; use super::*; +use crate::algebra::field::FiniteField; // TODO: We should allow for arbitrary data in the message so long as it can be // converted into an element of a prime field and decoded the same way. diff --git a/src/compiler/errors.rs b/src/compiler/errors.rs index e2db422..c398231 100644 --- a/src/compiler/errors.rs +++ b/src/compiler/errors.rs @@ -1,6 +1,6 @@ use std::{error::Error, fmt::Display}; -use crate::field::prime::PlutoScalarField; +use crate::PlutoScalarField; /// Errors from parsing the DSL #[derive(Debug)] diff --git a/src/compiler/parser.rs b/src/compiler/parser.rs index 780bac6..35f59f7 100644 --- a/src/compiler/parser.rs +++ b/src/compiler/parser.rs @@ -31,7 +31,7 @@ use super::{ errors::ParserError, utils::{get_product_key, is_valid_var_name}, }; -use crate::field::{prime::PlutoScalarField, FiniteField}; +use crate::{Field, PlutoScalarField}; /// Fan-in 2 Gate representing a constraint in the computation. /// Each constraint satisfies PLONK's arithmetic equation: `a(X)QL(X) + b(X)QR(X) + a(X)b(X)QM(X) + diff --git a/src/compiler/program.rs b/src/compiler/program.rs index 6a421eb..23b193e 100644 --- a/src/compiler/program.rs +++ b/src/compiler/program.rs @@ -14,13 +14,12 @@ use super::{ utils::get_product_key, }; use crate::{ + algebra::field::FiniteField, compiler::parser::{parse_constraints, WireCoeffs}, - field::{prime::PlutoScalarField, FiniteField}, polynomial::{Lagrange, Polynomial}, + Field, PlutoScalarField, }; -// type Poly = - /// Column represents all three columns in the execution trace which a variable /// can take. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] diff --git a/src/curve/README.md b/src/curve/README.md index d597b8b..4b5f6ac 100644 --- a/src/curve/README.md +++ b/src/curve/README.md @@ -5,7 +5,7 @@ When working over a finite field so that $x, y a, b \in \mathbb{F}_q$, we put $E It turns out that the set of points $E(\mathbb{F}_q)$ forms a group under a certain operation called point addition. This group, in at least certain cases, is discrete logarithm hard, which is the basis for much of modern day cryptography. -## Pluto Curve +## Curve Group and Pluto Curve For the sake of `ronkathon`, we use a specific curve which we affectionately call the "Pluto Curve." Our equation is: $$y^2 = x^3 + 3$$ @@ -14,6 +14,8 @@ Predominantly, we use the extension $F_{p^2}$ since we need this for the [Tate p We refer to $F_{101}$ as the `PlutoBaseField` and $F_{101^2}$ as the `PlutoBaseFieldExtension` within `ronkathon`. From which, we also use the terminology of `PlutoCurve` to refer to $E(F_{101})$ and `PlutoExtendedCurve` to refer to $E(F_{101^2})$. +We also define a `CurveGroup`, an extension of [`FiniteGroup`](../field/group.rs) trait representing the group law of the curve. + ### Type B curve and type 1 pairing Investigating our curve and choice of field, we find that the curve is Type B since: @@ -63,11 +65,11 @@ Here are a few related definitions that might be helpful to understand the curve ### roots of unity -the $rth$ root of unity in $F_p$ is some number $h$ such that $h^r \equiv 1$, For example in our field scaler field $F_{101}$ the $4th$ roots of unity are $\{1,10,91,100\}$. +the $rth$ root of unity in $F_p$ is some number $h$ such that $h^r \equiv 1$, For example in our field scaler field $F_{101}$ the $4th$ roots of unity are $\{1,10,91,100\}$. ### $r$-torsion - $r$-torsion points are points $P \in E(K) | rP = O$ for some point $P$ so that $P$ has order $r$ or is a factor of $r$. The set of r-torsion points in $E(K)$ is denoted $E(K)[r]$. If $\bar{K}$ is the [algebraic closure](https://en.wikipedia.org/wiki/Algebraic_closure) of $K$ then the number of r-torsion points in $E(K)$ is the number of points in $E(\bar{K})[r] = r^2$. + $r$-torsion points are points $P \in E(K) | rP = O$ for some point $P$ so that $P$ has order $r$ or is a factor of $r$. The set of r-torsion points in $E(K)$ is denoted $E(K)[r]$. If $\bar{K}$ is the [algebraic closure](https://en.wikipedia.org/wiki/Algebraic_closure) of $K$ then the number of r-torsion points in $E(K)$ is the number of points in $E(\bar{K})[r] = r^2$. - *Security note: If* $r$ and $q$ are not co-prime then the discrete log is solvable in linear time with something called an anomaly attack. diff --git a/src/curve/mod.rs b/src/curve/mod.rs index 164ee19..d98f529 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -1,7 +1,18 @@ //! Elliptic curve operations and types. -use self::field::prime::PlutoScalarField; +use std::fmt::Debug; + +use algebra::{ + field::FiniteField, + group::{AbelianGroup, FiniteGroup}, + Finite, +}; + use super::*; +use crate::{ + algebra::group::{FiniteCyclicGroup, Group}, + Field, PlutoScalarField, +}; pub mod pairing; pub mod pluto_curve; @@ -9,12 +20,16 @@ pub mod pluto_curve; /// Elliptic curve parameters for a curve over a finite field in Weierstrass form /// `y^2 = x^3 + ax + b` -pub trait EllipticCurve: Copy { +pub trait EllipticCurve: Copy + Debug + Eq { /// The field for the curve coefficients. - type Coefficient: FiniteField + Into; + type Coefficient: Field + Into; + + /// curve base field element type + /// TODO: need to be converted for big integers later + type BaseField: FiniteField + Into; - /// Integer field element type - type BaseField: FiniteField; + /// Curve scalar field type + type ScalarField: FiniteField + Into; /// Order of this elliptic curve, i.e. number of elements in the scalar field. const ORDER: usize; @@ -29,6 +44,20 @@ pub trait EllipticCurve: Copy { const GENERATOR: (Self::BaseField, Self::BaseField); } +/// Curve group representing curve element +pub trait CurveGroup: FiniteCyclicGroup { + /// Curve group's base field + type BaseField: Field + Into; + + /// Point doubling + fn double(self) -> Self; + /// Checks whether a point is on curve + fn is_on_curve(&self) -> bool; + + /// Returns affine point `(x, y)`, returns `(_, _, true)` if point is `infinity` + fn xy(&self) -> (Self::BaseField, Self::BaseField, bool); +} + // TODO: A potential issue here is that you can have a point that is not on the curve created via // this enum. This is a potential issue that should be addressed. /// An Affine Coordinate Point on a Weierstrass elliptic curve @@ -44,17 +73,107 @@ pub enum AffinePoint { impl AffinePoint { /// Create a new point on the curve so long as it satisfies the curve equation. pub fn new(x: C::BaseField, y: C::BaseField) -> Self { - assert_eq!( - y * y, - x * x * x + C::EQUATION_A.into() * x + C::EQUATION_B.into(), - "Point is not on curve" - ); - Self::Point(x, y) + let point = Self::Point(x, y); + assert!(point.is_on_curve(), "Point is not on curve"); + point + } +} + +impl Finite for AffinePoint { + const ORDER: usize = C::ORDER; +} + +impl Group for AffinePoint { + type Scalar = C::ScalarField; + + const IDENTITY: Self = AffinePoint::Infinity; + + fn op(&self, b: &Self) -> Self { self.add(*b) } + + fn inverse(&self) -> Option { + let (x, y) = match self { + AffinePoint::Infinity => return Some(*self), + AffinePoint::Point(x, y) => (*x, *y), + }; + Some(AffinePoint::Point(-x, y)) } + + fn scalar_mul(&self, b: Self::Scalar) -> Self { *self * b } +} + +impl FiniteGroup for AffinePoint {} +impl AbelianGroup for AffinePoint {} + +impl CurveGroup for AffinePoint { + type BaseField = C::BaseField; + + // NOTE: Apparently there is a faster way to do this with twisted curve methods + fn double(self) -> Self { + let (x, y) = match self { + AffinePoint::Point(x, y) => (x, y), + AffinePoint::Infinity => return AffinePoint::Infinity, + }; + // m = (3x^2) / (2y) + let m = (((C::BaseField::ONE + C::BaseField::ONE) + C::BaseField::ONE) * x * x + + C::EQUATION_A.into()) + / ((C::BaseField::ONE + C::BaseField::ONE) * y); + + // 2P = (m^2 - 2x, m(3x - m^2)- y) + let x_new = m * m - (C::BaseField::ONE + C::BaseField::ONE) * x; + let y_new = m * ((C::BaseField::ONE + C::BaseField::ONE + C::BaseField::ONE) * x - m * m) - y; + AffinePoint::new(x_new, y_new) + } + + fn is_on_curve(&self) -> bool { + match self { + AffinePoint::Infinity => true, + AffinePoint::Point(x, y) => { + let a: C::BaseField = C::EQUATION_A.into(); + let b: C::BaseField = C::EQUATION_B.into(); + *y * *y == *x * *x * *x + a * *x + b + }, + } + } + + fn xy(&self) -> (Self::BaseField, Self::BaseField, bool) { + match self { + AffinePoint::Infinity => (Self::BaseField::ZERO, Self::BaseField::ZERO, true), + AffinePoint::Point(x, y) => (*x, *y, false), + } + } +} + +impl FiniteCyclicGroup for AffinePoint { + const GENERATOR: Self = AffinePoint::Point(C::GENERATOR.0, C::GENERATOR.1); +} + +impl Default for AffinePoint { + fn default() -> Self { ::GENERATOR } +} + +impl Mul for AffinePoint { + type Output = Self; + + /// This is the naive implementation of scalar multiplication + /// There is a faster way to do this but this is simpler to reason about for now + fn mul(self, rhs: C::ScalarField) -> Self::Output { + if rhs == C::ScalarField::ZERO { + return AffinePoint::Infinity; + } + let mut val = self; + for _ in 1..rhs.into() { + val += self; + } + val + } +} + +impl MulAssign for AffinePoint { + fn mul_assign(&mut self, rhs: C::ScalarField) { *self = *self * rhs } } impl Add for AffinePoint { - type Output = AffinePoint; + type Output = Self; fn add(self, rhs: Self) -> Self::Output { // infty checks @@ -112,23 +231,7 @@ impl Neg for AffinePoint { } } -/// This is the niave implementation of scalar multiplication -/// There is a faster way to do this but this is simpler to reason about for now #[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for AffinePoint { - type Output = AffinePoint; - - fn mul(mut self, scalar: u32) -> Self::Output { - if scalar == 0 { - return AffinePoint::Infinity; - } - let val = self; - for _ in 1..scalar { - self += val; - } - self - } -} impl Sub for AffinePoint { type Output = AffinePoint; @@ -136,10 +239,8 @@ impl Sub for AffinePoint { fn sub(self, rhs: Self) -> Self::Output { self + -rhs } } -impl Mul for AffinePoint { - type Output = AffinePoint; - - fn mul(self, scalar: PlutoScalarField) -> Self::Output { scalar.value as u32 * self } +impl SubAssign for AffinePoint { + fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } } #[allow(clippy::suspicious_arithmetic_impl)] @@ -157,29 +258,3 @@ impl std::ops::Mul> for u32 { out } } - -// NOTE: Apparently there is a faster way to do this with twisted curve methods -impl AffinePoint { - /// Compute the point doubling operation on this point. - pub fn point_doubling(self) -> AffinePoint { - let (x, y) = match self { - AffinePoint::Point(x, y) => (x, y), - AffinePoint::Infinity => return AffinePoint::Infinity, - }; - // m = (3x^2) / (2y) - let m = (((C::BaseField::ONE + C::BaseField::ONE) + C::BaseField::ONE) * x * x - + C::EQUATION_A.into()) - / ((C::BaseField::ONE + C::BaseField::ONE) * y); - - // 2P = (m^2 - 2x, m(3x - m^2)- y) - let x_new = m * m - (C::BaseField::ONE + C::BaseField::ONE) * x; - let y_new = m * ((C::BaseField::ONE + C::BaseField::ONE + C::BaseField::ONE) * x - m * m) - y; - AffinePoint::new(x_new, y_new) - } - - /// Get the generator point of this curve. - pub fn generator() -> Self { - let (x, y) = C::GENERATOR; - AffinePoint::new(x, y) - } -} diff --git a/src/curve/pairing.rs b/src/curve/pairing.rs index 60712ed..3257398 100644 --- a/src/curve/pairing.rs +++ b/src/curve/pairing.rs @@ -1,5 +1,7 @@ //! Pairing operations for the Pluto curve. +use std::fmt::Debug; + use super::*; /// Compute the simplified Tate pairing of two points on the curve. @@ -28,7 +30,7 @@ use super::*; /// Refer to Ben Lynn's [Thesis](https://crypto.stanford.edu/pbc/thesis.pdf#page=99.18) Section 6.2. /// Note that this is only possible for a supersingular curve and [`curve::PlutoBaseCurve`] /// satisfies this property. -pub fn pairing( +pub fn pairing( p: AffinePoint, q: AffinePoint, ) -> C::BaseField { @@ -53,7 +55,7 @@ pub fn pairing( /// Evaluate a rational function on a divisor f_{r,P}(D_{Q}) in logarithmic time complexity using an /// algorithm similar to double and add. -pub(crate) fn miller_loop( +pub(crate) fn miller_loop( p: AffinePoint, q: AffinePoint, ) -> C::BaseField { @@ -265,10 +267,10 @@ mod tests { #[test] fn torsion_generators() { - let generator = AffinePoint::::generator(); + let generator = AffinePoint::::GENERATOR; println!("Generator: {:?}", generator); for i in 1..PlutoPrime::Scalar as usize + 1 { - let point = generator * i as u32; + let point = generator * PlutoScalarField::new(i); println!("{:?} * P = {:?}", i, point); if i == PlutoPrime::Scalar as usize { assert_eq!(point, AffinePoint::Infinity); @@ -281,7 +283,7 @@ mod tests { let cube_root_of_unity = PlutoBaseFieldExtension::primitive_root_of_unity(3); let torsion_generator = if let AffinePoint::::Point(x, y) = - AffinePoint::::generator() + AffinePoint::::GENERATOR { AffinePoint::::new( cube_root_of_unity * PlutoBaseFieldExtension::from(x), @@ -291,7 +293,7 @@ mod tests { panic!("Generator is not a point"); }; for i in 1..PlutoPrime::Scalar as usize + 1 { - let point = torsion_generator * i as u32; + let point = torsion_generator * PlutoScalarField::new(i); println!("{:?} * P = {:?}", i, point); if i == PlutoPrime::Scalar as usize { assert_eq!(point, AffinePoint::Infinity); @@ -305,10 +307,10 @@ mod tests { #[test] fn pairing_test() { - let p = AffinePoint::::from(AffinePoint::::generator()); + let p = AffinePoint::::from(AffinePoint::::GENERATOR); let cube_root_of_unity = PlutoBaseFieldExtension::primitive_root_of_unity(3); let q = if let AffinePoint::::Point(x, y) = - AffinePoint::::generator() + AffinePoint::::GENERATOR { AffinePoint::::new( cube_root_of_unity * PlutoBaseFieldExtension::from(x), @@ -358,10 +360,10 @@ mod tests { #[test] fn weil_from_tate_paring() { - let p = AffinePoint::::from(AffinePoint::::generator()); + let p = AffinePoint::::from(AffinePoint::::GENERATOR); let cube_root_of_unity = PlutoBaseFieldExtension::primitive_root_of_unity(3); let q = if let AffinePoint::::Point(x, y) = - AffinePoint::::generator() + AffinePoint::::GENERATOR { AffinePoint::::new( cube_root_of_unity * PlutoBaseFieldExtension::from(x), @@ -391,10 +393,10 @@ mod tests { let a = PlutoScalarField::new(3); let b = PlutoScalarField::new(5); - let p = AffinePoint::::from(AffinePoint::::generator()); + let p = AffinePoint::::from(AffinePoint::::GENERATOR); let cube_root_of_unity = PlutoBaseFieldExtension::primitive_root_of_unity(3); let q = if let AffinePoint::::Point(x, y) = - AffinePoint::::generator() + AffinePoint::::GENERATOR { AffinePoint::::new( cube_root_of_unity * PlutoBaseFieldExtension::from(x), @@ -417,7 +419,7 @@ mod tests { assert_eq!(lhs, rhs); let r = if let AffinePoint::::Point(x, y) = - AffinePoint::::generator().point_doubling() + AffinePoint::::GENERATOR.double() { AffinePoint::::new( cube_root_of_unity * PlutoBaseFieldExtension::from(x), diff --git a/src/curve/pluto_curve.rs b/src/curve/pluto_curve.rs index 9dcf5c1..ee50f85 100644 --- a/src/curve/pluto_curve.rs +++ b/src/curve/pluto_curve.rs @@ -8,8 +8,8 @@ //! Note that this would be cleaner if we could use trait specialization to keep the default //! implementations in the trait itself, but this feature is not yet to that point of utility. -use self::field::extension::PlutoExtensions; use super::*; +use crate::algebra::field::extension::PlutoExtensions; /// The [`PlutoBaseCurve`] is an the base field set to the [`PlutoBaseField`]. This is the curve /// used in the Pluto `ronkathon` system. The curve is defined by the equation `y^2 = x^3 + 3`. @@ -27,6 +27,7 @@ pub struct PlutoExtendedCurve; impl EllipticCurve for PlutoBaseCurve { type BaseField = PlutoBaseField; type Coefficient = PlutoBaseField; + type ScalarField = PlutoScalarField; const EQUATION_A: Self::Coefficient = PlutoBaseField::ZERO; const EQUATION_B: Self::Coefficient = PlutoBaseField::new(3); @@ -38,6 +39,7 @@ impl EllipticCurve for PlutoBaseCurve { impl EllipticCurve for PlutoExtendedCurve { type BaseField = PlutoBaseFieldExtension; type Coefficient = PlutoBaseField; + type ScalarField = PlutoScalarField; const EQUATION_A: Self::Coefficient = PlutoBaseField::ZERO; const EQUATION_B: Self::Coefficient = PlutoBaseField::new(3); @@ -86,9 +88,9 @@ mod pluto_base_curve_tests { #[test] fn point_doubling() { - let g = AffinePoint::::generator(); + let g = AffinePoint::::GENERATOR; - let two_g = g.point_doubling(); + let two_g = g.double(); let expected_2g = AffinePoint::::new(PlutoBaseField::new(68), PlutoBaseField::new(74)); let expected_negative_2g = @@ -96,7 +98,7 @@ mod pluto_base_curve_tests { assert_eq!(two_g, expected_2g); assert_eq!(-two_g, expected_negative_2g); - let four_g = two_g.point_doubling(); + let four_g = two_g.double(); let expected_4g = AffinePoint::::new(PlutoBaseField::new(65), PlutoBaseField::new(98)); let expected_negative_4g = @@ -104,7 +106,7 @@ mod pluto_base_curve_tests { assert_eq!(four_g, expected_4g); assert_eq!(-four_g, expected_negative_4g); - let eight_g = four_g.point_doubling(); + let eight_g = four_g.double(); let expected_8g = AffinePoint::::new(PlutoBaseField::new(18), PlutoBaseField::new(49)); let expected_negative_8g = @@ -112,7 +114,7 @@ mod pluto_base_curve_tests { assert_eq!(eight_g, expected_8g); assert_eq!(-eight_g, expected_negative_8g); - let sixteen_g = eight_g.point_doubling(); + let sixteen_g = eight_g.double(); let expected_16g = AffinePoint::::new(PlutoBaseField::new(1), PlutoBaseField::new(99)); let expected_negative_16g = @@ -124,11 +126,11 @@ mod pluto_base_curve_tests { #[test] fn order_17() { - let g = AffinePoint::::generator(); - let mut g_double = g.point_doubling(); + let g: AffinePoint = AffinePoint::::GENERATOR; + let mut g_double = g.double(); let mut count = 2; while g_double != g && -g_double != g { - g_double = g_double.point_doubling(); + g_double = g_double.double(); count *= 2; } assert_eq!(count + 1, 17); @@ -136,8 +138,8 @@ mod pluto_base_curve_tests { #[test] fn point_addition() { - let g = AffinePoint::::generator(); - let two_g = g.point_doubling(); + let g = AffinePoint::::GENERATOR; + let two_g = g.double(); let three_g = g + two_g; let expected_3g = AffinePoint::::new(PlutoBaseField::new(26), PlutoBaseField::new(45)); @@ -169,18 +171,18 @@ mod pluto_base_curve_tests { #[test] fn scalar_multiplication_rhs() { - let g = AffinePoint::::generator(); - let two_g = g * 2; - let expected_2g = g.point_doubling(); + let g = AffinePoint::::GENERATOR; + let two_g = g * PlutoScalarField::new(2); + let expected_2g: AffinePoint = g.double(); assert_eq!(two_g, expected_2g); assert_eq!(-two_g, -expected_2g); } #[test] fn scalar_multiplication_lhs() { - let g = AffinePoint::::generator(); + let g = AffinePoint::::GENERATOR; let two_g = 2 * g; - let expected_2g = g.point_doubling(); + let expected_2g = g.double(); assert_eq!(two_g, expected_2g); assert_eq!(-two_g, -expected_2g); } @@ -212,7 +214,7 @@ mod pluto_extended_curve_tests { } #[rstest] - #[case(AffinePoint::::generator())] + #[case(AffinePoint::::GENERATOR)] #[case(generator())] #[case(point())] #[should_panic] @@ -221,8 +223,8 @@ mod pluto_extended_curve_tests { #[test] fn point_doubling() { - let g = AffinePoint::::generator(); - let two_g = g.point_doubling(); + let g = AffinePoint::::GENERATOR; + let two_g = g.double(); let expected_g = generator(); let expected_two_g = point(); @@ -233,18 +235,18 @@ mod pluto_extended_curve_tests { #[test] fn scalar_multiplication_rhs() { - let g = AffinePoint::::generator(); - let two_g = g * 2; - let expected_two_g = g.point_doubling(); + let g = AffinePoint::::GENERATOR; + let two_g = g * PlutoScalarField::new(2); + let expected_two_g = g.double(); assert_eq!(two_g, expected_two_g); assert_eq!(-two_g, -expected_two_g); } #[test] fn scalar_multiplication_lhs() { - let g = AffinePoint::::generator(); + let g = AffinePoint::::GENERATOR; let two_g = 2 * g; - let expected_two_g = g.point_doubling(); + let expected_two_g: AffinePoint = g.double(); assert_eq!(two_g, expected_two_g); assert_eq!(-two_g, -expected_two_g); } diff --git a/src/curve/tests/fields.rs b/src/curve/tests/fields.rs index 96af4ff..b48c6d4 100644 --- a/src/curve/tests/fields.rs +++ b/src/curve/tests/fields.rs @@ -1,13 +1,13 @@ -use super::*; +use core::ops::{Div, DivAssign, Mul, MulAssign, Rem}; +use std::iter::Product; +use super::*; +use crate::algebra::field::extension::ExtensionField; pub type TestField = PrimeField<59>; pub type TestExtension = GaloisField<2, 59>; -impl FiniteField for TestExtension { +impl Field for TestExtension { const ONE: Self = Self::new([TestField::ONE, TestField::ZERO]); - const ORDER: usize = TestField::ORDER * TestField::ORDER; - // TODO: This is not correct for this field!!! Fix! - const PRIMITIVE_ELEMENT: Self = Self::new([TestField::new(14), TestField::new(9)]); const ZERO: Self = Self::new([TestField::ZERO, TestField::ZERO]); /// Computes the multiplicative inverse of `a`, i.e. 1 / (a0 + a1 * t). @@ -44,6 +44,11 @@ impl ExtensionField<2, 59> for GaloisField<2, 59> { [PrimeField::<59>::ONE, PrimeField::<59>::ZERO, PrimeField::<59>::ONE]; } +impl FiniteField for TestExtension { + // TODO: This is not correct for this field!!! Fix! + const PRIMITIVE_ELEMENT: Self = Self::new([TestField::new(14), TestField::new(9)]); +} + /// Returns the multiplication of two [`Ext<2, GF101>`] elements by reducing result modulo /// irreducible polynomial. impl Mul for TestExtension { diff --git a/src/curve/tests/mod.rs b/src/curve/tests/mod.rs index 9223076..db3b445 100644 --- a/src/curve/tests/mod.rs +++ b/src/curve/tests/mod.rs @@ -1,8 +1,7 @@ use std::array; -use self::{field::extension::ExtensionField, pairing::miller_loop}; use super::*; -use crate::curve::pairing::{line_function, pairing, tangent_line, vertical_line}; +use crate::curve::pairing::{line_function, miller_loop, pairing, tangent_line, vertical_line}; mod fields; use fields::*; @@ -25,6 +24,8 @@ struct TestCurve; impl EllipticCurve for TestCurve { type BaseField = TestField; type Coefficient = TestField; + // TODO: incorrect + type ScalarField = TestField; const EQUATION_A: Self::Coefficient = TestField::ONE; const EQUATION_B: Self::Coefficient = TestField::ZERO; @@ -40,6 +41,8 @@ struct TestCurveExtended; impl EllipticCurve for TestCurveExtended { type BaseField = TestExtension; type Coefficient = TestField; + // TODO: incorrect + type ScalarField = TestField; const EQUATION_A: Self::Coefficient = TestField::ONE; const EQUATION_B: Self::Coefficient = TestField::ZERO; @@ -50,10 +53,10 @@ impl EllipticCurve for TestCurveExtended { #[test] fn five_torsion() { - let generator = AffinePoint::::generator(); + let generator = AffinePoint::::GENERATOR; println!("Generator: {:?}", generator); for i in 1..6 { - let point = generator * i as u32; + let point = i as u32 * generator; println!("{:?} * P = {:?}", i, point); if i == 5 { assert_eq!(point, AffinePoint::Infinity); @@ -72,7 +75,7 @@ fn five_torsion() { println!("\n\n"); let torsion_generator = - if let AffinePoint::::Point(x, y) = AffinePoint::::generator() { + if let AffinePoint::::Point(x, y) = AffinePoint::::GENERATOR { // Apply the distortion map AffinePoint::::new( -TestExtension::from(x), @@ -85,7 +88,7 @@ fn five_torsion() { println!("Distortion map on generator: {:?}", torsion_generator); for i in 1..6 { - let point = torsion_generator * i as u32; + let point = i as u32 * torsion_generator; println!("{:?} * P = {:?}", i, point); if i == 5 { assert_eq!(point, AffinePoint::Infinity); @@ -108,7 +111,7 @@ fn five_torsion() { #[test] fn vertical_line_2p() { - let generator = AffinePoint::::generator(); + let generator = AffinePoint::::GENERATOR; let two_p = generator + generator; println!("2P: {:?}", two_p); // We should get: @@ -126,7 +129,7 @@ fn vertical_line_2p() { #[test] fn tangent_line_p() { - let p = AffinePoint::::generator(); + let p = AffinePoint::::GENERATOR; println!("P: {:?}", p); // We should get: // P = Point(PrimeField { value: 25 }, PrimeField { value: 30 }) @@ -146,7 +149,7 @@ fn tangent_line_p() { #[test] fn line_from_p_to_2p() { - let p = AffinePoint::::generator(); + let p = AffinePoint::::GENERATOR; let two_p = p + p; println!("P: {:?}", p); println!("2P: {:?}", two_p); @@ -170,8 +173,7 @@ fn line_from_p_to_2p() { #[test] fn miller_loop_check() { - let (p, q) = if let AffinePoint::::Point(x, y) = AffinePoint::::generator() - { + let (p, q) = if let AffinePoint::::Point(x, y) = AffinePoint::::GENERATOR { ( AffinePoint::::new(TestExtension::from(x), TestExtension::from(y)), // Apply the distortion map @@ -203,8 +205,7 @@ fn miller_loop_check() { #[test] fn pairing_check() { - let (p, q) = if let AffinePoint::::Point(x, y) = AffinePoint::::generator() - { + let (p, q) = if let AffinePoint::::Point(x, y) = AffinePoint::::GENERATOR { ( AffinePoint::::new(TestExtension::from(x), TestExtension::from(y)), // Apply the distortion map diff --git a/src/ecdsa.rs b/src/ecdsa.rs index efe459d..f2c5bee 100644 --- a/src/ecdsa.rs +++ b/src/ecdsa.rs @@ -1,7 +1,9 @@ //! ECDSA signature verification use std::hash::{DefaultHasher, Hasher}; -use self::field::prime::PlutoScalarField; +use algebra::field::FiniteField; +use curve::CurveGroup; + use super::*; // PARAMETERS @@ -22,35 +24,36 @@ use super::*; /// 5. Compute r = x_1 mod n. If r = 0, go back to step 3. /// 6. Compute s = k^(-1) (z + r * d_A) mod n. If s = 0, go back to step 3. /// 7. The signature is the pair (r, s). the pair (r, -s mod n) is also a valid signature. -pub fn sign(message: &[u8], private_key: PlutoScalarField) -> (PlutoScalarField, PlutoScalarField) { +pub fn sign>(message: &[u8], private_key: F) -> (F, F) { // Hash and extract bits - let z = hash_and_extract_bits(message, 17); + let bit_count = (F::ORDER.leading_zeros() - 1) as usize; + let z = hash_and_extract_bits::(message, bit_count); let mut rng = rand::rngs::OsRng; // Select a cryptographically secure random integer k from [1, n-1]. - let k = PlutoScalarField::new(rand::Rng::gen_range(&mut rng, 1..=PlutoScalarField::ORDER)); + let k = F::from(rand::Rng::gen_range(&mut rng, 1..=F::ORDER)); // Compute the curve point (x_1, y_1) = k × G. - let point = AffinePoint::::generator() * k; - let x_1 = match point { - AffinePoint::Point(x, _) => x, - _ => PlutoBaseField::ZERO, - }; + let point = G::GENERATOR * k; + let (mut x_1, _, is_infty) = point.xy(); + if is_infty { + x_1 = G::BaseField::ZERO; + } // Compute r = x_1 mod n. If r = 0, go back to step 3. - let r = PlutoScalarField::from(x_1.value); - if r == PlutoScalarField::ZERO { - return sign(message, private_key); + let r = F::from(x_1.into()); + if r == F::ZERO { + return sign::(message, private_key); } // Compute s = k^(-1) (z + r * d_A) mod n. If s = 0, go back to step 3. let k_inv = k.inverse().unwrap(); let s = k_inv * (z + r * private_key); - if s == PlutoScalarField::ZERO { - return sign(message, private_key); + if s == F::ZERO { + return sign::(message, private_key); } // The signature is the pair (Notable not nessisarily a point on the curve) (r, s). the pair // (r, -s mod n) is also a valid signature. - let r = PlutoScalarField::from(r.value); - let s = PlutoScalarField::from(s.value); + // let r = F::from(r.value); + // let s = F::from(s.value); (r, s) } @@ -69,50 +72,54 @@ pub fn sign(message: &[u8], private_key: PlutoScalarField) -> (PlutoScalarField, /// 5. Compute u_2 = rs^(-1) mod n. /// 6. Compute the curve point (x_1, y_1) = u_1 × G + u_2 × Q_A. If = O, the signature is invalid. /// 7. The signature is valid if r = x_1 mod n, invalid otherwise. -pub fn verify( +pub fn verify>( m: &[u8], - q_a: AffinePoint, - signature: (PlutoScalarField, PlutoScalarField), + q_a: G, + signature: (F, F), ) -> bool { // Check that n × Q_A = O. - if (q_a * 17) != AffinePoint::Infinity { + let (_, _, is_infty) = (q_a * F::ORDER.into()).xy(); + if !is_infty { return false; } // Verify that the signature is valid. - let (r, s): (PlutoScalarField, PlutoScalarField) = signature; + let (r, s): (F, F) = signature; // Verify that r and s are integers in the interval [1, n-1]. - if r == PlutoScalarField::ZERO || s == PlutoScalarField::ZERO { + if r == F::ZERO || s == F::ZERO { return false; } // Hash and extract bits - let z = hash_and_extract_bits(m, 17); + let bit_count = (F::ORDER.leading_zeros() - 1) as usize; + let z = hash_and_extract_bits::(m, bit_count); // Compute u_1 = zs^(-1) mod n. let s_inv = s.inverse().unwrap(); let u_1 = z * s_inv; // Compute u_2 = rs^(-1) mod n. let u_2 = r * s_inv; // Compute the curve point (x_1, y_1) = u_1 × G + u_2 × Q_A. If - let point = (AffinePoint::::generator() * u_1) + (q_a * u_2); - let (x_1, _) = match point { - AffinePoint::Point(x, y) => (x, y), - _ => (PlutoBaseField::ZERO, PlutoBaseField::ZERO), - }; - let x = PlutoScalarField::from(x_1.value); + let point = (G::GENERATOR * u_1) + (q_a * u_2); + let (x_1, _, is_infty) = point.xy(); + if is_infty { + panic!("signature invalid"); + } + let x = F::from(x_1.into()); r == x } /// Computes the hash of a message and extracts the leftmost bits. -fn hash_and_extract_bits(m: &[u8], bit_count: usize) -> PlutoScalarField { +fn hash_and_extract_bits(m: &[u8], bit_count: usize) -> F { let mut hasher = DefaultHasher::new(); m.hash(&mut hasher); let e = hasher.finish().to_be_bytes(); - let e_4_bytes = [e[0], e[1], e[2], e[3]]; - PlutoScalarField::from(u32::from_be_bytes(e_4_bytes) >> (32 - bit_count)) + // let e_4_bytes = [e[0], e[1], e[2], e[3]]; + F::from(usize::from_be_bytes(e) & ((1 << bit_count) - 1)) } #[cfg(test)] mod tests { + use algebra::{field::prime::PlutoScalarField, group::FiniteCyclicGroup, Finite}; + use super::*; #[test] @@ -122,10 +129,10 @@ mod tests { let s_key = PlutoScalarField::new(rand::Rng::gen_range(&mut rng, 1..=PlutoScalarField::ORDER)); // public key - let q_a = AffinePoint::::generator() * s_key; + let q_a = AffinePoint::::GENERATOR * s_key; let m = b"Hello, world!"; // sign the message - let signature = sign(m, s_key); + let signature = sign::>(m, s_key); println!("signature = {:?}", signature); assert!(verify(m, q_a, signature)); } @@ -135,10 +142,10 @@ mod tests { let mut rng = rand::rngs::OsRng; let s_key = PlutoScalarField::new(rand::Rng::gen_range(&mut rng, 1..=PlutoScalarField::ORDER)); // public key - let q_a = AffinePoint::::generator() * s_key; + let q_a = AffinePoint::::GENERATOR * s_key; let m = b"Hello, Pluto!"; // sign the message - let mut signature = sign(m, s_key); + let mut signature = sign::>(m, s_key); // Modify the signature to make it invalid signature.0 = PlutoScalarField::ZERO; // Invalidate r assert!(!verify(m, q_a, signature), "Signature should be invalid but was verified as valid."); diff --git a/src/encryption/asymmetric/rsa/mod.rs b/src/encryption/asymmetric/rsa/mod.rs index 049fa5a..8cba3d1 100644 --- a/src/encryption/asymmetric/rsa/mod.rs +++ b/src/encryption/asymmetric/rsa/mod.rs @@ -95,21 +95,26 @@ pub fn random_prime(first_prime: usize) -> usize { n } -fn is_prime(n: usize) -> bool { +/// Primality testing in a constant function for compile time checks +pub const fn is_prime(n: usize) -> bool { if n <= 1 { return false; } - for i in 2..=((n as f64).sqrt() as usize) { + let mut i = 2; + while i * i <= n { if n % i == 0 { return false; } + i += 1; } true } -const fn euler_totient(prime_1: u64, prime_2: u64) -> u64 { (prime_1 - 1) * (prime_2 - 1) } +/// Euler totient: Ψ(x) +pub const fn euler_totient(prime_1: u64, prime_2: u64) -> u64 { (prime_1 - 1) * (prime_2 - 1) } -const fn gcd(a: u64, b: u64) -> u64 { +/// GCD of two numbers +pub const fn gcd(a: u64, b: u64) -> u64 { if b == 0 { a } else { diff --git a/src/encryption/symmetric/aes/mod.rs b/src/encryption/symmetric/aes/mod.rs index 27e3247..ba0c1ec 100644 --- a/src/encryption/symmetric/aes/mod.rs +++ b/src/encryption/symmetric/aes/mod.rs @@ -6,17 +6,16 @@ use std::ops::Mul; use itertools::Itertools; -use crate::field::{extension::AESFieldExtension, prime::AESField}; - -pub mod sbox; -#[cfg(test)] pub mod tests; - use super::{BlockCipher, SymmetricEncryption}; use crate::{ + algebra::field::{extension::AESFieldExtension, prime::AESField}, encryption::symmetric::aes::sbox::{INVERSE_SBOX, SBOX}, - field::FiniteField, + Field, }; +pub mod sbox; +#[cfg(test)] pub mod tests; + /// A block in AES represents a 128-bit sized message data. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Block(pub [u8; 16]); diff --git a/src/hashes/mod.rs b/src/hashes/mod.rs index 5d339bc..2c50760 100644 --- a/src/hashes/mod.rs +++ b/src/hashes/mod.rs @@ -4,11 +4,11 @@ //! Currently, the only supported algorithm is SHA-256. #![doc = include_str!("./README.md")] pub mod sha256; -use crate::field::FiniteField; +use crate::Field; pub mod poseidon; /// Sponge trait defining absorb and squeeze behavior of sponge based hash function. -pub trait Sponge { +pub trait Sponge { /// apply round function of hash to the sponge state fn permute(&mut self); /// absorb takes arbitrary number of elements and continue to apply inner permutation on the diff --git a/src/hashes/poseidon/mod.rs b/src/hashes/poseidon/mod.rs index 72f3f4e..50b9665 100644 --- a/src/hashes/poseidon/mod.rs +++ b/src/hashes/poseidon/mod.rs @@ -6,11 +6,11 @@ pub mod sponge; pub use sponge::*; -use crate::field::FiniteField; +use crate::Field; /// Poseidon config used to instantiate hash function #[derive(Debug, Clone)] -pub struct PoseidonConfig { +pub struct PoseidonConfig { /// alpha used during sbox layer calculate `x^{alpha}` alpha: usize, /// width of the hash function that decides how many elements of finite field are stored in the @@ -30,12 +30,12 @@ pub struct PoseidonConfig { /// state contains current hash state of `width` length and [`PoseidonConfig`] /// contains config for rounds in the hash function. #[derive(Debug, Clone)] -pub struct Poseidon { +pub struct Poseidon { state: Vec, config: PoseidonConfig, } -impl PoseidonConfig { +impl PoseidonConfig { fn new( width: usize, alpha: usize, @@ -56,7 +56,7 @@ impl PoseidonConfig { } } -impl Poseidon { +impl Poseidon { /// instantiate hash function with required config pub fn new( width: usize, diff --git a/src/hashes/poseidon/sponge.rs b/src/hashes/poseidon/sponge.rs index 5791ba5..1d6d260 100644 --- a/src/hashes/poseidon/sponge.rs +++ b/src/hashes/poseidon/sponge.rs @@ -34,7 +34,7 @@ use std::marker::PhantomData; use super::Poseidon; -use crate::{field::FiniteField, hashes::Sponge}; +use crate::{hashes::Sponge, Field}; /// initialised sponge state pub struct Init; @@ -61,12 +61,12 @@ pub struct SpongeConfig { /// * `poseidon` - [`Poseidon`] struct with hash state, and config /// * `parameters` - [`SpongeConfig`] with sponge related parameters like `rate` and `absorb_index` #[derive(Debug, Clone)] -pub struct PoseidonSponge { +pub struct PoseidonSponge { poseidon: Poseidon, parameters: SpongeConfig, } -impl PoseidonSponge { +impl PoseidonSponge { /// create new poseidon sponge object with [`Poseidon`] hash object and [`SpongeConfig`] pub fn new( width: usize, @@ -99,7 +99,7 @@ impl PoseidonSponge { } } -impl PoseidonSponge { +impl PoseidonSponge { /// start absorption stage of a sponge pub fn start_absorbing(self) -> PoseidonSponge { PoseidonSponge { @@ -115,7 +115,7 @@ impl PoseidonSponge { } } -impl PoseidonSponge { +impl PoseidonSponge { /// perform sponge absorption of arbitrary length finite field elements. permutes the state after /// each `rate` length chunk has been fully absorbed. /// @@ -213,7 +213,7 @@ impl PoseidonSponge { } } -impl PoseidonSponge { +impl PoseidonSponge { /// squeezes arbitrary element output /// /// ## Example @@ -274,7 +274,7 @@ impl PoseidonSponge { } } -impl Sponge for PoseidonSponge { +impl Sponge for PoseidonSponge { fn permute(&mut self) { self.permute(); } fn absorb(&mut self, elements: &[F]) -> Result<(), &str> { @@ -285,7 +285,7 @@ impl Sponge for PoseidonSponge { fn squeeze(&mut self, _: usize) -> Result, &str> { Err("sponge is in squeeze state") } } -impl Sponge for PoseidonSponge { +impl Sponge for PoseidonSponge { fn permute(&mut self) { self.permute(); } fn absorb(&mut self, _: &[F]) -> Result<(), &str> { Err("sponge is in squeezing state") } diff --git a/src/hashes/poseidon/tests/mod.rs b/src/hashes/poseidon/tests/mod.rs index 2ac911e..0a6375f 100644 --- a/src/hashes/poseidon/tests/mod.rs +++ b/src/hashes/poseidon/tests/mod.rs @@ -1,5 +1,5 @@ use super::*; -use crate::field::prime::PlutoBaseField; +use crate::PlutoBaseField; mod constants; use rstest::{fixture, rstest}; @@ -15,7 +15,7 @@ use ark_crypto_primitives::sponge::{ use constants::{constants, ALPHA, NUM_F, NUM_P, WIDTH}; use rand::{thread_rng, Rng}; -fn load_constants() -> (Vec, Vec>) { +fn load_constants() -> (Vec, Vec>) { let (rc, mds) = constants(); let rc = rc.into_iter().map(|val| F::from(val)).collect(); @@ -25,7 +25,7 @@ fn load_constants() -> (Vec, Vec>) { } #[allow(dead_code)] -fn random_constants(width: usize, num_rounds: usize) -> (Vec, Vec>) +fn random_constants(width: usize, num_rounds: usize) -> (Vec, Vec>) where rand::distributions::Standard: rand::distributions::Distribution { let mut rng = thread_rng(); let rc: Vec = (0..num_rounds * width).map(|_| rng.gen::()).collect(); diff --git a/src/kzg/setup.rs b/src/kzg/setup.rs index 7f90324..b7dcb44 100644 --- a/src/kzg/setup.rs +++ b/src/kzg/setup.rs @@ -1,6 +1,8 @@ //! Does the SRS setup for the KZG10 scheme. -use self::{curve::pairing::pairing, field::prime::PlutoScalarField}; +use algebra::group::FiniteCyclicGroup; + +use self::{curve::pairing::pairing, PlutoScalarField}; use super::*; /// simple setup to get params. @@ -11,8 +13,8 @@ pub fn setup() -> (Vec>, Vec::from(AffinePoint::::generator()); - let g2 = AffinePoint::::generator(); + let g1 = AffinePoint::::from(AffinePoint::::GENERATOR); + let g2 = AffinePoint::::GENERATOR; // NOTE: Just sample the d of both for now. // - g1 and g2 SRS have variable sizes for diff kzg uses // - in eth blobs, g1 is 4096 elements, g2 is 16 elements @@ -90,16 +92,12 @@ pub fn check( let g2 = g2_srs[1]; // e(pi, g2 - gen * point) - let lhs = pairing::( - q, - g2 - AffinePoint::::generator() * point, - ); + let lhs = + pairing::(q, g2 - AffinePoint::::GENERATOR * point); // e(p - g1 * value, gen) - let rhs = pairing::( - p - g1 * value, - AffinePoint::::generator(), - ); + let rhs = + pairing::(p - g1 * value, AffinePoint::::GENERATOR); println!("lhs {:?}", lhs); println!("rhs {:?}", rhs); diff --git a/src/kzg/tests.rs b/src/kzg/tests.rs index 1a1c152..32fb576 100644 --- a/src/kzg/tests.rs +++ b/src/kzg/tests.rs @@ -1,5 +1,7 @@ +use algebra::group::FiniteCyclicGroup; + use super::*; -use crate::{curve::pairing::pairing, field::prime::PlutoScalarField}; +use crate::{curve::pairing::pairing, PlutoScalarField}; #[test] fn test_setup() { @@ -43,7 +45,7 @@ fn test_setup() { PlutoBaseFieldExtension::new([PlutoBaseField::new(90), PlutoBaseField::ZERO]), PlutoBaseFieldExtension::new([PlutoBaseField::ZERO, PlutoBaseField::new(82)]), ); - let g2_gen = AffinePoint::::generator(); + let g2_gen = AffinePoint::::GENERATOR; let expected_g2srs = vec![g2_gen, expected_2g]; assert_eq!(g2srs, expected_g2srs); @@ -193,12 +195,12 @@ fn all_srs_combinations() { println!("Loop for g2 {:?}", g2); let lhs = pairing::( paring_params.q, - *g2 - AffinePoint::::generator() * paring_params.point, + *g2 - AffinePoint::::GENERATOR * paring_params.point, ); let rhs = pairing::( paring_params.p - g1 * paring_params.value, - AffinePoint::::generator(), + AffinePoint::::GENERATOR, ); if lhs == rhs { println!( @@ -231,18 +233,18 @@ fn e2e( // We can look at `g1srs` and see it is in `G1` and `g2srs` is in `G2` dbg!(paring_params.g1srs.first().unwrap()); for i in 0..17 { - println!("{}: {:?}", i, *paring_params.g1srs.first().unwrap() * i); + println!("{}: {:?}", i, i * *paring_params.g1srs.first().unwrap()); } assert_eq!( - *paring_params.g1srs.first().unwrap() * 17u32, + 17u32 * *paring_params.g1srs.first().unwrap(), AffinePoint::::Infinity ); dbg!(paring_params.g2srs.first().unwrap()); for i in 0..17 { - println!("{}: {:?}", i, *paring_params.g2srs.first().unwrap() * i); + println!("{}: {:?}", i, i * *paring_params.g2srs.first().unwrap()); } assert_eq!( - *paring_params.g2srs.first().unwrap() * 17u32, + 17u32 * *paring_params.g2srs.first().unwrap(), AffinePoint::::Infinity ); diff --git a/src/lib.rs b/src/lib.rs index 7dab5ca..40ae5dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,14 +20,15 @@ #![feature(const_for)] #![feature(const_option)] #![feature(generic_const_exprs)] +#![feature(specialization)] #![warn(missing_docs)] +pub mod algebra; pub mod codes; pub mod compiler; pub mod curve; pub mod ecdsa; pub mod encryption; -pub mod field; pub mod hashes; pub mod kzg; pub mod polynomial; @@ -36,8 +37,8 @@ pub mod tree; use core::{ fmt::{self, Display, Formatter}, hash::Hash, - iter::{Product, Sum}, - ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}, + iter::Sum, + ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Rem, Sub, SubAssign}, }; use rand::{ @@ -47,14 +48,14 @@ use rand::{ #[cfg(test)] use rstest::{fixture, rstest}; use self::{ + algebra::field::{ + extension::{GaloisField, PlutoBaseFieldExtension}, + prime::{PlutoBaseField, PlutoPrime, PlutoScalarField, PrimeField}, + Field, + }, curve::{ pluto_curve::{PlutoBaseCurve, PlutoExtendedCurve}, AffinePoint, }, - field::{ - extension::{GaloisField, PlutoBaseFieldExtension}, - prime::{PlutoBaseField, PlutoPrime, PrimeField}, - FiniteField, - }, polynomial::{Monomial, Polynomial}, }; diff --git a/src/polynomial/mod.rs b/src/polynomial/mod.rs index 9118119..49d9a74 100644 --- a/src/polynomial/mod.rs +++ b/src/polynomial/mod.rs @@ -7,19 +7,20 @@ //! [`Monomial`] [`Basis`]. //! //! - [`Polynomial`] struct represents a polynomial in any basis. These are generic over the -//! [`Basis`] and [`FiniteField`] traits. +//! [`Basis`] and [`FiniteFiniteField`] traits. //! - [`Basis`] trait is used to specify the basis of the polynomial which can be either: //! - [`Monomial`] basis as shown above. //! - [`Lagrange`] basis which is used in the [Lagrange interpolation](https://en.wikipedia.org/wiki/Lagrange_polynomial). //! - Includes arithmetic operations such as addition, subtraction, multiplication, and division in //! the [`arithmetic`] module. The [`Polynomial`] struct is generic over the [`Basis`] and -//! [`FiniteField`] traits. +//! [`FiniteFiniteField`] traits. //! - Includes Discrete Fourier Transform (DFT) for polynomials in the [`Monomial`] basis to convert //! into the [`Lagrange`] basis via evaluation at the roots of unity. use std::array; use super::*; +use crate::algebra::field::FiniteField; pub mod arithmetic; #[cfg(test)] mod tests; @@ -120,15 +121,15 @@ impl Polynomial { self.coefficients.iter().rev().find(|&&coeff| coeff != F::ZERO).copied().unwrap_or(F::ZERO) } - /// Evaluates the polynomial at a given [`FiniteField`] element `x` using the [`Monomial`] basis. - /// This is not using Horner's method or any other optimization. + /// Evaluates the polynomial at a given [`FiniteFiniteField`] element `x` using the [`Monomial`] + /// basis. This is not using Horner's method or any other optimization. /// /// ## Arguments: /// - `x`: The field element at which to evaluate the polynomial. /// /// ## Returns: /// - The result of evaluating the polynomial at `x` which is an element of the associated - /// [`FiniteField`]. + /// [`FiniteFiniteField`]. pub fn evaluate(&self, x: F) -> F { let mut result = F::ZERO; for (i, c) in self.coefficients.iter().enumerate() { @@ -234,8 +235,8 @@ impl Polynomial { /// evaluation of the polynomial at the roots of unity. /// /// ## Panics - /// - This function will panic in calling [`FiniteField::primitive_root_of_unity`] if the field - /// does not have roots of unity for the degree of the polynomial. + /// - This function will panic in calling [`FiniteFiniteField::primitive_root_of_unity`] if the + /// field does not have roots of unity for the degree of the polynomial. pub fn dft(&self) -> Polynomial, F, D> { let n = self.num_terms(); let primitive_root_of_unity = F::primitive_root_of_unity(n); @@ -309,11 +310,11 @@ impl Polynomial, F, D> { /// polynomial. The evaluation of the polynomial at `x` is then given by $L(x)$. /// /// ## Arguments: - /// - `x`: The field element as [`FiniteField`] at which to evaluate the polynomial. + /// - `x`: The field element as [`FiniteFiniteField`] at which to evaluate the polynomial. /// /// ## Returns: /// - The result of evaluating the polynomial at `x` which is an element of the associated - /// [`FiniteField`]. + /// [`FiniteFiniteField`]. pub fn evaluate(&self, x: F) -> F { let n = self.coefficients.len();