Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Curve whispering #54

Merged
merged 7 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions math/field.sage
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,16 @@ print(omega_n, "^", n, " = ", omega_n ^ n)
# extension field of degree 2
Ft.<t> = F[]

# irreducible element: t^2-2
P = Ft(t ^ 2 - 2)
# irreducible element: t^2 + 2
P = Ft(t ^ 2 + 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

magical u polynomial. someday i want to learn how to find this irreducible element.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some algorithms to do it! Essentially an irreducible element here means that it cannot be factored in our base field. Wheres x^ + 1 = (x+ 10)(x - 10) in our base field. That is a counter example!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Galois tells us how

assert P.is_irreducible()

F = GF(101)
Ft.<t> = F[]
P = Ft(t ^ 2 + 2)
F_2 = GF(101 ^ 2, name="t", modulus=P)
print("extension field:", F_2, "of order:", F_2.order())

# Primitive element
f_2_primitive_element = F_2([2, 1])
f_2_primitive_element = F_2.primitive_element()
assert f_2_primitive_element.multiplicative_order() == (101^2) -1
print("Primitive element of F_2:", f_2_primitive_element, f_2_primitive_element.multiplicative_order())

# 100th root of unity
Expand All @@ -85,4 +86,20 @@ quotient = (F_2_order-1)//root_of_unity_order
f_2_omega_n = f_2_primitive_element ^ quotient
print("The", root_of_unity_order, "th root of unity of extension field is: ", f_2_omega_n)

######################################################################
######################################################################

# Define the field and elements for computation
F = GF(101)
Ft.<t> = F[]

# Define the polynomial for the extension field
P = Ft(t^2 + 2)
F_2 = GF(101^2, name="t", modulus=P)

# Define the elements 50 and 60t in the extension field
numerator = F_2(50)
denominator = F_2(62*t)

# Compute the division of 50 by 60t in the extension field
result = numerator / denominator
print("The division of 50 by 60t in the extension field is:", result)
54 changes: 38 additions & 16 deletions src/curves/g2_curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use super::*;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
pub struct G2Curve {}
// The Elliptic curve $y^2=x^3+3$, i.e.
// - a = 0
// - b = 3
// a = 0
// b = 3

impl CurveParams for G2Curve {
type FieldElement = Ext2<GF101>;
Expand Down Expand Up @@ -54,22 +54,44 @@ impl G2Curve {
}
}

#[cfg(test)]
mod tests {
// use super::*;
// use crate::curves::AffinePoint;
use super::*;
use crate::curves::AffinePoint;

// #[test]
// fn on_curve() {
// let gen = G2Curve::on_curve(G2Curve::GENERATOR.0, G2Curve::GENERATOR.1);
// }
#[test]
fn point_doubling() {
let g = AffinePoint::<G2Curve>::generator();
let two_g = g.point_doubling();

// #[test]
// fn doubling() {
// let g = AffinePoint::<G2Curve>::generator();
// println!("g: {:?}", g)
let expected_2g = AffinePoint::<G2Curve>::new(
Ext2::<GF101>::new(GF101::new(90), GF101::ZERO),
Ext2::<GF101>::new(GF101::ZERO, GF101::new(82)),
);
let expected_g = AffinePoint::<G2Curve>::new(
Ext2::<GF101>::new(GF101::new(36), GF101::ZERO),
Ext2::<GF101>::new(GF101::ZERO, GF101::new(31)),
);

// // want to asset that g = (36, 31*X)
// // right now this ^ fails to construct as it doesn't believe that the generator is a valid
// point on the curve // want to asset that 2g = (90 , 82*X)
// }
assert_eq!(two_g, expected_2g);
assert_eq!(g, expected_g);
}

#[test]
fn scalar_multiplication_rhs() {
let g = AffinePoint::<G2Curve>::generator();
let two_g = g * 2;
let expected_2g = g.point_doubling();
assert_eq!(two_g, expected_2g);
assert_eq!(-two_g, -expected_2g);
}

#[test]
fn scalar_multiplication_lhs() {
let g = AffinePoint::<G2Curve>::generator();
let two_g = 2 * g;
let expected_2g = g.point_doubling();
assert_eq!(two_g, expected_2g);
assert_eq!(-two_g, -expected_2g);
}
}
7 changes: 3 additions & 4 deletions src/curves/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub struct Curve<F: FiniteField> {
/// say 2 is in GF101
pub trait CurveParams: 'static + Copy + Clone + fmt::Debug + Default + Eq + Ord {
/// Integer field element type
type FieldElement: FiniteField + Neg;
type FieldElement: FiniteField + Neg + Mul;
/// Order of this elliptic curve, i.e. number of elements in the scalar field.
const ORDER: u32;
/// Coefficient `a` in the Weierstrass equation of this elliptic curve.
Expand Down Expand Up @@ -40,7 +40,6 @@ pub enum AffinePoint<C: CurveParams> {

impl<C: CurveParams> AffinePoint<C> {
pub fn new(x: C::FieldElement, y: C::FieldElement) -> Self {
println!("X: {:?}, Y: {:?}", x, y);
// okay so this is breaking because the curve equation doesn't know how to plug in polynomials.
// y = 31x -> y^2 = 52x^2
// x = 36 -> x^3 = 95 + 3
Expand Down Expand Up @@ -148,13 +147,14 @@ impl<C: CurveParams> Add for AffinePoint<C> {
}
}

// NOTE: Apparently there is a faster way to do this with twisted curve methods
impl<C: CurveParams> AffinePoint<C> {
pub fn point_doubling(self) -> AffinePoint<C> {
let (x, y) = match self {
AffinePoint::XY(x, y) => (x, y),
AffinePoint::Infty => panic!("Cannot double point at infinity"),
};
// m = 3x^2 / 26
// m = (3x^2) / (2y)
let m = (C::THREE * x * x) / (C::TWO * y);

// 2P = (m^2 - 2x, m(3x - m^2)- y)
Expand All @@ -165,7 +165,6 @@ impl<C: CurveParams> AffinePoint<C> {

pub fn generator() -> Self {
let (x, y) = C::GENERATOR;
println!("X: {:?}, Y: {:?}", x, y);
AffinePoint::new(x, y)
}
}
Expand Down
114 changes: 15 additions & 99 deletions src/field/gf_101.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,18 @@ use rand::distributions::{Distribution, Standard};
use super::*;

const PLUTO_FIELD_PRIME: u32 = 101;
// value chosen such that 2^k is closest power of two from modulus
const MONTY_BITS: u32 = 7;
// mask used in (mod R) operation for montgomery reduciton
const MONTY_MASK: u32 = (1 << MONTY_BITS) - 1;
// (-P^-1) mod 2^MONTY_BITS
const MONTY_MU: u32 = 19;

#[derive(Copy, Clone, Default, Serialize, Deserialize, Debug, Hash, PartialEq, Eq)]
pub struct GF101 {
value: u32,
}

impl fmt::Display for GF101 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", from_monty(self.value))
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.value) }
}

impl GF101 {
pub const fn new(value: u32) -> Self { Self { value: to_monty(value) } }
pub const fn new(value: u32) -> Self { Self { value: value % PLUTO_FIELD_PRIME } }
}

impl FiniteField for GF101 {
Expand All @@ -31,6 +23,7 @@ impl FiniteField for GF101 {
const NEG_ONE: Self = Self::new(Self::ORDER - 1);
const ONE: Self = Self::new(1);
const ORDER: Self::Storage = PLUTO_FIELD_PRIME;
const THREE: Self = Self::new(3);
const TWO: Self = Self::new(2);
const ZERO: Self = Self::new(0);

Expand Down Expand Up @@ -79,7 +72,7 @@ impl Sub for GF101 {

fn sub(self, rhs: Self) -> Self {
let (mut diff, over) = self.value.overflowing_sub(rhs.value);
let corr = if over { PLUTO_FIELD_PRIME } else { 0 };
let corr = if over { Self::ORDER } else { 0 };
diff = diff.wrapping_add(corr);
Self { value: diff }
}
Expand All @@ -92,7 +85,7 @@ impl SubAssign for GF101 {
impl Mul for GF101 {
type Output = Self;

fn mul(self, rhs: Self) -> Self { Self { value: from_monty(self.value * rhs.value) } }
fn mul(self, rhs: Self) -> Self { Self { value: (self.value * rhs.value) % Self::ORDER } }
}

impl MulAssign for GF101 {
Expand Down Expand Up @@ -141,86 +134,16 @@ impl Distribution<GF101> for Standard {
}
}

#[must_use]
#[inline]
/// Converts a number to montgomery form: \bar{x} := x * R mod N.
/// R is chosen such that gcd(R, N) = 1, usually nearest 2^k to N.
///
/// Arithmetic in finite fields involves mod N operation which involves
/// division, a very costly operation as compared to other arithmetic operations.
/// But, division by 2^k only involves shifting right by `k` bits. Aim of montgomery
/// form is to make resulting number divisible by 2^k.
const fn to_monty(val: u32) -> u32 {
(((val as u64) << MONTY_BITS) % PLUTO_FIELD_PRIME as u64) as u32
impl From<u32> for GF101 {
fn from(val: u32) -> Self { Self::new(val) }
}

#[must_use]
#[inline]
/// Computes x*R^{-1} mod N.
/// Assumes: `x` is in montgomery form
///
/// Add such a multiple `m` of `N` such that `x` is divisible by `R` implies (x + mN) % R^{-1} = 0.
/// So, m = x*(-N)^{-1} % R satisfies above relation. Precompute N' = (-N)^{-1} mod R.
/// - Precompute: N' = (-N)^{-1}
/// - m = x*N' mod R (1 mult)
/// - u = m*N (1 mult)
/// - t = x+u mod R
/// - t \in [0, 2P), subtract N if t >= N
///
/// Montgomery arithmetic allows to perform modular multiplication in 3 mults, 2 mults per
/// reduction, and some bit shifts and masks since R is a power of 2, saving a costly division.
///
/// # Examples
/// ```ignore
/// let N = 101;
/// let a = to_monty(10);
/// let b = to_monty(20);
/// let c = from_monty(a * b);
/// assert_eq!(from_monty(c), 99);
/// ```
fn from_monty(x: u32) -> u32 {
let x = x as u64;
let m = x.wrapping_mul(MONTY_MU as u64) & (MONTY_MASK as u64); // x*N' % R
let u = m * (PLUTO_FIELD_PRIME as u64); // m*P
let t = ((x + u) >> MONTY_BITS) as u32; // x+mP / R
let corr = if t >= PLUTO_FIELD_PRIME { PLUTO_FIELD_PRIME } else { 0 };
t.wrapping_sub(corr)
impl From<u64> for GF101 {
fn from(val: u64) -> Self { Self::new(val as u32) }
}

// β=2^7
// I=β^2/N
const _INV_APPROX: u32 = (1 << (2 * MONTY_BITS)) / PLUTO_FIELD_PRIME;

#[must_use]
#[inline]
/// Adapted from [Algorithm 1](https://hackmd.io/@chaosma/SyAvcYFxh).
///
/// Computes x mod N using barret reduction method. Assumes x < N^2.
/// Modular reduction involves division by N, barret's method approximates
/// value of 1/N with m/2^k so that costly division operation is substituted with
/// bit shifts. 2^2k is used because x < n*n < 2^k * 2^k. This allows to approximate
/// value of q closer to x/n.
///
/// x = q*N + r => r = x-qN => q = x/N => approximate q as ⌊m/2^2k⌋ => approximate m as ⌊2^2k/N⌋
///
/// - Precompute: I = ⌊2^{2k}/N⌋. floor is used as approximation function, implicitly used in
/// division
/// - q = x*I / 2^2k. divide by 2^{2k} again to approximate a value closer to 1/N
/// - r = x-qN
/// - r \in [0, 2P), subtract N if t >= N
///
/// # Examples
/// ```ignore
/// let x = 200 * 10;
/// let res = barret_reduction(x);
/// assert_eq!(res, x % PLUTO_FIELD_PRIME);
/// ```
fn _barret_reduction(x: u32) -> u32 {
assert!(x < (PLUTO_FIELD_PRIME.pow(2)));
let q = (x * _INV_APPROX) >> (2 * MONTY_BITS); // q = ⌊x*I/β^2⌋
let r = x - (q * PLUTO_FIELD_PRIME); // t = x - q*N
let corr = if r >= PLUTO_FIELD_PRIME { PLUTO_FIELD_PRIME } else { 0 };
r.wrapping_sub(corr)
impl From<usize> for GF101 {
fn from(val: usize) -> Self { Self::new(val as u32) }
}

#[cfg(test)]
Expand Down Expand Up @@ -257,13 +180,6 @@ mod tests {
assert_eq!(c, GF101::new(99));
}

#[test]
fn test_barret_reduction() {
let x = 200 * 10;
let res = _barret_reduction(x);
assert_eq!(res, x % PLUTO_FIELD_PRIME);
}

#[test]
fn zero() {
let f = GF101::new(0);
Expand Down Expand Up @@ -401,9 +317,9 @@ mod tests {
let omega = GF101::primitive_root_of_unity(n);
println!("omega: {:?}", omega);
assert_eq!(omega, GF101::new(95));
let omega_n = omega.pow(n);
let omega_n = omega.pow(n.into());
for i in 1..n {
let omega_i = omega.pow(i);
let omega_i = omega.pow(i.into());
println!("omega^{}: {:?}", i, omega_i);
assert_ne!(omega_i, GF101::new(1));
}
Expand All @@ -414,11 +330,11 @@ mod tests {
println!("omega: {:?}", omega);
assert_eq!(omega, GF101::new(16));
for i in 1..n {
let omega_i = omega.pow(i);
let omega_i = omega.pow(i.into());
println!("omega^{}: {:?}", i, omega_i);
assert_ne!(omega_i, GF101::new(1));
}
let omega_n = omega.pow(n);
let omega_n = omega.pow(n.into());
assert_eq!(omega_n, GF101::new(1));
}

Expand Down
Loading