use crate::fields::fp::{FieldExtensionTrait, Fp};
use crate::hasher::Expander;
use crypto_bigint::rand_core::CryptoRngCore;
use crypto_bigint::subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
use std::ops::{Add, Mul, Neg, Sub};
#[derive(Debug, Copy, Clone)]
pub enum GroupError {
NotOnCurve,
NotInSubgroup,
CannotHashToGroup,
DecodeError,
}
pub trait GroupTrait<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>>:
Sized
+ Copy
+ Clone
+ std::fmt::Debug
+ Neg
+ ConstantTimeEq
+ ConditionallySelectable
+ PartialEq
+ Default
{
fn generator() -> Self;
fn endomorphism(&self) -> Self;
fn rand<R: CryptoRngCore>(rng: &mut R) -> Self;
fn hash_to_curve<E: Expander>(exp: &E, msg: &[u8]) -> Result<Self, GroupError>;
fn sign_message<E: Expander>(exp: &E, msg: &[u8], private_key: F) -> Result<Self, GroupError>;
}
#[derive(Copy, Clone, Debug)]
pub struct GroupAffine<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> {
pub(crate) x: F,
pub(crate) y: F,
pub(crate) infinity: Choice,
}
impl<'a, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Neg
for &'a GroupAffine<D, N, F>
{
type Output = GroupAffine<D, N, F>;
#[inline]
fn neg(self) -> Self::Output {
Self::Output {
x: self.x,
y: F::conditional_select(&-self.y, &F::one(), self.infinity),
infinity: self.infinity,
}
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Neg for GroupAffine<D, N, F> {
type Output = GroupAffine<D, N, F>;
#[inline]
fn neg(self) -> Self::Output {
-&self
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> ConstantTimeEq
for GroupAffine<D, N, F>
{
fn ct_eq(&self, other: &Self) -> Choice {
(self.infinity & other.infinity)
| ((!self.infinity)
& (!other.infinity)
& self.x.ct_eq(&other.x)
& self.y.ct_eq(&other.y))
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> ConditionallySelectable
for GroupAffine<D, N, F>
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
x: F::conditional_select(&a.x, &b.x, choice),
y: F::conditional_select(&a.y, &b.y, choice),
infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice),
}
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> PartialEq
for GroupAffine<D, N, F>
{
#[inline]
fn eq(&self, other: &Self) -> bool {
bool::from(self.ct_eq(other))
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> GroupAffine<D, N, F> {
pub(crate) fn zero() -> Self {
Self {
x: F::zero(),
y: F::one(),
infinity: Choice::from(1u8),
}
}
#[inline(always)]
pub(crate) fn is_zero(&self) -> bool {
bool::from(self.infinity)
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Default
for GroupAffine<D, N, F>
{
fn default() -> Self {
Self::zero()
}
}
#[derive(Copy, Clone, Debug)]
pub struct GroupProjective<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> {
pub(crate) x: F,
pub(crate) y: F,
pub(crate) z: F,
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> GroupProjective<D, N, F> {
pub fn zero() -> Self {
Self {
x: F::zero(),
y: F::one(),
z: F::zero(),
}
}
#[inline(always)]
pub fn is_zero(&self) -> bool {
self.z.is_zero()
}
pub fn double(&self) -> Self {
let t0 = self.y * self.y;
let z3 = t0 + t0;
let z3 = z3 + z3;
tracing::trace!(?t0, ?z3, "GroupProjective::double 1");
let z3 = z3 + z3;
let t1 = self.y * self.z;
let t2 = self.z * self.z;
tracing::trace!(?z3, ?t1, ?t2, "GroupProjective::double 2");
let t2 = F::from(3) * F::curve_constant() * t2;
let x3 = t2 * z3;
let y3 = t0 + t2;
tracing::trace!(?t2, ?x3, ?y3, "GroupProjective::double 3");
let z3 = t1 * z3;
let t1 = t2 + t2;
let t2 = t1 + t2;
tracing::trace!(?z3, ?t1, ?t2, "GroupProjective::double 3");
let t0 = t0 - t2;
let y3 = t0 * y3;
let y3 = x3 + y3;
tracing::trace!(?t0, ?y3, "GroupProjective::double 4");
let t1 = self.x * self.y;
let x3 = t0 * t1;
let x3 = x3 + x3;
tracing::trace!(?t1, ?x3, "GroupProjective::double 5");
Self::conditional_select(
&Self {
x: x3,
y: y3,
z: z3,
},
&Self::zero(),
Choice::from(self.is_zero() as u8),
)
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Default
for GroupProjective<D, N, F>
{
fn default() -> Self {
Self::zero()
}
}
impl<'a, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Neg
for &'a GroupProjective<D, N, F>
{
type Output = GroupProjective<D, N, F>;
#[inline]
fn neg(self) -> Self::Output {
Self::Output {
x: self.x,
y: -self.y,
z: self.z,
}
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Neg
for GroupProjective<D, N, F>
{
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
-&self
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> ConstantTimeEq
for GroupProjective<D, N, F>
{
fn ct_eq(&self, other: &Self) -> Choice {
let x0 = self.x * other.z;
let x1 = other.x * self.z;
let y0 = self.y * other.z;
let y1 = other.y * self.z;
tracing::trace!(?x0, ?x1, ?y0, ?y1, "GroupProjective::ct_eq");
let i_am_zero = self.z.is_zero();
let you_are_zero = other.z.is_zero();
let decision = (i_am_zero & you_are_zero) | ((!i_am_zero) & (!you_are_zero) & bool::from(x0.ct_eq(&x1)) & bool::from(y0.ct_eq
(&y1)));
Choice::from(decision as u8)
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> ConditionallySelectable
for GroupProjective<D, N, F>
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
x: F::conditional_select(&a.x, &b.x, choice),
y: F::conditional_select(&a.y, &b.y, choice),
z: F::conditional_select(&a.z, &b.z, choice),
}
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> PartialEq
for GroupProjective<D, N, F>
{
#[inline]
fn eq(&self, other: &Self) -> bool {
bool::from(self.ct_eq(other))
}
}
impl<'a, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>>
From<&'a GroupProjective<D, N, F>> for GroupAffine<D, N, F>
{
fn from(arg: &'a GroupProjective<D, N, F>) -> Self {
let inverse = arg.z.inv(); let x = arg.x * inverse;
let y = arg.y * inverse;
tracing::trace!(?x, ?y, "GroupAffine::from(GroupProjective)");
GroupAffine::conditional_select(
&GroupAffine {
x,
y,
infinity: Choice::from(0u8),
},
&GroupAffine::zero(),
Choice::from(inverse.is_zero() as u8),
)
}
}
impl<const D: usize, const N: usize, F> From<GroupProjective<D, N, F>> for GroupAffine<D, N, F>
where
F: FieldExtensionTrait<D, N>,
{
fn from(value: GroupProjective<D, N, F>) -> GroupAffine<D, N, F> {
GroupAffine::from(&value)
}
}
impl<'a, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>>
From<&'a GroupAffine<D, N, F>> for GroupProjective<D, N, F>
{
fn from(value: &'a GroupAffine<D, N, F>) -> Self {
Self {
x: value.x,
y: value.y,
z: F::conditional_select(&F::one(), &F::zero(), value.infinity),
}
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> From<GroupAffine<D, N, F>>
for GroupProjective<D, N, F>
{
fn from(value: GroupAffine<D, N, F>) -> Self {
GroupProjective::from(&value)
}
}
impl<'a, 'b, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>>
Add<&'b GroupProjective<D, N, F>> for &'a GroupProjective<D, N, F>
{
type Output = GroupProjective<D, N, F>;
#[inline]
fn add(self, other: &'b GroupProjective<D, N, F>) -> Self::Output {
let t0 = self.x * other.x;
let t1 = self.y * other.y;
let t2 = self.z * other.z;
tracing::trace!(?t0, ?t1, ?t1, "GroupProjective::add 1");
let t3 = self.x + self.y;
let t4 = other.x + other.y;
let t3 = t3 * t4;
tracing::trace!(?t3, ?t4, "GroupProjective::add 2");
let t4 = t0 + t1;
let t3 = t3 - t4;
let t4 = self.y + self.z;
tracing::trace!(?t3, ?t4, "GroupProjective::add 3");
let x3 = other.y + other.z;
let t4 = t4 * x3;
let x3 = t1 + t2;
tracing::trace!(?x3, ?t4, "GroupProjective::add 4");
let t4 = t4 - x3;
let x3 = self.x + self.z;
let y3 = other.x + other.z;
tracing::trace!(?t4, ?x3, ?y3, "GroupProjective::add 5");
let x3 = x3 * y3;
let y3 = t0 + t2;
let y3 = x3 - y3;
tracing::trace!(?x3, ?y3, "GroupProjective::add 6");
let x3 = t0 + t0;
let t0 = x3 + t0;
let t2 = F::from(3) * F::curve_constant() * t2;
let z3 = t1 + t2;
let t1 = t1 - t2;
let y3 = F::from(3) * F::curve_constant() * y3;
tracing::trace!(?x3, ?t0, ?t2, ?z3, ?t1, ?y3, "GroupProjective::add 7");
let x3 = t4 * y3;
let t2 = t3 * t1;
let x3 = t2 - x3;
let y3 = y3 * t0;
let t1 = t1 * z3;
let y3 = t1 + y3;
let t0 = t0 * t3;
let z3 = z3 * t4;
let z3 = z3 + t0;
tracing::trace!(?x3, ?t2, ?y3, ?t1, ?t0, ?z3, "GroupProjective::add 8");
Self::Output {
x: x3,
y: y3,
z: z3,
}
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Add<GroupProjective<D, N, F>>
for GroupProjective<D, N, F>
{
type Output = Self;
#[inline]
fn add(self, rhs: GroupProjective<D, N, F>) -> Self::Output {
&self + &rhs
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl<'a, 'b, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>>
Sub<&'b GroupProjective<D, N, F>> for &'a GroupProjective<D, N, F>
{
type Output = GroupProjective<D, N, F>;
#[inline]
fn sub(self, other: &'b GroupProjective<D, N, F>) -> Self::Output {
self + &(-other)
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Sub<GroupProjective<D, N, F>>
for GroupProjective<D, N, F>
{
type Output = Self;
#[inline]
fn sub(self, rhs: GroupProjective<D, N, F>) -> Self::Output {
&self - &rhs
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl<'a, 'b, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Mul<&'b Fp>
for &'a GroupProjective<D, N, F>
{
type Output = GroupProjective<D, N, F>;
fn mul(self, other: &'b Fp) -> Self::Output {
let (np, nm) = other.compute_naf();
let mut res = Self::Output::zero();
for i in (0..256).rev() {
res = res.double();
let np_bit = np.bit(i).into();
let nm_bit = nm.bit(i).into();
if np_bit {
res = &res + self;
} else if nm_bit {
res = &res - self;
}
}
res
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Mul<Fp>
for GroupProjective<D, N, F>
{
type Output = Self;
#[inline]
fn mul(self, rhs: Fp) -> Self::Output {
&self * &rhs
}
}