use ff::Field;
use group::{Curve, Group};
#[cfg(feature = "dev-curves")]
use midnight_curves::bn256;
use midnight_curves::{
curve25519::{Curve25519, Curve25519Affine, CURVE_A as CURVE25519_A, CURVE_D as CURVE25519_D},
CurveAffine, Fq as BlsScalar, JubjubAffine, JubjubExtended, JubjubSubgroup,
};
use crate::CircuitField;
pub trait CircuitCurve: Curve + Default {
type Base: CircuitField;
type ScalarField: CircuitField;
type CryptographicGroup: Group<Scalar = Self::ScalarField> + Into<Self>;
const COFACTOR: u128 = 1;
const NUM_BITS_SUBGROUP: u32;
fn coordinates(&self) -> Option<(Self::Base, Self::Base)>;
fn from_xy(x: Self::Base, y: Self::Base) -> Option<Self>;
fn into_subgroup(self) -> Self::CryptographicGroup;
}
pub trait WeierstrassCurve: CircuitCurve {
const A: Self::Base;
const B: Self::Base;
fn has_cubic_endomorphism() -> bool {
true
}
fn base_zeta() -> Self::Base;
fn scalar_zeta() -> Self::ScalarField;
}
pub trait EdwardsCurve: CircuitCurve {
const A: Self::Base;
const D: Self::Base;
}
impl CircuitCurve for JubjubExtended {
type Base = BlsScalar;
type ScalarField = <Self as Group>::Scalar;
type CryptographicGroup = JubjubSubgroup;
const COFACTOR: u128 = 8;
const NUM_BITS_SUBGROUP: u32 = 252;
fn coordinates(&self) -> Option<(Self::Base, Self::Base)> {
Some((self.to_affine().get_u(), self.to_affine().get_v()))
}
fn from_xy(x: Self::Base, y: Self::Base) -> Option<Self> {
let mut bytes = y.to_bytes_le();
let x_sign = x.to_bytes_le()[0] << 7;
bytes[31] |= x_sign;
let point = JubjubAffine::from_bytes(bytes).into_option()?;
if point.get_v() == y {
Some(point.into())
} else {
None
}
}
fn into_subgroup(self) -> Self::CryptographicGroup {
<JubjubExtended as CofactorGroup>::into_subgroup(self)
.expect("Point should be part of the subgroup")
}
}
impl EdwardsCurve for JubjubExtended {
const A: Self::Base = Self::Base::from_raw([
0xffff_ffff_0000_0000,
0x53bd_a402_fffe_5bfe,
0x3339_d808_09a1_d805,
0x73ed_a753_299d_7d48,
]);
const D: Self::Base = Self::Base::from_raw([
0x0106_5fd6_d634_3eb1,
0x292d_7f6d_3757_9d26,
0xf5fd_9207_e6bd_7fd4,
0x2a93_18e7_4bfa_2b48,
]);
}
use midnight_curves::curve25519::{Curve25519Subgroup, Fp as Curve25519Base};
impl CircuitCurve for Curve25519 {
type Base = Curve25519Base;
type ScalarField = <Self as Group>::Scalar;
type CryptographicGroup = Curve25519Subgroup;
const COFACTOR: u128 = 8;
const NUM_BITS_SUBGROUP: u32 = 253;
fn coordinates(&self) -> Option<(Self::Base, Self::Base)> {
let affine = Curve25519Affine::from_edwards(self.0);
Some((*affine.x(), *affine.y()))
}
fn from_xy(x: Self::Base, y: Self::Base) -> Option<Self> {
let affine = Curve25519Affine::from_xy(x, y)?;
Some(Curve25519::from(affine))
}
fn into_subgroup(self) -> Self::CryptographicGroup {
Curve25519Subgroup::from_edwards(self.0).expect("point must be in the prime-order subgroup")
}
}
impl EdwardsCurve for Curve25519 {
const A: Self::Base = CURVE25519_A;
const D: Self::Base = CURVE25519_D;
}
use midnight_curves::k256::{Fp as K256Fp, K256Affine, K256};
impl CircuitCurve for K256 {
type Base = K256Fp;
type ScalarField = <Self as Group>::Scalar;
type CryptographicGroup = K256;
const NUM_BITS_SUBGROUP: u32 = 256;
fn coordinates(&self) -> Option<(Self::Base, Self::Base)> {
if self.is_identity().into() {
return None;
}
let affine = self.to_affine();
Some((affine.x(), affine.y()))
}
fn from_xy(x: Self::Base, y: Self::Base) -> Option<Self> {
K256Affine::from_xy(x, y).map(|p| p.into())
}
fn into_subgroup(self) -> Self::CryptographicGroup {
self
}
}
impl WeierstrassCurve for K256 {
const A: Self::Base = K256Fp::ZERO;
const B: Self::Base = K256Fp::from_u64(7);
fn base_zeta() -> Self::Base {
K256::base_zeta()
}
fn scalar_zeta() -> Self::ScalarField {
K256::scalar_zeta()
}
}
use midnight_curves::p256::{
affine_from_xy, affine_x, affine_y, Fp as P256Fp, CURVE_A, CURVE_B, P256,
};
impl CircuitCurve for P256 {
type Base = P256Fp;
type ScalarField = <Self as Group>::Scalar;
type CryptographicGroup = P256;
const NUM_BITS_SUBGROUP: u32 = 256;
fn coordinates(&self) -> Option<(Self::Base, Self::Base)> {
if bool::from(self.is_identity()) {
return Some((P256Fp::ZERO, P256Fp::ZERO));
}
let affine = self.to_affine();
Some((affine_x(&affine), affine_y(&affine)))
}
fn from_xy(x: Self::Base, y: Self::Base) -> Option<Self> {
affine_from_xy(x, y).map(Into::into)
}
fn into_subgroup(self) -> Self::CryptographicGroup {
self
}
}
impl WeierstrassCurve for P256 {
const A: Self::Base = CURVE_A;
const B: Self::Base = CURVE_B;
fn has_cubic_endomorphism() -> bool {
false
}
fn base_zeta() -> Self::Base {
unimplemented!("P256 does not have a cubic endomorphism")
}
fn scalar_zeta() -> Self::ScalarField {
unimplemented!("P256 does not have a cubic endomorphism")
}
}
use group::cofactor::CofactorGroup;
use midnight_curves::{Fp as BlsBase, G1Affine, G1Projective};
impl CircuitCurve for G1Projective {
type Base = BlsBase;
type ScalarField = <Self as Group>::Scalar;
type CryptographicGroup = G1Projective;
const COFACTOR: u128 = 0x396c8c005555e1568c00aaab0000aaab;
const NUM_BITS_SUBGROUP: u32 = 255;
fn coordinates(&self) -> Option<(Self::Base, Self::Base)> {
if self.is_identity().into() {
return None;
}
let affine = self.to_affine();
Some((affine.x(), affine.y()))
}
fn from_xy(x: Self::Base, y: Self::Base) -> Option<Self> {
let a = <Self as WeierstrassCurve>::A;
let b = <Self as WeierstrassCurve>::B;
if y.square() != x.square() * x + a * x + b {
return None;
}
<G1Affine as CurveAffine>::from_xy(x, y).into_option().map(|p| p.into())
}
fn into_subgroup(self) -> Self::CryptographicGroup {
self
}
}
impl WeierstrassCurve for G1Projective {
const A: Self::Base = midnight_curves::A;
const B: Self::Base = midnight_curves::B;
fn base_zeta() -> Self::Base {
<BlsBase as ff::WithSmallOrderMulGroup<3>>::ZETA
}
fn scalar_zeta() -> Self::ScalarField {
<BlsScalar as ff::WithSmallOrderMulGroup<3>>::ZETA
}
}
#[cfg(feature = "dev-curves")]
impl CircuitCurve for bn256::G1 {
type Base = bn256::Fq;
type ScalarField = <Self as Group>::Scalar;
type CryptographicGroup = bn256::G1;
const NUM_BITS_SUBGROUP: u32 = 254;
fn coordinates(&self) -> Option<(Self::Base, Self::Base)> {
if self.is_identity().into() {
return None;
}
let affine = self.to_affine();
Some((affine.x, affine.y))
}
fn from_xy(x: Self::Base, y: Self::Base) -> Option<Self> {
let a = <Self as WeierstrassCurve>::A;
let b = <Self as WeierstrassCurve>::B;
if y.square() != x.square() * x + a * x + b {
return None;
}
<bn256::G1Affine as CurveAffine>::from_xy(x, y).into_option().map(|p| p.into())
}
fn into_subgroup(self) -> Self::CryptographicGroup {
self
}
}
#[cfg(feature = "dev-curves")]
impl WeierstrassCurve for bn256::G1 {
const A: Self::Base = bn256::Fq::from_raw([0, 0, 0, 0]);
const B: Self::Base = bn256::Fq::from_raw([3, 0, 0, 0]);
fn base_zeta() -> Self::Base {
<bn256::Fq as ff::WithSmallOrderMulGroup<3>>::ZETA
}
fn scalar_zeta() -> Self::ScalarField {
<bn256::Fr as ff::WithSmallOrderMulGroup<3>>::ZETA
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_identity_coordinates() {
fn check<C: CircuitCurve>(has_coordinates: bool) {
assert_eq!(C::identity().coordinates().is_some(), has_coordinates);
}
check::<JubjubExtended>(true);
check::<Curve25519>(true);
check::<K256>(false);
check::<G1Projective>(false);
#[cfg(feature = "dev-curves")]
check::<bn256::G1>(false);
}
#[test]
fn test_identity_from_xy() {
fn check<C: CircuitCurve>(identity_on_curve: bool) {
let (x, y) = C::identity().coordinates().unwrap_or((C::Base::ZERO, C::Base::ZERO));
match C::from_xy(x, y) {
Some(p) => assert!(identity_on_curve && bool::from(p.is_identity())),
None => assert!(!identity_on_curve),
}
}
check::<JubjubExtended>(true);
check::<Curve25519>(true);
check::<K256>(false);
check::<G1Projective>(false);
#[cfg(feature = "dev-curves")]
check::<bn256::G1>(false);
}
}