From 3407d13e4b251e2317299ebc18cd8ed4aa3769df Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 12 Nov 2020 20:13:13 +0000 Subject: [PATCH] Move curves and fields into tweedle module --- benches/arithmetic.rs | 3 +- src/arithmetic.rs | 5 +- src/arithmetic/curves.rs | 700 +--------------------- src/arithmetic/fields.rs | 29 +- src/lib.rs | 1 + src/plonk.rs | 3 +- src/poly/commitment.rs | 7 +- src/poly/multiopen.rs | 3 +- src/tweedle.rs | 10 + src/tweedle/curves.rs | 703 +++++++++++++++++++++++ src/tweedle/fields.rs | 23 + src/{arithmetic => tweedle}/fields/fp.rs | 4 +- src/{arithmetic => tweedle}/fields/fq.rs | 4 +- src/{arithmetic => tweedle}/macros.rs | 0 14 files changed, 759 insertions(+), 736 deletions(-) create mode 100644 src/tweedle.rs create mode 100644 src/tweedle/curves.rs create mode 100644 src/tweedle/fields.rs rename src/{arithmetic => tweedle}/fields/fp.rs (99%) rename src/{arithmetic => tweedle}/fields/fq.rs (99%) rename src/{arithmetic => tweedle}/macros.rs (100%) diff --git a/benches/arithmetic.rs b/benches/arithmetic.rs index 49d965b5..a4fe17f8 100644 --- a/benches/arithmetic.rs +++ b/benches/arithmetic.rs @@ -2,9 +2,10 @@ extern crate criterion; extern crate halo2; -use crate::arithmetic::{small_multiexp, EqAffine, Field, Fp, Fq}; +use crate::arithmetic::{small_multiexp, Field}; use crate::poly::commitment::Params; use crate::transcript::DummyHash; +use crate::tweedle::{EqAffine, Fp, Fq}; use halo2::*; use criterion::{black_box, Criterion}; diff --git a/src/arithmetic.rs b/src/arithmetic.rs index a9b780b6..cab568ef 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -3,8 +3,6 @@ use crossbeam_utils::thread; -#[macro_use] -mod macros; mod curves; mod fields; @@ -489,6 +487,9 @@ pub fn lagrange_interpolate(points: &[F], evals: &[F]) -> Vec { } } +#[cfg(test)] +use crate::tweedle::Fp; + #[test] fn test_lagrange_interpolate() { let points = (0..5).map(|_| Fp::random()).collect::>(); diff --git a/src/arithmetic/curves.rs b/src/arithmetic/curves.rs index 7bb28503..184c7bc8 100644 --- a/src/arithmetic/curves.rs +++ b/src/arithmetic/curves.rs @@ -1,13 +1,12 @@ -//! This module contains implementations for the Tweedledum and Tweedledee -//! elliptic curve groups. The `Curve`/`CurveAffine` abstractions allow us to -//! write code that generalizes over these two groups. +//! This module contains the `Curve`/`CurveAffine` abstractions that allow us to +//! write code that generalizes over a pair of groups. use core::cmp; use core::fmt::Debug; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -use super::{Field, Fp, Fq, Group}; +use super::{Field, Group}; /// This trait is a common interface for dealing with elements of an elliptic /// curve group in the "projective" form, where that arithmetic is usually more @@ -165,696 +164,3 @@ pub trait CurveAffine: /// Returns the curve constant $b$ fn b() -> Self::Base; } - -macro_rules! new_curve_impl { - ($name:ident, $name_affine:ident, $base:ident, $scalar:ident) => { - /// Represents a point in the projective coordinate space. - #[derive(Copy, Clone, Debug)] - pub struct $name { - x: $base, - y: $base, - z: $base, - } - - impl $name { - const fn curve_constant_b() -> $base { - // NOTE: this is specific to b = 5 - $base::from_raw([5, 0, 0, 0]) - } - } - - /// Represents a point in the affine coordinate space (or the point at - /// infinity). - #[derive(Copy, Clone, Debug)] - pub struct $name_affine { - x: $base, - y: $base, - infinity: Choice, - } - - impl Curve for $name { - type Affine = $name_affine; - type Scalar = $scalar; - type Base = $base; - - fn zero() -> Self { - Self { - x: $base::zero(), - y: $base::zero(), - z: $base::zero(), - } - } - - fn one() -> Self { - // NOTE: This is specific to b = 5 - - const NEGATIVE_ONE: $base = $base::neg(&$base::one()); - const TWO: $base = $base::from_raw([2, 0, 0, 0]); - - Self { - x: NEGATIVE_ONE, - y: TWO, - z: $base::one(), - } - } - - fn is_zero(&self) -> Choice { - self.z.is_zero() - } - - fn to_affine(&self) -> Self::Affine { - let zinv = self.z.invert().unwrap_or($base::zero()); - let zinv2 = zinv.square(); - let x = self.x * zinv2; - let zinv3 = zinv2 * zinv; - let y = self.y * zinv3; - - let tmp = $name_affine { - x, - y, - infinity: Choice::from(0u8), - }; - - $name_affine::conditional_select(&tmp, &$name_affine::zero(), zinv.is_zero()) - } - - fn double(&self) -> Self { - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - // - // There are no points of order 2. - - let a = self.x.square(); - let b = self.y.square(); - let c = b.square(); - let d = self.x + b; - let d = d.square(); - let d = d - a - c; - let d = d + d; - let e = a + a + a; - let f = e.square(); - let z3 = self.z * self.y; - let z3 = z3 + z3; - let x3 = f - (d + d); - let c = c + c; - let c = c + c; - let c = c + c; - let y3 = e * (d - x3) - c; - - let tmp = $name { - x: x3, - y: y3, - z: z3, - }; - - $name::conditional_select(&tmp, &$name::zero(), self.is_zero()) - } - - /// Apply the curve endomorphism by multiplying the x-coordinate - /// by an element of multiplicative order 3. - fn endo(&self) -> Self { - $name { - x: self.x * $base::ZETA, - y: self.y, - z: self.z, - } - } - - fn b() -> Self::Base { - $name::curve_constant_b() - } - - fn is_on_curve(&self) -> Choice { - // Y^2 - X^3 = 5(Z^6) - - (self.y.square() - (self.x.square() * self.x)) - .ct_eq(&((self.z.square() * self.z).square() * $name::curve_constant_b())) - | self.z.is_zero() - } - - fn batch_to_affine(p: &[Self], q: &mut [Self::Affine]) { - assert_eq!(p.len(), q.len()); - - let mut acc = $base::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of $name_affine to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - acc = $base::conditional_select(&(acc * p.z), &acc, p.is_zero()); - } - - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { - let skip = p.is_zero(); - - // Compute tmp = 1/z - let tmp = q.x * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc = $base::conditional_select(&(acc * p.z), &acc, skip); - - // Set the coordinates to the correct value - let tmp2 = tmp.square(); - let tmp3 = tmp2 * tmp; - - q.x = p.x * tmp2; - q.y = p.y * tmp3; - q.infinity = Choice::from(0u8); - - *q = $name_affine::conditional_select(&q, &$name_affine::zero(), skip); - } - } - } - - impl<'a> From<&'a $name_affine> for $name { - fn from(p: &'a $name_affine) -> $name { - p.to_projective() - } - } - - impl From<$name_affine> for $name { - fn from(p: $name_affine) -> $name { - p.to_projective() - } - } - - impl Default for $name { - fn default() -> $name { - $name::zero() - } - } - - impl ConstantTimeEq for $name { - fn ct_eq(&self, other: &Self) -> Choice { - // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? - - let z = other.z.square(); - let x1 = self.x * z; - let z = z * other.z; - let y1 = self.y * z; - let z = self.z.square(); - let x2 = other.x * z; - let z = z * self.z; - let y2 = other.y * z; - - let self_is_zero = self.is_zero(); - let other_is_zero = other.is_zero(); - - (self_is_zero & other_is_zero) // Both point at infinity - | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) - // Neither point at infinity, coordinates are the same - } - } - - impl PartialEq for $name { - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).into() - } - } - - impl cmp::Eq for $name {} - - impl ConditionallySelectable for $name { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - $name { - x: $base::conditional_select(&a.x, &b.x, choice), - y: $base::conditional_select(&a.y, &b.y, choice), - z: $base::conditional_select(&a.z, &b.z, choice), - } - } - } - - impl<'a> Neg for &'a $name { - type Output = $name; - - fn neg(self) -> $name { - $name { - x: self.x, - y: -self.y, - z: self.z, - } - } - } - - impl Neg for $name { - type Output = $name; - - fn neg(self) -> $name { - -&self - } - } - - impl<'a, 'b> Add<&'a $name> for &'b $name { - type Output = $name; - - fn add(self, rhs: &'a $name) -> $name { - if bool::from(self.is_zero()) { - *rhs - } else if bool::from(rhs.is_zero()) { - *self - } else { - let z1z1 = self.z.square(); - let z2z2 = rhs.z.square(); - let u1 = self.x * z2z2; - let u2 = rhs.x * z1z1; - let s1 = self.y * z2z2 * rhs.z; - let s2 = rhs.y * z1z1 * self.z; - - if u1 == u2 { - if s1 == s2 { - self.double() - } else { - $name::zero() - } - } else { - let h = u2 - u1; - let i = (h + h).square(); - let j = h * i; - let r = s2 - s1; - let r = r + r; - let v = u1 * i; - let x3 = r.square() - j - v - v; - let s1 = s1 * j; - let s1 = s1 + s1; - let y3 = r * (v - x3) - s1; - let z3 = (self.z + rhs.z).square() - z1z1 - z2z2; - let z3 = z3 * h; - - $name { - x: x3, y: y3, z: z3 - } - } - } - } - } - - impl<'a, 'b> Add<&'a $name_affine> for &'b $name { - type Output = $name; - - fn add(self, rhs: &'a $name_affine) -> $name { - if bool::from(self.is_zero()) { - rhs.to_projective() - } else if bool::from(rhs.is_zero()) { - *self - } else { - let z1z1 = self.z.square(); - let u2 = rhs.x * z1z1; - let s2 = rhs.y * z1z1 * self.z; - - if self.x == u2 { - if self.y == s2 { - self.double() - } else { - $name::zero() - } - } else { - let h = u2 - self.x; - let hh = h.square(); - let i = hh + hh; - let i = i + i; - let j = h * i; - let r = s2 - self.y; - let r = r + r; - let v = self.x * i; - let x3 = r.square() - j - v - v; - let j = self.y * j; - let j = j + j; - let y3 = r * (v - x3) - j; - let z3 = (self.z + h).square() - z1z1 - hh; - - $name { - x: x3, y: y3, z: z3 - } - } - } - } - } - - impl<'a, 'b> Sub<&'a $name> for &'b $name { - type Output = $name; - - fn sub(self, other: &'a $name) -> $name { - self + (-other) - } - } - - impl<'a, 'b> Sub<&'a $name_affine> for &'b $name { - type Output = $name; - - fn sub(self, other: &'a $name_affine) -> $name { - self + (-other) - } - } - - impl<'a, 'b> Mul<&'b $scalar> for &'a $name { - type Output = $name; - - fn mul(self, other: &'b $scalar) -> Self::Output { - // TODO: make this faster - - let mut acc = $name::zero(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // NOTE: We skip the leading bit because it's always unset. - for bit in other - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = $name::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - } - - impl<'a> Neg for &'a $name_affine { - type Output = $name_affine; - - fn neg(self) -> $name_affine { - $name_affine { - x: self.x, - y: -self.y, - infinity: self.infinity, - } - } - } - - impl Neg for $name_affine { - type Output = $name_affine; - - fn neg(self) -> $name_affine { - -&self - } - } - - impl<'a, 'b> Add<&'a $name> for &'b $name_affine { - type Output = $name; - - fn add(self, rhs: &'a $name) -> $name { - rhs + self - } - } - - impl<'a, 'b> Add<&'a $name_affine> for &'b $name_affine { - type Output = $name; - - fn add(self, rhs: &'a $name_affine) -> $name { - if bool::from(self.is_zero()) { - rhs.to_projective() - } else if bool::from(rhs.is_zero()) { - self.to_projective() - } else { - if self.x == rhs.x { - if self.y == rhs.y { - self.to_projective().double() - } else { - $name::zero() - } - } else { - let h = rhs.x - self.x; - let hh = h.square(); - let i = hh + hh; - let i = i + i; - let j = h * i; - let r = rhs.y - self.y; - let r = r + r; - let v = self.x * i; - let x3 = r.square() - j - v - v; - let j = self.y * j; - let j = j + j; - let y3 = r * (v - x3) - j; - let z3 = h + h; - - $name { - x: x3, y: y3, z: z3 - } - } - } - } - } - - impl<'a, 'b> Sub<&'a $name_affine> for &'b $name_affine { - type Output = $name; - - fn sub(self, other: &'a $name_affine) -> $name { - self + (-other) - } - } - - impl<'a, 'b> Sub<&'a $name> for &'b $name_affine { - type Output = $name; - - fn sub(self, other: &'a $name) -> $name { - self + (-other) - } - } - - impl<'a, 'b> Mul<&'b $scalar> for &'a $name_affine { - type Output = $name; - - fn mul(self, other: &'b $scalar) -> Self::Output { - // TODO: make this faster - - let mut acc = $name::zero(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // NOTE: We skip the leading bit because it's always unset. - for bit in other - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = $name::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - } - - impl CurveAffine for $name_affine { - type Projective = $name; - type Scalar = $scalar; - type Base = $base; - - fn zero() -> Self { - Self { - x: $base::zero(), - y: $base::zero(), - infinity: Choice::from(1u8), - } - } - - fn one() -> Self { - // NOTE: This is specific to b = 5 - - const NEGATIVE_ONE: $base = $base::neg(&$base::from_raw([1, 0, 0, 0])); - const TWO: $base = $base::from_raw([2, 0, 0, 0]); - - Self { - x: NEGATIVE_ONE, - y: TWO, - infinity: Choice::from(0u8), - } - } - - fn is_zero(&self) -> Choice { - self.infinity - } - - fn is_on_curve(&self) -> Choice { - // y^2 - x^3 ?= b - (self.y.square() - (self.x.square() * self.x)).ct_eq(&$name::curve_constant_b()) - | self.infinity - } - - fn to_projective(&self) -> Self::Projective { - $name { - x: self.x, - y: self.y, - z: $base::conditional_select(&$base::one(), &$base::zero(), self.infinity), - } - } - - fn get_xy(&self) -> CtOption<(Self::Base, Self::Base)> { - CtOption::new((self.x, self.y), !self.is_zero()) - } - - fn from_xy(x: Self::Base, y: Self::Base) -> CtOption { - let p = $name_affine { - x, y, infinity: 0u8.into() - }; - CtOption::new(p, p.is_on_curve()) - } - - fn from_bytes(bytes: &[u8; 32]) -> CtOption { - let mut tmp = *bytes; - let ysign = Choice::from(tmp[31] >> 7); - tmp[31] &= 0b0111_1111; - - $base::from_bytes(&tmp).and_then(|x| { - CtOption::new(Self::zero(), x.is_zero() & (!ysign)).or_else(|| { - let x3 = x.square() * x; - (x3 + $name::curve_constant_b()).sqrt().and_then(|y| { - let sign = Choice::from(y.to_bytes()[0] & 1); - - let y = $base::conditional_select(&y, &-y, ysign ^ sign); - - CtOption::new( - $name_affine { - x, - y, - infinity: Choice::from(0u8), - }, - Choice::from(1u8), - ) - }) - }) - }) - } - - fn to_bytes(&self) -> [u8; 32] { - // TODO: not constant time - if bool::from(self.is_zero()) { - [0; 32] - } else { - let (x, y) = (self.x, self.y); - let sign = (y.to_bytes()[0] & 1) << 7; - let mut xbytes = x.to_bytes(); - xbytes[31] |= sign; - xbytes - } - } - - fn from_bytes_wide(bytes: &[u8; 64]) -> CtOption { - let mut xbytes = [0u8; 32]; - let mut ybytes = [0u8; 32]; - xbytes.copy_from_slice(&bytes[0..32]); - ybytes.copy_from_slice(&bytes[32..64]); - - $base::from_bytes(&xbytes).and_then(|x| { - $base::from_bytes(&ybytes).and_then(|y| { - CtOption::new(Self::zero(), x.is_zero() & y.is_zero()).or_else(|| { - let on_curve = - (x * x.square() + $name::curve_constant_b()).ct_eq(&y.square()); - - CtOption::new( - $name_affine { - x, - y, - infinity: Choice::from(0u8), - }, - Choice::from(on_curve), - ) - }) - }) - }) - } - - fn to_bytes_wide(&self) -> [u8; 64] { - // TODO: not constant time - if bool::from(self.is_zero()) { - [0; 64] - } else { - let mut out = [0u8; 64]; - (&mut out[0..32]).copy_from_slice(&self.x.to_bytes()); - (&mut out[32..64]).copy_from_slice(&self.y.to_bytes()); - - out - } - } - - fn b() -> Self::Base { - $name::curve_constant_b() - } - } - - impl Default for $name_affine { - fn default() -> $name_affine { - $name_affine::zero() - } - } - - impl<'a> From<&'a $name> for $name_affine { - fn from(p: &'a $name) -> $name_affine { - p.to_affine() - } - } - - impl From<$name> for $name_affine { - fn from(p: $name) -> $name_affine { - p.to_affine() - } - } - - impl ConstantTimeEq for $name_affine { - fn ct_eq(&self, other: &Self) -> Choice { - let z1 = self.infinity; - let z2 = other.infinity; - - (z1 & z2) | ((!z1) & (!z2) & (self.x.ct_eq(&other.x)) & (self.y.ct_eq(&other.y))) - } - } - - impl PartialEq for $name_affine { - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).into() - } - } - - impl cmp::Eq for $name_affine {} - - impl ConditionallySelectable for $name_affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - $name_affine { - x: $base::conditional_select(&a.x, &b.x, choice), - y: $base::conditional_select(&a.y, &b.y, choice), - infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), - } - } - } - - impl_binops_additive!($name, $name); - impl_binops_additive!($name, $name_affine); - impl_binops_additive_specify_output!($name_affine, $name_affine, $name); - impl_binops_additive_specify_output!($name_affine, $name, $name); - impl_binops_multiplicative!($name, $scalar); - impl_binops_multiplicative_mixed!($name_affine, $scalar, $name); - - impl Group for $name { - type Scalar = $scalar; - - fn group_zero() -> Self { - Self::zero() - } - fn group_add(&mut self, rhs: &Self) { - *self = *self + *rhs; - } - fn group_sub(&mut self, rhs: &Self) { - *self = *self - *rhs; - } - fn group_scale(&mut self, by: &Self::Scalar) { - *self = *self * (*by); - } - } - }; -} - -new_curve_impl!(Ep, EpAffine, Fp, Fq); -new_curve_impl!(Eq, EqAffine, Fq, Fp); diff --git a/src/arithmetic/fields.rs b/src/arithmetic/fields.rs index 67c30438..d0fff80f 100644 --- a/src/arithmetic/fields.rs +++ b/src/arithmetic/fields.rs @@ -1,6 +1,5 @@ -//! This module contains implementations for the two finite fields of the -//! Tweedledum and Tweedledee curves. The `Field` abstraction allows us to write -//! code that generalizes over these two fields. +//! This module contains the `Field` abstraction that allows us to write +//! code that generalizes over a pair of fields. use std::fmt::Debug; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; @@ -261,41 +260,23 @@ pub trait Field: } } -mod fp; -mod fq; - /// Compute a + b + carry, returning the result and the new carry over. #[inline(always)] -const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { +pub(crate) const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { let ret = (a as u128) + (b as u128) + (carry as u128); (ret as u64, (ret >> 64) as u64) } /// Compute a - (b + borrow), returning the result and the new borrow. #[inline(always)] -const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { +pub(crate) const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); (ret as u64, (ret >> 64) as u64) } /// Compute a + (b * c) + carry, returning the result and the new carry over. #[inline(always)] -const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { +pub(crate) const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); (ret as u64, (ret >> 64) as u64) } - -pub use fp::*; -pub use fq::*; - -#[test] -fn test_extract() { - let a = Fq::random(); - let a = a.square(); - let (t, s) = a.extract_radix2_vartime().unwrap(); - assert_eq!( - t.pow_vartime(&[1 << Fq::S, 0, 0, 0]) * Fq::ROOT_OF_UNITY.pow_vartime(&[s, 0, 0, 0]), - a - ); - assert_eq!(a.deterministic_sqrt().unwrap().square(), a); -} diff --git a/src/lib.rs b/src/lib.rs index ede9adc4..49441e8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,3 +17,4 @@ pub mod arithmetic; pub mod plonk; pub mod poly; pub mod transcript; +pub mod tweedle; diff --git a/src/plonk.rs b/src/plonk.rs index 6a457d4f..7ce0de3a 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -111,9 +111,10 @@ pub fn hash_point>( #[test] fn test_proving() { - use crate::arithmetic::{Curve, EqAffine, Field, Fp, Fq}; + use crate::arithmetic::{Curve, Field}; use crate::poly::commitment::{Blind, Params}; use crate::transcript::DummyHash; + use crate::tweedle::{EqAffine, Fp, Fq}; use circuit::{Advice, Column, Fixed}; use std::marker::PhantomData; const K: u32 = 5; diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 15e089e3..c135ad0c 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -222,8 +222,8 @@ impl MulAssign for Blind { fn test_commit_lagrange() { const K: u32 = 6; - use crate::arithmetic::{EpAffine, Fp, Fq}; use crate::transcript::DummyHash; + use crate::tweedle::{EpAffine, Fp, Fq}; let params = Params::::new::>(K); let domain = super::EvaluationDomain::new(1, K); @@ -248,10 +248,9 @@ fn test_opening_proof() { commitment::{Blind, Params}, EvaluationDomain, }; - use crate::arithmetic::{ - eval_polynomial, get_challenge_scalar, Challenge, Curve, EpAffine, Field, Fp, Fq, - }; + use crate::arithmetic::{eval_polynomial, get_challenge_scalar, Challenge, Curve, Field}; use crate::transcript::{DummyHash, Hasher}; + use crate::tweedle::{EpAffine, Fp, Fq}; let params = Params::::new::>(K); let domain = EvaluationDomain::new(1, K); diff --git a/src/poly/multiopen.rs b/src/poly/multiopen.rs index 844157c0..e10a240a 100644 --- a/src/poly/multiopen.rs +++ b/src/poly/multiopen.rs @@ -191,7 +191,8 @@ where #[cfg(test)] mod tests { use super::{construct_intermediate_sets, Query}; - use crate::arithmetic::{Field, Fp}; + use crate::arithmetic::Field; + use crate::tweedle::Fp; #[derive(Clone)] struct MyQuery { diff --git a/src/tweedle.rs b/src/tweedle.rs new file mode 100644 index 00000000..f8f9be9c --- /dev/null +++ b/src/tweedle.rs @@ -0,0 +1,10 @@ +//! This module contains implementations for the Tweedledum and Tweedledee +//! elliptic curve groups. + +#[macro_use] +mod macros; +mod curves; +mod fields; + +pub use curves::*; +pub use fields::*; diff --git a/src/tweedle/curves.rs b/src/tweedle/curves.rs new file mode 100644 index 00000000..646bd1fd --- /dev/null +++ b/src/tweedle/curves.rs @@ -0,0 +1,703 @@ +//! This module contains implementations for the Tweedledum and Tweedledee +//! elliptic curve groups. + +use core::cmp; +use core::fmt::Debug; +use core::ops::{Add, Mul, Neg, Sub}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use super::{Fp, Fq}; +use crate::arithmetic::{Curve, CurveAffine, Field, Group}; + +macro_rules! new_curve_impl { + ($name:ident, $name_affine:ident, $base:ident, $scalar:ident) => { + /// Represents a point in the projective coordinate space. + #[derive(Copy, Clone, Debug)] + pub struct $name { + x: $base, + y: $base, + z: $base, + } + + impl $name { + const fn curve_constant_b() -> $base { + // NOTE: this is specific to b = 5 + $base::from_raw([5, 0, 0, 0]) + } + } + + /// Represents a point in the affine coordinate space (or the point at + /// infinity). + #[derive(Copy, Clone, Debug)] + pub struct $name_affine { + x: $base, + y: $base, + infinity: Choice, + } + + impl Curve for $name { + type Affine = $name_affine; + type Scalar = $scalar; + type Base = $base; + + fn zero() -> Self { + Self { + x: $base::zero(), + y: $base::zero(), + z: $base::zero(), + } + } + + fn one() -> Self { + // NOTE: This is specific to b = 5 + + const NEGATIVE_ONE: $base = $base::neg(&$base::one()); + const TWO: $base = $base::from_raw([2, 0, 0, 0]); + + Self { + x: NEGATIVE_ONE, + y: TWO, + z: $base::one(), + } + } + + fn is_zero(&self) -> Choice { + self.z.is_zero() + } + + fn to_affine(&self) -> Self::Affine { + let zinv = self.z.invert().unwrap_or($base::zero()); + let zinv2 = zinv.square(); + let x = self.x * zinv2; + let zinv3 = zinv2 * zinv; + let y = self.y * zinv3; + + let tmp = $name_affine { + x, + y, + infinity: Choice::from(0u8), + }; + + $name_affine::conditional_select(&tmp, &$name_affine::zero(), zinv.is_zero()) + } + + fn double(&self) -> Self { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // + // There are no points of order 2. + + let a = self.x.square(); + let b = self.y.square(); + let c = b.square(); + let d = self.x + b; + let d = d.square(); + let d = d - a - c; + let d = d + d; + let e = a + a + a; + let f = e.square(); + let z3 = self.z * self.y; + let z3 = z3 + z3; + let x3 = f - (d + d); + let c = c + c; + let c = c + c; + let c = c + c; + let y3 = e * (d - x3) - c; + + let tmp = $name { + x: x3, + y: y3, + z: z3, + }; + + $name::conditional_select(&tmp, &$name::zero(), self.is_zero()) + } + + /// Apply the curve endomorphism by multiplying the x-coordinate + /// by an element of multiplicative order 3. + fn endo(&self) -> Self { + $name { + x: self.x * $base::ZETA, + y: self.y, + z: self.z, + } + } + + fn b() -> Self::Base { + $name::curve_constant_b() + } + + fn is_on_curve(&self) -> Choice { + // Y^2 - X^3 = 5(Z^6) + + (self.y.square() - (self.x.square() * self.x)) + .ct_eq(&((self.z.square() * self.z).square() * $name::curve_constant_b())) + | self.z.is_zero() + } + + fn batch_to_affine(p: &[Self], q: &mut [Self::Affine]) { + assert_eq!(p.len(), q.len()); + + let mut acc = $base::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of $name_affine to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + acc = $base::conditional_select(&(acc * p.z), &acc, p.is_zero()); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_zero(); + + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = $base::conditional_select(&(acc * p.z), &acc, skip); + + // Set the coordinates to the correct value + let tmp2 = tmp.square(); + let tmp3 = tmp2 * tmp; + + q.x = p.x * tmp2; + q.y = p.y * tmp3; + q.infinity = Choice::from(0u8); + + *q = $name_affine::conditional_select(&q, &$name_affine::zero(), skip); + } + } + } + + impl<'a> From<&'a $name_affine> for $name { + fn from(p: &'a $name_affine) -> $name { + p.to_projective() + } + } + + impl From<$name_affine> for $name { + fn from(p: $name_affine) -> $name { + p.to_projective() + } + } + + impl Default for $name { + fn default() -> $name { + $name::zero() + } + } + + impl ConstantTimeEq for $name { + fn ct_eq(&self, other: &Self) -> Choice { + // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? + + let z = other.z.square(); + let x1 = self.x * z; + let z = z * other.z; + let y1 = self.y * z; + let z = self.z.square(); + let x2 = other.x * z; + let z = z * self.z; + let y2 = other.y * z; + + let self_is_zero = self.is_zero(); + let other_is_zero = other.is_zero(); + + (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) + // Neither point at infinity, coordinates are the same + } + } + + impl PartialEq for $name { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } + } + + impl cmp::Eq for $name {} + + impl ConditionallySelectable for $name { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + $name { + x: $base::conditional_select(&a.x, &b.x, choice), + y: $base::conditional_select(&a.y, &b.y, choice), + z: $base::conditional_select(&a.z, &b.z, choice), + } + } + } + + impl<'a> Neg for &'a $name { + type Output = $name; + + fn neg(self) -> $name { + $name { + x: self.x, + y: -self.y, + z: self.z, + } + } + } + + impl Neg for $name { + type Output = $name; + + fn neg(self) -> $name { + -&self + } + } + + impl<'a, 'b> Add<&'a $name> for &'b $name { + type Output = $name; + + fn add(self, rhs: &'a $name) -> $name { + if bool::from(self.is_zero()) { + *rhs + } else if bool::from(rhs.is_zero()) { + *self + } else { + let z1z1 = self.z.square(); + let z2z2 = rhs.z.square(); + let u1 = self.x * z2z2; + let u2 = rhs.x * z1z1; + let s1 = self.y * z2z2 * rhs.z; + let s2 = rhs.y * z1z1 * self.z; + + if u1 == u2 { + if s1 == s2 { + self.double() + } else { + $name::zero() + } + } else { + let h = u2 - u1; + let i = (h + h).square(); + let j = h * i; + let r = s2 - s1; + let r = r + r; + let v = u1 * i; + let x3 = r.square() - j - v - v; + let s1 = s1 * j; + let s1 = s1 + s1; + let y3 = r * (v - x3) - s1; + let z3 = (self.z + rhs.z).square() - z1z1 - z2z2; + let z3 = z3 * h; + + $name { + x: x3, y: y3, z: z3 + } + } + } + } + } + + impl<'a, 'b> Add<&'a $name_affine> for &'b $name { + type Output = $name; + + fn add(self, rhs: &'a $name_affine) -> $name { + if bool::from(self.is_zero()) { + rhs.to_projective() + } else if bool::from(rhs.is_zero()) { + *self + } else { + let z1z1 = self.z.square(); + let u2 = rhs.x * z1z1; + let s2 = rhs.y * z1z1 * self.z; + + if self.x == u2 { + if self.y == s2 { + self.double() + } else { + $name::zero() + } + } else { + let h = u2 - self.x; + let hh = h.square(); + let i = hh + hh; + let i = i + i; + let j = h * i; + let r = s2 - self.y; + let r = r + r; + let v = self.x * i; + let x3 = r.square() - j - v - v; + let j = self.y * j; + let j = j + j; + let y3 = r * (v - x3) - j; + let z3 = (self.z + h).square() - z1z1 - hh; + + $name { + x: x3, y: y3, z: z3 + } + } + } + } + } + + impl<'a, 'b> Sub<&'a $name> for &'b $name { + type Output = $name; + + fn sub(self, other: &'a $name) -> $name { + self + (-other) + } + } + + impl<'a, 'b> Sub<&'a $name_affine> for &'b $name { + type Output = $name; + + fn sub(self, other: &'a $name_affine) -> $name { + self + (-other) + } + } + + impl<'a, 'b> Mul<&'b $scalar> for &'a $name { + type Output = $name; + + fn mul(self, other: &'b $scalar) -> Self::Output { + // TODO: make this faster + + let mut acc = $name::zero(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // NOTE: We skip the leading bit because it's always unset. + for bit in other + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = $name::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + } + + impl<'a> Neg for &'a $name_affine { + type Output = $name_affine; + + fn neg(self) -> $name_affine { + $name_affine { + x: self.x, + y: -self.y, + infinity: self.infinity, + } + } + } + + impl Neg for $name_affine { + type Output = $name_affine; + + fn neg(self) -> $name_affine { + -&self + } + } + + impl<'a, 'b> Add<&'a $name> for &'b $name_affine { + type Output = $name; + + fn add(self, rhs: &'a $name) -> $name { + rhs + self + } + } + + impl<'a, 'b> Add<&'a $name_affine> for &'b $name_affine { + type Output = $name; + + fn add(self, rhs: &'a $name_affine) -> $name { + if bool::from(self.is_zero()) { + rhs.to_projective() + } else if bool::from(rhs.is_zero()) { + self.to_projective() + } else { + if self.x == rhs.x { + if self.y == rhs.y { + self.to_projective().double() + } else { + $name::zero() + } + } else { + let h = rhs.x - self.x; + let hh = h.square(); + let i = hh + hh; + let i = i + i; + let j = h * i; + let r = rhs.y - self.y; + let r = r + r; + let v = self.x * i; + let x3 = r.square() - j - v - v; + let j = self.y * j; + let j = j + j; + let y3 = r * (v - x3) - j; + let z3 = h + h; + + $name { + x: x3, y: y3, z: z3 + } + } + } + } + } + + impl<'a, 'b> Sub<&'a $name_affine> for &'b $name_affine { + type Output = $name; + + fn sub(self, other: &'a $name_affine) -> $name { + self + (-other) + } + } + + impl<'a, 'b> Sub<&'a $name> for &'b $name_affine { + type Output = $name; + + fn sub(self, other: &'a $name) -> $name { + self + (-other) + } + } + + impl<'a, 'b> Mul<&'b $scalar> for &'a $name_affine { + type Output = $name; + + fn mul(self, other: &'b $scalar) -> Self::Output { + // TODO: make this faster + + let mut acc = $name::zero(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // NOTE: We skip the leading bit because it's always unset. + for bit in other + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = $name::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + } + + impl CurveAffine for $name_affine { + type Projective = $name; + type Scalar = $scalar; + type Base = $base; + + fn zero() -> Self { + Self { + x: $base::zero(), + y: $base::zero(), + infinity: Choice::from(1u8), + } + } + + fn one() -> Self { + // NOTE: This is specific to b = 5 + + const NEGATIVE_ONE: $base = $base::neg(&$base::from_raw([1, 0, 0, 0])); + const TWO: $base = $base::from_raw([2, 0, 0, 0]); + + Self { + x: NEGATIVE_ONE, + y: TWO, + infinity: Choice::from(0u8), + } + } + + fn is_zero(&self) -> Choice { + self.infinity + } + + fn is_on_curve(&self) -> Choice { + // y^2 - x^3 ?= b + (self.y.square() - (self.x.square() * self.x)).ct_eq(&$name::curve_constant_b()) + | self.infinity + } + + fn to_projective(&self) -> Self::Projective { + $name { + x: self.x, + y: self.y, + z: $base::conditional_select(&$base::one(), &$base::zero(), self.infinity), + } + } + + fn get_xy(&self) -> CtOption<(Self::Base, Self::Base)> { + CtOption::new((self.x, self.y), !self.is_zero()) + } + + fn from_xy(x: Self::Base, y: Self::Base) -> CtOption { + let p = $name_affine { + x, y, infinity: 0u8.into() + }; + CtOption::new(p, p.is_on_curve()) + } + + fn from_bytes(bytes: &[u8; 32]) -> CtOption { + let mut tmp = *bytes; + let ysign = Choice::from(tmp[31] >> 7); + tmp[31] &= 0b0111_1111; + + $base::from_bytes(&tmp).and_then(|x| { + CtOption::new(Self::zero(), x.is_zero() & (!ysign)).or_else(|| { + let x3 = x.square() * x; + (x3 + $name::curve_constant_b()).sqrt().and_then(|y| { + let sign = Choice::from(y.to_bytes()[0] & 1); + + let y = $base::conditional_select(&y, &-y, ysign ^ sign); + + CtOption::new( + $name_affine { + x, + y, + infinity: Choice::from(0u8), + }, + Choice::from(1u8), + ) + }) + }) + }) + } + + fn to_bytes(&self) -> [u8; 32] { + // TODO: not constant time + if bool::from(self.is_zero()) { + [0; 32] + } else { + let (x, y) = (self.x, self.y); + let sign = (y.to_bytes()[0] & 1) << 7; + let mut xbytes = x.to_bytes(); + xbytes[31] |= sign; + xbytes + } + } + + fn from_bytes_wide(bytes: &[u8; 64]) -> CtOption { + let mut xbytes = [0u8; 32]; + let mut ybytes = [0u8; 32]; + xbytes.copy_from_slice(&bytes[0..32]); + ybytes.copy_from_slice(&bytes[32..64]); + + $base::from_bytes(&xbytes).and_then(|x| { + $base::from_bytes(&ybytes).and_then(|y| { + CtOption::new(Self::zero(), x.is_zero() & y.is_zero()).or_else(|| { + let on_curve = + (x * x.square() + $name::curve_constant_b()).ct_eq(&y.square()); + + CtOption::new( + $name_affine { + x, + y, + infinity: Choice::from(0u8), + }, + Choice::from(on_curve), + ) + }) + }) + }) + } + + fn to_bytes_wide(&self) -> [u8; 64] { + // TODO: not constant time + if bool::from(self.is_zero()) { + [0; 64] + } else { + let mut out = [0u8; 64]; + (&mut out[0..32]).copy_from_slice(&self.x.to_bytes()); + (&mut out[32..64]).copy_from_slice(&self.y.to_bytes()); + + out + } + } + + fn b() -> Self::Base { + $name::curve_constant_b() + } + } + + impl Default for $name_affine { + fn default() -> $name_affine { + $name_affine::zero() + } + } + + impl<'a> From<&'a $name> for $name_affine { + fn from(p: &'a $name) -> $name_affine { + p.to_affine() + } + } + + impl From<$name> for $name_affine { + fn from(p: $name) -> $name_affine { + p.to_affine() + } + } + + impl ConstantTimeEq for $name_affine { + fn ct_eq(&self, other: &Self) -> Choice { + let z1 = self.infinity; + let z2 = other.infinity; + + (z1 & z2) | ((!z1) & (!z2) & (self.x.ct_eq(&other.x)) & (self.y.ct_eq(&other.y))) + } + } + + impl PartialEq for $name_affine { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } + } + + impl cmp::Eq for $name_affine {} + + impl ConditionallySelectable for $name_affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + $name_affine { + x: $base::conditional_select(&a.x, &b.x, choice), + y: $base::conditional_select(&a.y, &b.y, choice), + infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), + } + } + } + + impl_binops_additive!($name, $name); + impl_binops_additive!($name, $name_affine); + impl_binops_additive_specify_output!($name_affine, $name_affine, $name); + impl_binops_additive_specify_output!($name_affine, $name, $name); + impl_binops_multiplicative!($name, $scalar); + impl_binops_multiplicative_mixed!($name_affine, $scalar, $name); + + impl Group for $name { + type Scalar = $scalar; + + fn group_zero() -> Self { + Self::zero() + } + fn group_add(&mut self, rhs: &Self) { + *self = *self + *rhs; + } + fn group_sub(&mut self, rhs: &Self) { + *self = *self - *rhs; + } + fn group_scale(&mut self, by: &Self::Scalar) { + *self = *self * (*by); + } + } + }; +} + +new_curve_impl!(Ep, EpAffine, Fp, Fq); +new_curve_impl!(Eq, EqAffine, Fq, Fp); diff --git a/src/tweedle/fields.rs b/src/tweedle/fields.rs new file mode 100644 index 00000000..03f15b39 --- /dev/null +++ b/src/tweedle/fields.rs @@ -0,0 +1,23 @@ +//! This module contains implementations for the two finite fields of the +//! Tweedledum and Tweedledee curves. + +mod fp; +mod fq; + +pub use fp::*; +pub use fq::*; + +#[cfg(test)] +use crate::arithmetic::Field; + +#[test] +fn test_extract() { + let a = Fq::random(); + let a = a.square(); + let (t, s) = a.extract_radix2_vartime().unwrap(); + assert_eq!( + t.pow_vartime(&[1 << Fq::S, 0, 0, 0]) * Fq::ROOT_OF_UNITY.pow_vartime(&[s, 0, 0, 0]), + a + ); + assert_eq!(a.deterministic_sqrt().unwrap().square(), a); +} diff --git a/src/arithmetic/fields/fp.rs b/src/tweedle/fields/fp.rs similarity index 99% rename from src/arithmetic/fields/fp.rs rename to src/tweedle/fields/fp.rs index ad74f669..5de4df9e 100644 --- a/src/arithmetic/fields/fp.rs +++ b/src/tweedle/fields/fp.rs @@ -1,12 +1,10 @@ -use super::{Field, Group}; - use core::convert::TryInto; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -use super::{adc, mac, sbb}; +use crate::arithmetic::{adc, mac, sbb, Field, Group}; /// This represents an element of $\mathbb{F}_p$ where /// diff --git a/src/arithmetic/fields/fq.rs b/src/tweedle/fields/fq.rs similarity index 99% rename from src/arithmetic/fields/fq.rs rename to src/tweedle/fields/fq.rs index 25ec53a2..6d1fdc52 100644 --- a/src/arithmetic/fields/fq.rs +++ b/src/tweedle/fields/fq.rs @@ -1,12 +1,10 @@ -use super::{Field, Group}; - use core::convert::TryInto; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -use super::{adc, mac, sbb}; +use crate::arithmetic::{adc, mac, sbb, Field, Group}; /// This represents an element of $\mathbb{F}_q$ where /// diff --git a/src/arithmetic/macros.rs b/src/tweedle/macros.rs similarity index 100% rename from src/arithmetic/macros.rs rename to src/tweedle/macros.rs