-
Notifications
You must be signed in to change notification settings - Fork 59
feat: twisted edwards curves #633
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
base: main
Are you sure you want to change the base?
Changes from all commits
d5eb5c3
fe5f9e8
3a45cf1
dd870a2
277f8ba
57c294e
fabe9c4
511f3b3
bbcb612
fcd9506
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
//! Affine coordinates for a point on a Twisted Edwards curve ([Affine Space]). | ||
//! | ||
//! [Affine Space]: https://en.wikipedia.org/wiki/Affine_space | ||
use core::{ | ||
borrow::Borrow, | ||
fmt::{Debug, Display, Formatter}, | ||
ops::{Add, Mul, Neg, Sub}, | ||
}; | ||
|
||
use educe::Educe; | ||
use num_traits::{One, Zero}; | ||
use zeroize::Zeroize; | ||
|
||
use super::{Projective, TECurveConfig}; | ||
use crate::{ | ||
bits::BitIteratorBE, | ||
curve::AffineRepr, | ||
field::{group::AdditiveGroup, prime::PrimeField, Field}, | ||
}; | ||
|
||
/// Affine coordinates for a point on a twisted Edwards curve, over the | ||
/// base field `P::BaseField`. | ||
#[derive(Educe)] | ||
#[educe(Copy, Clone, PartialEq, Eq, Hash)] | ||
#[must_use] | ||
pub struct Affine<P: TECurveConfig> { | ||
/// X coordinate of the point represented as a field element | ||
pub x: P::BaseField, | ||
/// Y coordinate of the point represented as a field element | ||
pub y: P::BaseField, | ||
} | ||
|
||
impl<P: TECurveConfig> Display for Affine<P> { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { | ||
if self.is_zero() { | ||
write!(f, "infinity") | ||
} else { | ||
write!(f, "({}, {})", self.x, self.y) | ||
} | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> Debug for Affine<P> { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { | ||
if self.is_zero() { | ||
write!(f, "infinity") | ||
} else { | ||
write!(f, "({}, {})", self.x, self.y) | ||
} | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> PartialEq<Projective<P>> for Affine<P> { | ||
fn eq(&self, other: &Projective<P>) -> bool { | ||
&self.into_group() == other | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> Affine<P> { | ||
/// Construct a new group element without checking whether the coordinates | ||
/// specify a point in the subgroup. | ||
pub const fn new_unchecked(x: P::BaseField, y: P::BaseField) -> Self { | ||
Self { x, y } | ||
} | ||
|
||
/// Construct a new group element in a way while enforcing that points are | ||
/// in the prime-order subgroup. | ||
/// | ||
/// # Panics | ||
/// | ||
/// * If point is not on curve. | ||
/// * If point is not in the prime-order subgroup. | ||
pub fn new(x: P::BaseField, y: P::BaseField) -> Self { | ||
let p = Self::new_unchecked(x, y); | ||
assert!(p.is_on_curve()); | ||
assert!(p.is_in_correct_subgroup_assuming_on_curve()); | ||
p | ||
} | ||
|
||
/// Construct the identity of the group. | ||
pub const fn zero() -> Self { | ||
Self::new_unchecked(P::BaseField::ZERO, P::BaseField::ONE) | ||
} | ||
|
||
/// Is this point the identity? | ||
pub fn is_zero(&self) -> bool { | ||
self.x.is_zero() && self.y.is_one() | ||
} | ||
|
||
/// Checks that the current point is on the elliptic curve. | ||
pub fn is_on_curve(&self) -> bool { | ||
let x2 = self.x.square(); | ||
let y2 = self.y.square(); | ||
|
||
let lhs = y2 + P::mul_by_a(x2); | ||
let rhs = P::BaseField::one() + (P::COEFF_D * (x2 * y2)); | ||
|
||
lhs == rhs | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> Affine<P> { | ||
/// Checks if this point is in the prime-order subgroup. | ||
/// | ||
/// This assumes the point is already on the curve and verifies it belongs | ||
/// to the subgroup with order equal to `P::ScalarField`. | ||
pub fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { | ||
P::is_in_correct_subgroup_assuming_on_curve(self) | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> AffineRepr for Affine<P> { | ||
type BaseField = P::BaseField; | ||
type Config = P; | ||
type Group = Projective<P>; | ||
type ScalarField = P::ScalarField; | ||
|
||
fn xy(&self) -> Option<(Self::BaseField, Self::BaseField)> { | ||
(!self.is_zero()).then_some((self.x, self.y)) | ||
} | ||
Comment on lines
+118
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you think it's better to return |
||
|
||
fn generator() -> Self { | ||
P::GENERATOR | ||
} | ||
|
||
fn zero() -> Self { | ||
Self::new_unchecked(P::BaseField::ZERO, P::BaseField::ONE) | ||
} | ||
|
||
fn mul_bigint(&self, by: impl BitIteratorBE) -> Self::Group { | ||
P::mul_affine(self, by) | ||
} | ||
|
||
/// Multiplies this element by the cofactor and output the | ||
/// resulting projective element. | ||
fn mul_by_cofactor_to_group(&self) -> Self::Group { | ||
P::mul_affine(self, Self::Config::COFACTOR) | ||
} | ||
|
||
/// Performs cofactor clearing. | ||
/// The default method is simply to multiply by the cofactor. | ||
/// Some curves can implement a more efficient algorithm. | ||
fn clear_cofactor(&self) -> Self { | ||
P::clear_cofactor(self) | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> Zeroize for Affine<P> { | ||
// The phantom data does not contain element-specific data | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which "phantom data" is this referring to? |
||
// and thus does not need to be zeroized. | ||
fn zeroize(&mut self) { | ||
self.x.zeroize(); | ||
self.y.zeroize(); | ||
} | ||
Comment on lines
+151
to
+154
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If Self::new_unchecked(P::BaseField::ZERO, P::BaseField::ONE) // (x: 0, y: 1) Shouldn't this be something like: fn zeroize(&mut self) {
self.x.zeroize();
self.y = P::BaseField::ONE;
} ? |
||
} | ||
|
||
impl<P: TECurveConfig> Neg for Affine<P> { | ||
type Output = Self; | ||
|
||
fn neg(self) -> Self { | ||
Self::new_unchecked(-self.x, self.y) | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig, T: Borrow<Self>> Add<T> for Affine<P> { | ||
type Output = Projective<P>; | ||
|
||
fn add(self, other: T) -> Self::Output { | ||
let mut copy = self.into_group(); | ||
copy += other.borrow(); | ||
copy | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> Add<Projective<P>> for Affine<P> { | ||
type Output = Projective<P>; | ||
|
||
fn add(self, other: Projective<P>) -> Projective<P> { | ||
other + self | ||
} | ||
} | ||
|
||
impl<'a, P: TECurveConfig> Add<&'a Projective<P>> for Affine<P> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Current
Would be useful to have impls for:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could these be implemented with |
||
type Output = Projective<P>; | ||
|
||
fn add(self, other: &'a Projective<P>) -> Projective<P> { | ||
*other + self | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig, T: Borrow<Self>> Sub<T> for Affine<P> { | ||
type Output = Projective<P>; | ||
|
||
fn sub(self, other: T) -> Self::Output { | ||
let mut copy = self.into_group(); | ||
copy -= other.borrow(); | ||
copy | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> Sub<Projective<P>> for Affine<P> { | ||
type Output = Projective<P>; | ||
|
||
fn sub(self, other: Projective<P>) -> Projective<P> { | ||
self + (-other) | ||
} | ||
} | ||
|
||
impl<'a, P: TECurveConfig> Sub<&'a Projective<P>> for Affine<P> { | ||
type Output = Projective<P>; | ||
|
||
fn sub(self, other: &'a Projective<P>) -> Projective<P> { | ||
self + (-*other) | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig> Default for Affine<P> { | ||
#[inline] | ||
fn default() -> Self { | ||
Self::zero() | ||
} | ||
} | ||
|
||
impl<P: TECurveConfig, T: Borrow<P::ScalarField>> Mul<T> for Affine<P> { | ||
type Output = Projective<P>; | ||
|
||
#[inline] | ||
fn mul(self, other: T) -> Self::Output { | ||
self.mul_bigint(other.borrow().into_bigint()) | ||
} | ||
} | ||
|
||
// The projective point X, Y, T, Z is represented in the affine | ||
// coordinates as X/Z, Y/Z. | ||
impl<P: TECurveConfig> From<Projective<P>> for Affine<P> { | ||
fn from(p: Projective<P>) -> Affine<P> { | ||
if p.is_zero() { | ||
Affine::zero() | ||
} else if p.z.is_one() { | ||
// If Z is one, the point is already normalized. | ||
Affine::new_unchecked(p.x, p.y) | ||
} else { | ||
// Z is nonzero, so it must have inverse in a field. | ||
let z_inv = p.z.inverse().unwrap(); | ||
let x = p.x * z_inv; | ||
let y = p.y * z_inv; | ||
Affine::new_unchecked(x, y) | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some general comments:
inline(always)
ormust_use
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with @0xNeshi 💯