use crate::ed25519::Fq;
use crate::ed25519::Fr;
use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt};
use core::cmp;
use core::fmt::Debug;
use core::iter::Sum;
use core::ops::{Add, Mul, Neg, Sub};
use ff::{BatchInverter, Field, PrimeField};
use group::{self, Curve};
use group::{prime::PrimeCurveAffine, GroupEncoding};
use rand::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
#[cfg(feature = "derive_serde")]
use serde::{Deserialize, Serialize};
const ED25519_GENERATOR_X: Fq = Fq::from_raw([
0xc956_2d60_8f25_d51a,
0x692c_c760_9525_a7b2,
0xc0a4_e231_fdd6_dc5c,
0x2169_36d3_cd6e_53fe,
]);
const ED25519_GENERATOR_Y: Fq = Fq::from_raw([
0x6666_6666_6666_6658,
0x6666_6666_6666_6666,
0x6666_6666_6666_6666,
0x6666_6666_6666_6666,
]);
const ED25519_D: Fq = Fq::from_raw([
0x75eb_4dca_1359_78a3,
0x0070_0a4d_4141_d8ab,
0x8cc7_4079_7779_e898,
0x5203_6cee_2b6f_fe73,
]);
const FR_MODULUS_BYTES: [u8; 32] = [
237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 16,
];
use crate::{
impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
};
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))]
pub struct Ed25519 {
pub x: Fq,
pub y: Fq,
pub z: Fq,
pub t: Fq,
}
#[derive(Copy, Clone, Debug, PartialEq, Hash)]
#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))]
pub struct Ed25519Affine {
pub x: Fq,
pub y: Fq,
}
#[derive(Copy, Clone, Hash, Default)]
pub struct Ed25519Compressed([u8; 32]);
impl Ed25519 {
pub const fn identity() -> Self {
Ed25519 {
x: Fq::zero(),
y: Fq::one(),
z: Fq::one(),
t: Fq::zero(),
}
}
pub fn is_identity(&self) -> Choice {
self.x.ct_eq(&Fq::zero()) & self.y.ct_eq(&self.z)
}
pub fn is_torsion_free(&self) -> Choice {
self.multiply(&FR_MODULUS_BYTES).is_identity()
}
#[inline]
fn multiply(&self, by: &[u8; 32]) -> Ed25519 {
let zero = Ed25519::identity();
let mut acc = Ed25519::identity();
for bit in by
.iter()
.rev()
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
.skip(3)
{
acc = acc.double();
acc += Ed25519::conditional_select(&zero, self, bit);
}
acc
}
pub fn mul_by_cofactor(&self) -> Ed25519 {
self.double().double().double()
}
pub fn generator() -> Self {
let generator = Ed25519Affine::generator();
Self {
x: generator.x,
y: generator.y,
z: Fq::one(),
t: generator.x * generator.y,
}
}
pub fn double(&self) -> Ed25519 {
let a = self.x.square();
let b = self.y.square();
let c = self.z.square().double();
let h = a + b;
let e = h - (self.x + self.y).square();
let g = a - b;
let f = c + g;
Ed25519 {
x: e * f,
y: g * h,
z: f * g,
t: e * h,
}
}
}
impl Ed25519Affine {
pub const fn identity() -> Self {
Ed25519Affine {
x: Fq::zero(),
y: Fq::one(),
}
}
pub fn is_identity(&self) -> Choice {
Ed25519::from(*self).is_identity()
}
pub fn generator() -> Self {
Self {
x: ED25519_GENERATOR_X,
y: ED25519_GENERATOR_Y,
}
}
pub fn to_extended(&self) -> Ed25519 {
Ed25519 {
x: self.x,
y: self.y,
z: Fq::one(),
t: self.x * self.y,
}
}
pub fn random(mut rng: impl RngCore) -> Self {
loop {
let y = Fq::random(&mut rng);
let flip_sign = rng.next_u32() % 2 != 0;
let y2 = y.square();
let p = ((y2 - Fq::one())
* ((Fq::one() + ED25519_D * y2).invert().unwrap_or(Fq::zero())))
.sqrt()
.map(|x| Ed25519Affine {
x: if flip_sign { -x } else { x },
y,
});
if p.is_some().into() {
use crate::group::cofactor::CofactorGroup;
let p = p.unwrap().to_curve();
if bool::from(!p.is_identity()) {
return p.clear_cofactor().to_affine();
}
}
}
}
pub fn to_bytes(&self) -> [u8; 32] {
let mut tmp = self.y.to_bytes();
let u = self.x.to_bytes();
tmp[31] |= u[0] << 7;
tmp
}
pub fn from_bytes(b: [u8; 32]) -> CtOption<Self> {
Self::from_bytes_inner(b, 1.into())
}
fn from_bytes_inner(mut b: [u8; 32], zip_216_enabled: Choice) -> CtOption<Self> {
let sign = b[31] >> 7;
b[31] &= 0b0111_1111;
Fq::from_bytes(&b).and_then(|v| {
let v2 = v.square();
((v2 - Fq::one()) * ((Fq::one() + ED25519_D * v2).invert().unwrap_or(Fq::zero())))
.sqrt()
.and_then(|u| {
let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1);
let u_negated = -u;
let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);
let u_is_zero = u.ct_eq(&Fq::zero());
CtOption::new(
Ed25519Affine { x: final_u, y: v },
!(zip_216_enabled & u_is_zero & flip_sign),
)
})
})
}
}
impl std::fmt::Debug for Ed25519Compressed {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0[..].fmt(f)
}
}
impl AsRef<[u8]> for Ed25519Compressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for Ed25519Compressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl<'a> From<&'a Ed25519Affine> for Ed25519 {
fn from(p: &'a Ed25519Affine) -> Ed25519 {
p.to_curve()
}
}
impl From<Ed25519Affine> for Ed25519 {
fn from(p: Ed25519Affine) -> Ed25519 {
p.to_curve()
}
}
impl Default for Ed25519 {
fn default() -> Ed25519 {
Ed25519::identity()
}
}
impl subtle::ConstantTimeEq for Ed25519 {
fn ct_eq(&self, other: &Self) -> Choice {
(self.x * other.z).ct_eq(&(other.x * self.z))
& (self.y * other.z).ct_eq(&(other.y * self.z))
}
}
impl subtle::ConditionallySelectable for Ed25519 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Ed25519 {
x: Fq::conditional_select(&a.x, &b.x, choice),
y: Fq::conditional_select(&a.y, &b.y, choice),
z: Fq::conditional_select(&a.z, &b.z, choice),
t: Fq::conditional_select(&a.t, &b.t, choice),
}
}
}
impl PartialEq for Ed25519 {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl cmp::Eq for Ed25519 {}
impl CurveExt for Ed25519 {
type ScalarExt = Fr;
type Base = Fq;
type AffineExt = Ed25519Affine;
const CURVE_ID: &'static str = "ed25519";
fn is_on_curve(&self) -> Choice {
let affine = Ed25519Affine::from(*self);
!self.z.is_zero() & affine.is_on_curve() & (affine.x * affine.y * self.z).ct_eq(&self.t)
}
fn endo(&self) -> Self {
unimplemented!();
}
fn jacobian_coordinates(&self) -> (Fq, Fq, Fq) {
unimplemented!();
}
fn hash_to_curve<'a>(_: &'a str) -> Box<dyn Fn(&[u8]) -> Self + 'a> {
unimplemented!();
}
fn a() -> Self::Base {
unimplemented!()
}
fn b() -> Self::Base {
unimplemented!()
}
fn new_jacobian(_x: Self::Base, _y: Self::Base, _z: Self::Base) -> CtOption<Self> {
unimplemented!();
}
}
impl group::Curve for Ed25519 {
type AffineRepr = Ed25519Affine;
fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) {
assert_eq!(p.len(), q.len());
for (p, q) in p.iter().zip(q.iter_mut()) {
q.x = p.z;
}
BatchInverter::invert_with_internal_scratch(q, |q| &mut q.x, |q| &mut q.y);
for (p, q) in p.iter().zip(q.iter_mut()).rev() {
let tmp = q.x;
q.x = p.x * tmp; q.y = p.y * tmp; }
}
fn to_affine(&self) -> Self::AffineRepr {
let zinv = self.z.invert().unwrap();
Ed25519Affine {
x: self.x * zinv,
y: self.y * zinv,
}
}
}
impl group::Group for Ed25519 {
type Scalar = Fr;
fn random(mut rng: impl RngCore) -> Self {
Ed25519Affine::random(&mut rng).to_curve()
}
fn generator() -> Self {
Ed25519::generator()
}
fn identity() -> Self {
Self::identity()
}
fn is_identity(&self) -> Choice {
self.is_identity()
}
#[must_use]
fn double(&self) -> Self {
self.double()
}
}
impl GroupEncoding for Ed25519 {
type Repr = Ed25519Compressed;
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
Ed25519Affine::from_bytes(bytes.0).map(Self::from)
}
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
Ed25519Affine::from_bytes(bytes.0).map(Self::from)
}
fn to_bytes(&self) -> Self::Repr {
Ed25519Compressed(Ed25519Affine::from(self).to_bytes())
}
}
impl crate::serde::SerdeObject for Ed25519 {
fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self {
debug_assert_eq!(bytes.len(), 4 * Fq::size());
let [x, y, z, t] = [0, 1, 2, 3]
.map(|i| Fq::from_raw_bytes_unchecked(&bytes[i * Fq::size()..(i + 1) * Fq::size()]));
Self { x, y, z, t }
}
fn from_raw_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != 4 * Fq::size() {
return None;
}
let [x, y, z, t] =
[0, 1, 2, 3].map(|i| Fq::from_raw_bytes(&bytes[i * Fq::size()..(i + 1) * Fq::size()]));
x.zip(y).zip(z).zip(t).and_then(|(((x, y), z), t)| {
let res = Self { x, y, z, t };
bool::from(res.is_on_curve()).then_some(res)
})
}
fn to_raw_bytes(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(4 * Fq::size());
Self::write_raw(self, &mut res).unwrap();
res
}
fn read_raw_unchecked<R: std::io::Read>(reader: &mut R) -> Self {
let [x, y, z, t] = [(); 4].map(|_| Fq::read_raw_unchecked(reader));
Self { x, y, z, t }
}
fn read_raw<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
let x = Fq::read_raw(reader)?;
let y = Fq::read_raw(reader)?;
let z = Fq::read_raw(reader)?;
let t = Fq::read_raw(reader)?;
Ok(Self { x, y, z, t })
}
fn write_raw<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
self.x.write_raw(writer)?;
self.y.write_raw(writer)?;
self.z.write_raw(writer)?;
self.t.write_raw(writer)
}
}
impl group::prime::PrimeGroup for Ed25519 {}
impl group::prime::PrimeCurve for Ed25519 {
type Affine = Ed25519Affine;
}
impl group::cofactor::CofactorCurve for Ed25519 {
type Affine = Ed25519Affine;
}
impl group::cofactor::CofactorGroup for Ed25519 {
type Subgroup = Ed25519;
fn clear_cofactor(&self) -> Self {
self.mul_by_cofactor()
}
fn into_subgroup(self) -> CtOption<Self::Subgroup> {
CtOption::new(self, self.is_torsion_free())
}
fn is_torsion_free(&self) -> Choice {
self.is_torsion_free()
}
}
impl<'a> From<&'a Ed25519> for Ed25519Affine {
fn from(p: &'a Ed25519) -> Ed25519Affine {
p.to_affine()
}
}
impl From<Ed25519> for Ed25519Affine {
fn from(p: Ed25519) -> Ed25519Affine {
p.to_affine()
}
}
impl Default for Ed25519Affine {
fn default() -> Ed25519Affine {
Ed25519Affine::identity()
}
}
impl subtle::ConstantTimeEq for Ed25519Affine {
fn ct_eq(&self, other: &Self) -> Choice {
self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y)
}
}
impl subtle::ConditionallySelectable for Ed25519Affine {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Ed25519Affine {
x: Fq::conditional_select(&a.x, &b.x, choice),
y: Fq::conditional_select(&a.y, &b.y, choice),
}
}
}
impl cmp::Eq for Ed25519Affine {}
impl group::GroupEncoding for Ed25519Affine {
type Repr = [u8; 32];
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
Self::from_bytes(*bytes)
}
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
Self::from_bytes(*bytes)
}
fn to_bytes(&self) -> Self::Repr {
self.to_bytes()
}
}
impl crate::serde::SerdeObject for Ed25519Affine {
fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self {
debug_assert_eq!(bytes.len(), 2 * Fq::size());
let [x, y] =
[0, Fq::size()].map(|i| Fq::from_raw_bytes_unchecked(&bytes[i..i + Fq::size()]));
Self { x, y }
}
fn from_raw_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != 2 * Fq::size() {
return None;
}
let [x, y] = [0, Fq::size()].map(|i| Fq::from_raw_bytes(&bytes[i..i + Fq::size()]));
x.zip(y).and_then(|(x, y)| {
let res = Self { x, y };
bool::from(res.is_on_curve()).then_some(res)
})
}
fn to_raw_bytes(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(2 * Fq::size());
Self::write_raw(self, &mut res).unwrap();
res
}
fn read_raw_unchecked<R: std::io::Read>(reader: &mut R) -> Self {
let [x, y] = [(); 2].map(|_| Fq::read_raw_unchecked(reader));
Self { x, y }
}
fn read_raw<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
let x = Fq::read_raw(reader)?;
let y = Fq::read_raw(reader)?;
Ok(Self { x, y })
}
fn write_raw<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
self.x.write_raw(writer)?;
self.y.write_raw(writer)
}
}
impl group::prime::PrimeCurveAffine for Ed25519Affine {
type Curve = Ed25519;
type Scalar = Fr;
fn generator() -> Self {
Ed25519Affine::generator()
}
fn identity() -> Self {
Ed25519Affine::identity()
}
fn is_identity(&self) -> Choice {
self.is_identity()
}
fn to_curve(&self) -> Self::Curve {
Ed25519 {
x: self.x,
y: self.y,
z: Fq::one(),
t: self.x * self.y,
}
}
}
impl group::cofactor::CofactorCurveAffine for Ed25519Affine {
type Curve = Ed25519;
type Scalar = Fr;
fn identity() -> Self {
<Self as group::prime::PrimeCurveAffine>::identity()
}
fn generator() -> Self {
<Self as group::prime::PrimeCurveAffine>::generator()
}
fn is_identity(&self) -> Choice {
<Self as group::prime::PrimeCurveAffine>::is_identity(self)
}
fn to_curve(&self) -> Self::Curve {
<Self as group::prime::PrimeCurveAffine>::to_curve(self)
}
}
impl CurveAffine for Ed25519Affine {
type ScalarExt = Fr;
type Base = Fq;
type CurveExt = Ed25519;
fn is_on_curve(&self) -> Choice {
let x2 = self.x.square();
let y2 = self.y.square();
(y2 - x2).ct_eq(&(Fq::one() + ED25519_D * x2 * y2))
}
fn coordinates(&self) -> CtOption<Coordinates<Self>> {
Coordinates::from_xy(self.x, self.y)
}
fn from_xy(x: Self::Base, y: Self::Base) -> CtOption<Self> {
let p = Ed25519Affine { x, y };
CtOption::new(p, p.is_on_curve())
}
fn a() -> Self::Base {
unimplemented!()
}
fn b() -> Self::Base {
unimplemented!()
}
}
impl_binops_additive!(Ed25519, Ed25519);
impl_binops_additive!(Ed25519, Ed25519Affine);
impl_binops_additive_specify_output!(Ed25519Affine, Ed25519Affine, Ed25519);
impl_binops_additive_specify_output!(Ed25519Affine, Ed25519, Ed25519);
impl_binops_multiplicative!(Ed25519, Fr);
impl_binops_multiplicative_mixed!(Ed25519Affine, Fr, Ed25519);
impl<'a> Neg for &'a Ed25519 {
type Output = Ed25519;
fn neg(self) -> Ed25519 {
Ed25519 {
x: -self.x,
y: self.y,
z: self.z,
t: -self.t,
}
}
}
impl Neg for Ed25519 {
type Output = Ed25519;
fn neg(self) -> Ed25519 {
-&self
}
}
impl<T> Sum<T> for Ed25519
where
T: core::borrow::Borrow<Ed25519>,
{
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = T>,
{
iter.fold(Self::identity(), |acc, item| acc + item.borrow())
}
}
impl<'a, 'b> Add<&'a Ed25519> for &'b Ed25519 {
type Output = Ed25519;
fn add(self, rhs: &'a Ed25519) -> Ed25519 {
let a = (self.x - self.y) * (rhs.x - rhs.y);
let b = (self.x + self.y) * (rhs.x + rhs.y);
let c = (self.t * rhs.t * ED25519_D).double();
let d = (self.z * rhs.z).double();
let e = b - a;
let f = d - c;
let g = d + c;
let h = b + a;
Ed25519 {
x: e * f,
y: g * h,
z: f * g,
t: e * h,
}
}
}
impl<'a, 'b> Add<&'a Ed25519Affine> for &'b Ed25519 {
type Output = Ed25519;
fn add(self, rhs: &'a Ed25519Affine) -> Ed25519 {
self + rhs.to_extended()
}
}
impl<'a, 'b> Sub<&'a Ed25519> for &'b Ed25519 {
type Output = Ed25519;
fn sub(self, other: &'a Ed25519) -> Ed25519 {
self + (-other)
}
}
impl<'a, 'b> Sub<&'a Ed25519Affine> for &'b Ed25519 {
type Output = Ed25519;
fn sub(self, other: &'a Ed25519Affine) -> Ed25519 {
self + (-other)
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl<'a, 'b> Mul<&'b Fr> for &'a Ed25519 {
type Output = Ed25519;
fn mul(self, other: &'b Fr) -> Self::Output {
let mut acc = Ed25519::identity();
for bit in other
.to_repr()
.iter()
.rev()
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
.skip(3)
{
acc = acc.double();
acc = Ed25519::conditional_select(&acc, &(acc + self), bit);
}
acc
}
}
impl<'a> Neg for &'a Ed25519Affine {
type Output = Ed25519Affine;
fn neg(self) -> Ed25519Affine {
Ed25519Affine {
x: -self.x,
y: self.y,
}
}
}
impl Neg for Ed25519Affine {
type Output = Ed25519Affine;
fn neg(self) -> Ed25519Affine {
-&self
}
}
impl<'a, 'b> Add<&'a Ed25519> for &'b Ed25519Affine {
type Output = Ed25519;
fn add(self, rhs: &'a Ed25519) -> Ed25519 {
rhs + self
}
}
impl<'a, 'b> Add<&'a Ed25519Affine> for &'b Ed25519Affine {
type Output = Ed25519;
fn add(self, rhs: &'a Ed25519Affine) -> Ed25519 {
self.to_extended() + rhs.to_extended()
}
}
impl<'a, 'b> Sub<&'a Ed25519Affine> for &'b Ed25519Affine {
type Output = Ed25519;
fn sub(self, other: &'a Ed25519Affine) -> Ed25519 {
self + (-other)
}
}
impl<'a, 'b> Sub<&'a Ed25519> for &'b Ed25519Affine {
type Output = Ed25519;
fn sub(self, other: &'a Ed25519) -> Ed25519 {
self + (-other)
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl<'a, 'b> Mul<&'b Fr> for &'a Ed25519Affine {
type Output = Ed25519;
fn mul(self, other: &'b Fr) -> Self::Output {
let mut acc = Ed25519::identity();
for bit in other
.to_repr()
.iter()
.rev()
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
{
acc = acc.double();
acc = Ed25519::conditional_select(&acc, &(acc + self), bit);
}
acc
}
}
impl CurveAffineExt for Ed25519Affine {
fn into_coordinates(self) -> (Self::Base, Self::Base) {
(self.x, self.y)
}
}
pub trait TwistedEdwardsCurveExt: CurveExt {
fn a() -> <Self as CurveExt>::Base;
fn d() -> <Self as CurveExt>::Base;
}
impl TwistedEdwardsCurveExt for Ed25519 {
fn a() -> Fq {
-Fq::ONE
}
fn d() -> Fq {
ED25519_D
}
}
pub trait TwistedEdwardsCurveAffineExt: CurveAffineExt {
fn a() -> <Self as CurveAffine>::Base;
fn d() -> <Self as CurveAffine>::Base;
}
impl TwistedEdwardsCurveAffineExt for Ed25519Affine {
fn a() -> Fq {
-Fq::ONE
}
fn d() -> Fq {
ED25519_D
}
}
#[test]
fn test_is_on_curve() {
assert!(bool::from(Ed25519Affine::identity().is_on_curve()));
}
#[test]
fn test_d_is_non_quadratic_residue() {
assert!(bool::from(ED25519_D.sqrt().is_none()));
assert!(bool::from((-ED25519_D).sqrt().is_none()));
assert!(bool::from((-ED25519_D).invert().unwrap().sqrt().is_none()));
}
#[test]
fn test_double() {
let p = Ed25519::generator();
assert_eq!(p.double(), p + p);
}
#[test]
fn test_assoc() {
let p = Ed25519::from(Ed25519Affine {
x: Fq::from_raw([
0x4eb5_31fa_487c_0f3e,
0x1313_5118_1c90_b35e,
0xdb9a_afaf_f32a_26f7,
0x5e0c_b226_a2aa_bab4,
]),
y: Fq::from_raw([
0xbf09_6275_684b_b8c9,
0xc7ba_2458_90af_256d,
0x5911_9f3e_8638_0eb0,
0x3793_de18_2f9f_b1d2,
]),
})
.mul_by_cofactor();
assert!(bool::from(p.is_on_curve()));
assert_eq!(
(p * Fr::from(1000u64)) * Fr::from(3938u64),
p * (Fr::from(1000u64) * Fr::from(3938u64)),
);
}
#[test]
fn test_curve() {
crate::tests::curve::curve_tests::<Ed25519>();
}
#[test]
fn test_serialization() {
crate::tests::curve::random_serialization_test::<Ed25519>();
}