//! 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. 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}; /// 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 /// efficient. pub trait Curve: Sized + Default + Copy + Clone + Send + Sync + 'static + Debug + Add + Sub + Mul<::Scalar, Output = Self> + Neg + for<'a> Add<&'a Self, Output = Self> + for<'a> Sub<&'a Self, Output = Self> + MulAssign<::Scalar> + AddAssign + SubAssign + for<'a> AddAssign<&'a Self> + for<'a> SubAssign<&'a Self> + AddAssign<::Affine> + SubAssign<::Affine> + PartialEq + cmp::Eq + ConditionallySelectable + ConstantTimeEq + From<::Affine> + Group::Scalar> { /// The representation of a point on this curve in the affine coordinate space. type Affine: CurveAffine< Projective = Self, Scalar = ::Scalar, Base = ::Base, > + Add + Sub + Mul<::Scalar, Output = Self> + Neg::Affine> + From; /// The scalar field of this elliptic curve. type Scalar: Field; /// The base field over which this elliptic curve is constructed. type Base: Field; /// Obtains the additive identity. fn zero() -> Self; /// Obtains the base point of the curve. fn one() -> Self; /// Doubles this element. fn double(&self) -> Self; /// Returns whether or not this element is the identity. fn is_zero(&self) -> Choice; /// Apply the curve endomorphism by multiplying the x-coordinate /// by an element of multiplicative order 3. fn endo(&self) -> Self; /// Converts this element into its affine form. fn to_affine(&self) -> Self::Affine; /// Returns whether or not this element is on the curve; should /// always be true unless an "unchecked" API was used. fn is_on_curve(&self) -> Choice; /// Converts many elements into their affine form. Panics if the /// sizes of the slices are different. fn batch_to_affine(v: &[Self], target: &mut [Self::Affine]); /// Returns the curve constant b fn b() -> Self::Base; } /// This trait is the affine counterpart to `Curve` and is used for /// serialization, storage in memory, and inspection of $x$ and $y$ coordinates. pub trait CurveAffine: Sized + Default + Copy + Clone + Send + Sync + 'static + Debug + Add::Projective> + Sub::Projective> + Mul<::Scalar, Output = ::Projective> + Neg + PartialEq + cmp::Eq + ConditionallySelectable + ConstantTimeEq + From<::Projective> { /// The representation of a point on this curve in the projective coordinate space. type Projective: Curve< Affine = Self, Scalar = ::Scalar, Base = ::Base, > + Mul<::Scalar, Output = ::Projective> + MulAssign<::Scalar> + AddAssign + SubAssign + From; /// The scalar field of this elliptic curve. type Scalar: Field; /// The base field over which this elliptic curve is constructed. type Base: Field; /// Obtains the additive identity. fn zero() -> Self; /// Obtains the base point of the curve. fn one() -> Self; /// Returns whether or not this element is the identity. fn is_zero(&self) -> Choice; /// Converts this element into its projective form. fn to_projective(&self) -> Self::Projective; /// Gets the $(x, y)$ coordinates of this point. fn get_xy(&self) -> CtOption<(Self::Base, Self::Base)>; /// Obtains a point given $(x, y)$, failing if it is not on the /// curve. fn from_xy(x: Self::Base, y: Self::Base) -> CtOption; /// Returns whether or not this element is on the curve; should /// always be true unless an "unchecked" API was used. fn is_on_curve(&self) -> Choice; /// Attempts to obtain a group element from its compressed 32-byte little /// endian representation. fn from_bytes(bytes: &[u8; 32]) -> CtOption; /// Obtains the compressed, 32-byte little endian representation of this /// element. fn to_bytes(&self) -> [u8; 32]; /// Attempts to obtain a group element from its uncompressed 64-byte little /// endian representation. fn from_bytes_wide(bytes: &[u8; 64]) -> CtOption; /// Obtains the uncompressed, 64-byte little endian representation of this /// element. fn to_bytes_wide(&self) -> [u8; 64]; /// 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);