mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 04:39:24 +00:00
Move embedwards25519 over to short-weierstrass
This commit is contained in:
@@ -21,7 +21,10 @@ rand_core = { version = "0.6", default-features = false }
|
||||
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
||||
group = { version = "0.13", default-features = false }
|
||||
|
||||
generic-array = { version = "1", default-features = false, optional = true }
|
||||
ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false, optional = true }
|
||||
|
||||
[features]
|
||||
alloc = ["zeroize/alloc", "rand_core/alloc", "ff/alloc", "group/alloc"]
|
||||
std = ["zeroize/std", "subtle/std", "rand_core/std", "ff/std"]
|
||||
alloc = ["zeroize/alloc", "rand_core/alloc", "ff/alloc", "group/alloc", "generic-array", "ec-divisors"]
|
||||
std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "ff/std"]
|
||||
default = ["std"]
|
||||
|
||||
@@ -5,7 +5,7 @@ use zeroize::DefaultIsZeroes;
|
||||
|
||||
use rand_core::RngCore;
|
||||
|
||||
use group::ff::Field;
|
||||
use group::ff::{Field, PrimeField};
|
||||
|
||||
use crate::{ShortWeierstrass, Projective};
|
||||
|
||||
@@ -23,8 +23,14 @@ impl<C: ShortWeierstrass> Clone for Affine<C> {
|
||||
impl<C: ShortWeierstrass> Copy for Affine<C> {}
|
||||
|
||||
impl<C: ShortWeierstrass> Affine<C> {
|
||||
pub fn try_from(projective: Projective<C>) -> CtOption<Self> {
|
||||
projective.z.invert().map(|z_inv| Self { x: projective.x * z_inv, y: projective.y * z_inv })
|
||||
pub fn try_from(projective: &Projective<C>) -> CtOption<Self> {
|
||||
let z_inv = projective.z.invert().unwrap_or(C::FieldElement::ZERO);
|
||||
let if_non_identity = Self { x: projective.x * z_inv, y: projective.y * z_inv };
|
||||
let identity = z_inv.ct_eq(&C::FieldElement::ZERO);
|
||||
// If this isn't a valid affine point, switch to a valid affine point
|
||||
let value = <_>::conditional_select(&if_non_identity, &C::GENERATOR, identity);
|
||||
// This is valid if the projective point wasn't the identity
|
||||
CtOption::new(value, !identity)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,24 +79,35 @@ impl<C: ShortWeierstrass> Affine<C> {
|
||||
/// Create an affine point from `x, y` coordinates, without performing any checks.
|
||||
///
|
||||
/// This should NOT be used. It is solely intended for trusted data at compile-time. It MUST NOT
|
||||
/// be used with any untrusted/unvalidated data.
|
||||
pub const fn from_xy_unchecked(x: C::FieldElement, y: C::FieldElement) -> Self { Self { x, y } }
|
||||
/// be used with any untrusted/unvalidated data. Providing any off-curve point may produce
|
||||
/// completely undefined behavior.
|
||||
pub const fn from_xy_unchecked(x: C::FieldElement, y: C::FieldElement) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
/// Decompress a point from it's `x`-coordinate and the sign of the `y` coordinate.
|
||||
pub fn decompress(x: C::FieldElement, odd_y: Choice) -> CtOption<Self> {
|
||||
let y_square = ((x.square() + C::A) * x) + C::B;
|
||||
y_square.sqrt().and_then(|mut y| {
|
||||
y = <_>::conditional_select(&y, &-y, odd_y.ct_ne(&y.is_odd()));
|
||||
CtOption::new(Self { x, y }, 1.into())
|
||||
})
|
||||
}
|
||||
|
||||
/// The `x, y` coordinates of this point.
|
||||
pub fn coordinates(self) -> (C::FieldElement, C::FieldElement) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Sample a random on-curve point with an unknown discrete logarithm w.r.t. any other points.
|
||||
pub fn random(mut rng: impl RngCore) -> Self {
|
||||
loop {
|
||||
let x = C::FieldElement::random(&mut rng);
|
||||
let y_square = ((x.square() + C::A) * x) + C::B;
|
||||
let Some(mut y) = Option::<C::FieldElement>::from(y_square.sqrt()) else { continue };
|
||||
if (rng.next_u64() % 2) == 1 {
|
||||
y = -y;
|
||||
/// Sample a random on-curve point with an unknown discrete logarithm w.r.t. any other points.
|
||||
///
|
||||
/// This runs in time variable to the amount of samples required to find a point.
|
||||
pub fn random(mut rng: impl RngCore) -> Self {
|
||||
loop {
|
||||
let x = C::FieldElement::random(&mut rng);
|
||||
let y_is_odd = Choice::from((rng.next_u64() % 2) as u8);
|
||||
let Some(res) = Option::<Self>::from(Self::decompress(x, y_is_odd)) else { continue };
|
||||
return res;
|
||||
}
|
||||
return Self { x, y };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
use core::fmt::Debug;
|
||||
|
||||
use subtle::Choice;
|
||||
use zeroize::Zeroize;
|
||||
use group::ff::PrimeField;
|
||||
|
||||
@@ -14,6 +15,10 @@ mod projective;
|
||||
pub use projective::Projective;
|
||||
|
||||
/// An elliptic curve represented in short Weierstrass form, with equation `y^2 = x^3 + A x + B`.
|
||||
///
|
||||
/// This elliptic curve is expected to be of prime order. If a generator of the elliptic curve has
|
||||
/// a composite order, the elliptic curve is defined solely as its largest odd-prime-order
|
||||
/// subgroup, further considered the entire group/elliptic curve.
|
||||
pub trait ShortWeierstrass: 'static + Sized + Debug {
|
||||
/// The field the elliptic curve is defined over.
|
||||
type FieldElement: Zeroize + PrimeField;
|
||||
@@ -27,4 +32,17 @@ pub trait ShortWeierstrass: 'static + Sized + Debug {
|
||||
///
|
||||
/// This may be omitted by specifying `()`.
|
||||
type Scalar;
|
||||
|
||||
/// The type used for encoding points.
|
||||
type Repr: 'static + Send + Sync + Copy + Default + AsRef<[u8]> + AsMut<[u8]>;
|
||||
/// The representation of the identity point.
|
||||
const IDENTITY: Self::Repr;
|
||||
/// Compress an affine point its byte encoding.
|
||||
///
|
||||
/// The space of potential outputs MUST exclude `Self::IDENTITY`.
|
||||
fn compress(x: Self::FieldElement, odd_y: Choice) -> Self::Repr;
|
||||
/// Decode a compressed point.
|
||||
///
|
||||
/// This is expected to return the `x` coordinate and if the `y` coordinate is odd.
|
||||
fn decode_compressed(bytes: &Self::Repr) -> (<Self::FieldElement as PrimeField>::Repr, Choice);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use core::{hint::black_box, borrow::Borrow, ops::*, iter::Sum};
|
||||
|
||||
use subtle::{Choice, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable};
|
||||
use subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable};
|
||||
use zeroize::{Zeroize, DefaultIsZeroes};
|
||||
|
||||
use rand_core::RngCore;
|
||||
|
||||
use group::{
|
||||
ff::{Field, PrimeFieldBits},
|
||||
Group,
|
||||
ff::{Field, PrimeField, PrimeFieldBits},
|
||||
Group, GroupEncoding,
|
||||
prime::PrimeGroup,
|
||||
};
|
||||
|
||||
use crate::{ShortWeierstrass, Affine};
|
||||
@@ -280,19 +281,6 @@ impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>, S: Borrow<C::Scalar>> MulAssig
|
||||
*self = *self * scalar.borrow();
|
||||
}
|
||||
}
|
||||
/*
|
||||
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> Mul<&C::Scalar> for Projective<C> {
|
||||
type Output = Self;
|
||||
fn mul(self, scalar: &C::Scalar) -> Self {
|
||||
self * *scalar
|
||||
}
|
||||
}
|
||||
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> MulAssign<&C::Scalar> for Projective<C> {
|
||||
fn mul_assign(&mut self, scalar: &C::Scalar) {
|
||||
*self *= *scalar;
|
||||
}
|
||||
}
|
||||
*/
|
||||
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> Group for Projective<C> {
|
||||
type Scalar = C::Scalar;
|
||||
fn random(rng: impl RngCore) -> Self {
|
||||
@@ -311,3 +299,73 @@ impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> Group for Projective<C> {
|
||||
self.double_internal()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ShortWeierstrass> GroupEncoding for Projective<C> {
|
||||
type Repr = C::Repr;
|
||||
fn from_bytes(bytes: &C::Repr) -> CtOption<Self> {
|
||||
// If this point is the identity point
|
||||
let identity = bytes.as_ref().ct_eq(C::IDENTITY.as_ref());
|
||||
|
||||
let (x, odd_y) = C::decode_compressed(bytes);
|
||||
|
||||
// Parse x, recover y, return the result
|
||||
C::FieldElement::from_repr(x).and_then(|x| {
|
||||
let non_identity_on_curve_point = Affine::decompress(x, odd_y).map(Projective::from);
|
||||
let identity = CtOption::new(Projective::IDENTITY, identity);
|
||||
non_identity_on_curve_point.or_else(|| identity)
|
||||
})
|
||||
}
|
||||
fn from_bytes_unchecked(bytes: &C::Repr) -> CtOption<Self> {
|
||||
Self::from_bytes(bytes)
|
||||
}
|
||||
fn to_bytes(&self) -> C::Repr {
|
||||
let affine_on_curve = Affine::try_from(self);
|
||||
let identity = affine_on_curve.is_none();
|
||||
|
||||
let compressed_if_not_identity = {
|
||||
let affine_on_curve = affine_on_curve.unwrap_or(C::GENERATOR);
|
||||
let (x, y) = affine_on_curve.coordinates();
|
||||
C::compress(x, y.is_odd())
|
||||
};
|
||||
|
||||
let mut res = C::Repr::default();
|
||||
{
|
||||
let res = res.as_mut();
|
||||
for (dst, (if_not_identity, if_identity)) in
|
||||
res.iter_mut().zip(compressed_if_not_identity.as_ref().iter().zip(C::IDENTITY.as_ref()))
|
||||
{
|
||||
*dst = <_>::conditional_select(if_not_identity, if_identity, identity);
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> PrimeGroup for Projective<C> {}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
mod alloc {
|
||||
use core::borrow::Borrow;
|
||||
use ff::{PrimeField, PrimeFieldBits};
|
||||
use crate::{ShortWeierstrass, Affine, Projective};
|
||||
|
||||
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> ec_divisors::DivisorCurve for Projective<C> {
|
||||
type FieldElement = C::FieldElement;
|
||||
type XyPoint = ec_divisors::Projective<Self>;
|
||||
|
||||
fn interpolator_for_scalar_mul() -> impl Borrow<ec_divisors::Interpolator<C::FieldElement>> {
|
||||
ec_divisors::Interpolator::new((<C::Scalar as PrimeField>::NUM_BITS as usize).div_ceil(2) + 2)
|
||||
}
|
||||
|
||||
fn a() -> C::FieldElement {
|
||||
C::A
|
||||
}
|
||||
fn b() -> C::FieldElement {
|
||||
C::B
|
||||
}
|
||||
|
||||
fn to_xy(point: Self) -> Option<(C::FieldElement, C::FieldElement)> {
|
||||
Option::<Affine<C>>::from(Affine::try_from(&point)).map(Affine::<_>::coordinates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user