use core::fmt::Debug;
pub trait CurveScalar: Clone + Debug + Sized + PartialEq + Send + Sync {
fn zeroize(&mut self);
fn zero() -> Self;
fn one() -> Self;
fn from_u32(v: u32) -> Self;
fn add(&self, other: &Self) -> Self;
fn sub(&self, other: &Self) -> Self;
fn mul(&self, other: &Self) -> Self;
fn neg(&self) -> Self;
fn invert(&self) -> Self;
fn random<R: rand_core::RngCore + rand_core::CryptoRng>(rng: &mut R) -> Self;
fn from_bytes_wide(bytes: &[u8; 64]) -> Self;
fn to_bytes(&self) -> [u8; 32];
fn from_canonical_bytes(bytes: &[u8; 32]) -> Option<Self>;
}
pub trait CurvePoint: Clone + Debug + Sized + PartialEq + Send + Sync {
type Scalar: CurveScalar;
const COMPRESSED_SIZE: usize;
type Compressed: AsRef<[u8]> + Copy + PartialEq + Debug + Send + Sync;
fn identity() -> Self;
fn generator() -> Self;
fn mul_scalar(&self, scalar: &Self::Scalar) -> Self;
fn add(&self, other: &Self) -> Self;
fn multiscalar_mul(scalars: &[Self::Scalar], points: &[Self]) -> Self;
fn compress(&self) -> Self::Compressed;
fn decompress(bytes: &[u8]) -> Option<Self>;
fn compress_vec(&self) -> alloc::vec::Vec<u8> {
self.compress().as_ref().to_vec()
}
}
extern crate alloc;
pub trait Curve: Clone + Debug + Default {
type Scalar: CurveScalar;
type Point: CurvePoint<Scalar = Self::Scalar>;
}
#[cfg(feature = "ristretto255")]
pub mod ristretto {
use super::*;
use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT,
ristretto::{CompressedRistretto, RistrettoPoint},
scalar::Scalar,
traits::MultiscalarMul,
};
use zeroize::Zeroize;
impl CurveScalar for Scalar {
fn zeroize(&mut self) {
Zeroize::zeroize(self);
}
fn zero() -> Self {
Scalar::ZERO
}
fn one() -> Self {
Scalar::ONE
}
fn from_u32(v: u32) -> Self {
Scalar::from(v)
}
fn add(&self, other: &Self) -> Self {
self + other
}
fn sub(&self, other: &Self) -> Self {
self - other
}
fn mul(&self, other: &Self) -> Self {
self * other
}
fn neg(&self) -> Self {
-self
}
fn invert(&self) -> Self {
Scalar::invert(self)
}
fn random<R: rand_core::RngCore + rand_core::CryptoRng>(rng: &mut R) -> Self {
Scalar::random(rng)
}
fn from_bytes_wide(bytes: &[u8; 64]) -> Self {
Scalar::from_bytes_mod_order_wide(bytes)
}
fn to_bytes(&self) -> [u8; 32] {
Scalar::to_bytes(self)
}
fn from_canonical_bytes(bytes: &[u8; 32]) -> Option<Self> {
Scalar::from_canonical_bytes(*bytes).into_option()
}
}
impl CurvePoint for RistrettoPoint {
type Scalar = Scalar;
const COMPRESSED_SIZE: usize = 32;
type Compressed = [u8; 32];
fn identity() -> Self {
curve25519_dalek::traits::Identity::identity()
}
fn generator() -> Self {
RISTRETTO_BASEPOINT_POINT
}
fn mul_scalar(&self, scalar: &Self::Scalar) -> Self {
self * scalar
}
fn add(&self, other: &Self) -> Self {
self + other
}
fn multiscalar_mul(scalars: &[Self::Scalar], points: &[Self]) -> Self {
<RistrettoPoint as MultiscalarMul>::multiscalar_mul(scalars, points)
}
fn compress(&self) -> Self::Compressed {
RistrettoPoint::compress(self).to_bytes()
}
fn decompress(bytes: &[u8]) -> Option<Self> {
let arr: [u8; 32] = bytes.try_into().ok()?;
CompressedRistretto::from_slice(&arr).ok()?.decompress()
}
}
#[derive(Clone, Debug, Default)]
pub struct Ristretto255;
impl Curve for Ristretto255 {
type Scalar = Scalar;
type Point = RistrettoPoint;
}
}
#[cfg(feature = "pallas")]
pub mod pallas {
use super::*;
use pasta_curves::{
group::{
ff::{Field, FromUniformBytes, PrimeField},
Group, GroupEncoding,
},
pallas::{Point, Scalar},
};
impl CurveScalar for Scalar {
fn zeroize(&mut self) {
unsafe { core::ptr::write_volatile(self, Self::zero()) };
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
fn zero() -> Self {
Scalar::ZERO
}
fn one() -> Self {
Scalar::ONE
}
fn from_u32(v: u32) -> Self {
Scalar::from(v as u64)
}
fn add(&self, other: &Self) -> Self {
*self + *other
}
fn sub(&self, other: &Self) -> Self {
*self - *other
}
fn mul(&self, other: &Self) -> Self {
*self * *other
}
fn neg(&self) -> Self {
-(*self)
}
fn invert(&self) -> Self {
Field::invert(self).unwrap_or(Scalar::ZERO)
}
fn random<R: rand_core::RngCore + rand_core::CryptoRng>(rng: &mut R) -> Self {
let mut bytes = [0u8; 64];
rng.fill_bytes(&mut bytes);
<Scalar as FromUniformBytes<64>>::from_uniform_bytes(&bytes)
}
fn from_bytes_wide(bytes: &[u8; 64]) -> Self {
<Scalar as FromUniformBytes<64>>::from_uniform_bytes(bytes)
}
fn to_bytes(&self) -> [u8; 32] {
self.to_repr()
}
fn from_canonical_bytes(bytes: &[u8; 32]) -> Option<Self> {
Scalar::from_repr(*bytes).into_option()
}
}
impl CurvePoint for Point {
type Scalar = Scalar;
const COMPRESSED_SIZE: usize = 32;
type Compressed = [u8; 32];
fn identity() -> Self {
<Point as Group>::identity()
}
fn generator() -> Self {
<Point as Group>::generator()
}
fn mul_scalar(&self, scalar: &Self::Scalar) -> Self {
self * scalar
}
fn add(&self, other: &Self) -> Self {
*self + *other
}
fn multiscalar_mul(scalars: &[Self::Scalar], points: &[Self]) -> Self {
scalars
.iter()
.zip(points.iter())
.fold(<Point as Group>::identity(), |acc, (s, p)| {
acc + p.mul_scalar(s)
})
}
fn compress(&self) -> Self::Compressed {
self.to_bytes()
}
fn decompress(bytes: &[u8]) -> Option<Self> {
let arr: [u8; 32] = bytes.try_into().ok()?;
Point::from_bytes(&arr).into_option()
}
}
#[derive(Clone, Debug, Default)]
pub struct PallasCurve;
impl Curve for PallasCurve {
type Scalar = Scalar;
type Point = Point;
}
pub const ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [
99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, 95, 68, 95,
62, 124, 24, 141, 59, 6, 214, 241, 40, 179, 35, 85, 183,
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SpendAuthPoint(pub Point);
impl SpendAuthPoint {
pub fn basepoint() -> Self {
SpendAuthPoint(
Point::from_bytes(&ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES)
.expect("constant is a valid Pallas point"),
)
}
pub fn inner(&self) -> &Point {
&self.0
}
}
impl CurvePoint for SpendAuthPoint {
type Scalar = Scalar;
const COMPRESSED_SIZE: usize = 32;
type Compressed = [u8; 32];
fn identity() -> Self {
SpendAuthPoint(<Point as Group>::identity())
}
fn generator() -> Self {
Self::basepoint()
}
fn mul_scalar(&self, scalar: &Self::Scalar) -> Self {
SpendAuthPoint(self.0 * scalar)
}
fn add(&self, other: &Self) -> Self {
SpendAuthPoint(self.0 + other.0)
}
fn multiscalar_mul(scalars: &[Self::Scalar], points: &[Self]) -> Self {
scalars
.iter()
.zip(points.iter())
.fold(Self::identity(), |acc, (s, p)| acc.add(&p.mul_scalar(s)))
}
fn compress(&self) -> Self::Compressed {
self.0.to_bytes()
}
fn decompress(bytes: &[u8]) -> Option<Self> {
let arr: [u8; 32] = bytes.try_into().ok()?;
Point::from_bytes(&arr).into_option().map(SpendAuthPoint)
}
}
#[derive(Clone, Debug, Default)]
pub struct OrchardSpendAuthCurve;
impl Curve for OrchardSpendAuthCurve {
type Scalar = Scalar;
type Point = SpendAuthPoint;
}
}
#[cfg(feature = "secp256k1")]
pub mod secp256k1 {
use super::*;
use k256::{
elliptic_curve::{
bigint::U512,
ops::Reduce,
sec1::{FromEncodedPoint, ToEncodedPoint},
Field, PrimeField,
},
ProjectivePoint, Scalar,
};
impl CurveScalar for Scalar {
fn zeroize(&mut self) {
unsafe { core::ptr::write_volatile(self, Self::zero()) };
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
fn zero() -> Self {
Scalar::ZERO
}
fn one() -> Self {
Scalar::ONE
}
fn from_u32(v: u32) -> Self {
Scalar::from(v as u64)
}
fn add(&self, other: &Self) -> Self {
*self + *other
}
fn sub(&self, other: &Self) -> Self {
*self - *other
}
fn mul(&self, other: &Self) -> Self {
*self * *other
}
fn neg(&self) -> Self {
-(*self)
}
fn invert(&self) -> Self {
<Scalar as Field>::invert(self).unwrap_or(Scalar::ZERO)
}
fn random<R: rand_core::RngCore + rand_core::CryptoRng>(rng: &mut R) -> Self {
<Scalar as Field>::random(rng)
}
fn from_bytes_wide(bytes: &[u8; 64]) -> Self {
let wide = U512::from_be_slice(bytes);
<Scalar as Reduce<U512>>::reduce(wide)
}
fn to_bytes(&self) -> [u8; 32] {
self.to_bytes().into()
}
fn from_canonical_bytes(bytes: &[u8; 32]) -> Option<Self> {
let arr: &k256::FieldBytes = bytes.into();
Scalar::from_repr(*arr).into_option()
}
}
impl CurvePoint for ProjectivePoint {
type Scalar = Scalar;
const COMPRESSED_SIZE: usize = 33;
type Compressed = [u8; 33];
fn identity() -> Self {
Self::IDENTITY
}
fn generator() -> Self {
Self::GENERATOR
}
fn mul_scalar(&self, scalar: &Self::Scalar) -> Self {
self * scalar
}
fn add(&self, other: &Self) -> Self {
*self + *other
}
fn multiscalar_mul(scalars: &[Self::Scalar], points: &[Self]) -> Self {
scalars
.iter()
.zip(points.iter())
.fold(Self::IDENTITY, |acc, (s, p)| acc + p.mul_scalar(s))
}
fn compress(&self) -> Self::Compressed {
let affine = self.to_affine();
let encoded = affine.to_encoded_point(true);
let bytes = encoded.as_bytes();
match bytes.len() {
33 => {
let mut out = [0u8; 33];
out.copy_from_slice(bytes);
out
}
1 if bytes[0] == 0 => [0u8; 33],
other => unreachable!(
"k256 emitted a {}-byte compressed point; SEC1 admits only 33 (a point) or 1 (the identity)",
other
),
}
}
fn decompress(bytes: &[u8]) -> Option<Self> {
use k256::EncodedPoint;
if bytes.len() != 33 {
return None;
}
if bytes == [0u8; 33] {
return Some(Self::IDENTITY);
}
let encoded = EncodedPoint::from_bytes(bytes).ok()?;
let affine = k256::AffinePoint::from_encoded_point(&encoded);
if affine.is_some().into() {
Some(ProjectivePoint::from(affine.unwrap()))
} else {
None
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Secp256k1Curve;
impl Curve for Secp256k1Curve {
type Scalar = Scalar;
type Point = ProjectivePoint;
}
}
#[cfg(feature = "decaf377")]
pub mod decaf377 {
use super::*;
use ::decaf377::{Element, Fr};
impl CurveScalar for Fr {
fn zeroize(&mut self) {
unsafe { core::ptr::write_volatile(self, Self::zero()) };
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
fn zero() -> Self {
Fr::ZERO
}
fn one() -> Self {
Fr::ONE
}
fn from_u32(v: u32) -> Self {
Fr::from(v as u64)
}
fn add(&self, other: &Self) -> Self {
*self + *other
}
fn sub(&self, other: &Self) -> Self {
*self - *other
}
fn mul(&self, other: &Self) -> Self {
*self * *other
}
fn neg(&self) -> Self {
-(*self)
}
fn invert(&self) -> Self {
self.inverse().unwrap_or(Fr::ZERO)
}
fn random<R: rand_core::RngCore + rand_core::CryptoRng>(rng: &mut R) -> Self {
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
Fr::from_le_bytes_mod_order(&bytes)
}
fn from_bytes_wide(bytes: &[u8; 64]) -> Self {
Fr::from_le_bytes_mod_order(bytes)
}
fn to_bytes(&self) -> [u8; 32] {
Fr::to_bytes(self)
}
fn from_canonical_bytes(bytes: &[u8; 32]) -> Option<Self> {
Fr::from_bytes_checked(bytes).ok()
}
}
impl CurvePoint for Element {
type Scalar = Fr;
const COMPRESSED_SIZE: usize = 32;
type Compressed = [u8; 32];
fn identity() -> Self {
Element::IDENTITY
}
fn generator() -> Self {
Element::GENERATOR
}
fn mul_scalar(&self, scalar: &Self::Scalar) -> Self {
*self * *scalar
}
fn add(&self, other: &Self) -> Self {
*self + *other
}
fn multiscalar_mul(scalars: &[Self::Scalar], points: &[Self]) -> Self {
scalars
.iter()
.zip(points.iter())
.fold(Element::IDENTITY, |acc, (s, p)| acc + (*p * *s))
}
fn compress(&self) -> Self::Compressed {
self.vartime_compress().0
}
fn decompress(bytes: &[u8]) -> Option<Self> {
let arr: [u8; 32] = bytes.try_into().ok()?;
::decaf377::Encoding(arr).vartime_decompress().ok()
}
}
#[derive(Clone, Debug, Default)]
pub struct Decaf377Curve;
impl Curve for Decaf377Curve {
type Scalar = Fr;
type Point = Element;
}
}