Skip to content

Commit

Permalink
feat: algebra (#135)
Browse files Browse the repository at this point in the history
* add `FiniteGroup` trait

* update tests

* add docs

* move ecdsa to generic group

* remove additivegroup

* fix doc test

* update readme

* move to algebra module

* add mul group

* fix docs

* update docs

* add `Group` and `AbelianGroup` trait

* separate Field and FiniteField traits

* satisfy lint

* add permutation group example

* change example name and AbelianGroup impl
  • Loading branch information
lonerapier authored Jul 31, 2024
1 parent 32111ee commit 3fd764b
Show file tree
Hide file tree
Showing 42 changed files with 875 additions and 297 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ dump
*.swp
*.swo

# coverage
lcov.info
## macOS
.DS_Store

# sage
*.sage.py
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ ark-std ={ git="https://github.com/arkworks-rs/std/" }

[[example]]
name="aes_chained_cbc"

[[example]]
name="symmetric_group"
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,24 @@
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)
- [Polynomials](src/polynomial/mod.rs)
- [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)

### 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)
Expand Down
135 changes: 135 additions & 0 deletions examples/symmetric_group.rs
Original file line number Diff line number Diff line change
@@ -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<Self> {
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<usize> for DihedralGroup {
type Output = Self;

fn mul(self, rhs: usize) -> Self::Output { Self::scalar_mul(&self, rhs) }
}

impl MulAssign<usize> 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);
}
46 changes: 46 additions & 0 deletions src/algebra/README.md
Original file line number Diff line number Diff line change
@@ -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.
38 changes: 20 additions & 18 deletions src/field/README.md → src/algebra/field/README.md
Original file line number Diff line number Diff line change
@@ -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).
Expand All @@ -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<Self>` - The multiplicative inverse of a nonzero field element.
- `inverse(&self) -> Option<Self>` - 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.
Expand All @@ -32,16 +34,15 @@ The only additional constraint aside from the `FiniteField` trait and adherance

We will discuss `PrimeField<P>` 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:
Expand All @@ -50,18 +51,19 @@ pub struct PrimeField<const P: usize> {
value: usize,
}
```
that implements the `FiniteField` trait and has some compile-time constructions.
For example, upon creation of an element of `PrimeField<P>` we utilize the `const fn is_prime<const P: usize>()` 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<P>` we utilize the `const fn is_prime<const P: usize>()` function which will `panic!` if `P` is not prime.
**Hence, it is impossible to compile a program for which you construct `PrimeField<P>` 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<P>` are implemented in `field::prime::arithmetic`.

### `GaloisField`
The `GaloisField` struct is a wrapper around a `PrimeField<P>` by:
```rust
use ronkathon::algebra::field::prime::PrimeField;
pub struct GaloisField<const N: usize, const P: usize> {
value: [PrimeField<P>; N],
}
Expand All @@ -70,5 +72,5 @@ where the `[PrimeField<P>; N]` is the representation of the field element as coe

We implement `ExtensionField` for specific instances of `GaloisField<N, P>` as, at the moment, we do not have a general compile-time-based implementation of extension fields as we do with `PrimeField<P>`, though it is possible to do so.
Instead, we have implemented much of the arithmetic operations for `GaloisField<N, P>` 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.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -36,13 +35,16 @@ where [(); 1 << K]:
}
}

impl<const K: usize> FiniteField for BinaryTowers<K>
impl<const K: usize> Finite for BinaryTowers<K>
where [(); 1 << K]:
{
const ONE: Self = Self::one();
const ORDER: usize = 1 << (1 << K);
// TODO: incorrect
const PRIMITIVE_ELEMENT: Self = Self::ONE;
}

impl<const K: usize> Field for BinaryTowers<K>
where [(); 1 << K]:
{
const ONE: Self = Self::one();
const ZERO: Self = Self::new([BinaryField::ZERO; 1 << K]);

fn pow(self, power: usize) -> Self {
Expand All @@ -69,6 +71,13 @@ where [(); 1 << K]:
}
}

impl<const K: usize> FiniteField for BinaryTowers<K>
where [(); 1 << K]:
{
// TODO: incorrect
const PRIMITIVE_ELEMENT: Self = Self::ONE;
}

impl<const K: usize> Default for BinaryTowers<K>
where [(); 1 << K]:
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Self> {
Expand All @@ -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<usize> for BinaryField {
fn from(value: usize) -> Self {
match value {
Expand Down
Loading

0 comments on commit 3fd764b

Please sign in to comment.