use crate::fields::fp::{FieldExtensionTrait, Fp};
use crate::groups::group::{GroupAffine, GroupError, GroupProjective, GroupTrait};
use crate::hasher::Expander;
use crate::svdw::{MapError, SvdW, SvdWTrait};
use crypto_bigint::rand_core::CryptoRngCore;
use num_traits::{One, Zero};
use std::sync::OnceLock;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
pub type G1Affine = GroupAffine<1, 1, Fp>;
pub type G1Projective = GroupProjective<1, 1, Fp>;
static BN254_SVDW: OnceLock<Result<SvdW, MapError>> = OnceLock::new();
pub fn get_bn254_svdw() -> Result<&'static SvdW, &'static MapError> {
BN254_SVDW
.get_or_init(|| SvdW::precompute_constants(Fp::ZERO, Fp::THREE))
.as_ref()
}
impl GroupTrait<1, 1, Fp> for G1Affine {
fn generator() -> Self {
Self {
x: Fp::ONE,
y: Fp::TWO,
infinity: Choice::from(0u8),
}
}
fn endomorphism(&self) -> Self {
Self::generator()
}
fn rand<R: CryptoRngCore>(rng: &mut R) -> Self {
Self::from(G1Projective::rand(rng))
}
fn hash_to_curve<E: Expander>(exp: &E, msg: &[u8]) -> Result<Self, GroupError> {
match G1Projective::hash_to_curve(exp, msg) {
Ok(d) => Ok(Self::from(d)),
Err(e) => Err(e),
}
}
fn sign_message<E: Expander>(exp: &E, msg: &[u8], private_key: Fp) -> Result<Self, GroupError> {
match G1Projective::sign_message(exp, msg, private_key) {
Ok(d) => Ok(Self::from(d)),
Err(e) => Err(e),
}
}
}
impl G1Affine {
pub fn new(v: [Fp; 2]) -> Result<Self, GroupError> {
let is_on_curve = {
let y2 = v[1].square();
let x2 = v[0].square();
let lhs = y2 - (x2 * v[0]);
let rhs = <Fp as FieldExtensionTrait<1, 1>>::curve_constant();
tracing::trace!(?y2, ?x2, ?lhs, ?rhs, "G1Affine::new");
lhs.ct_eq(&rhs)
};
tracing::trace!(?is_on_curve, "G1Affine::new");
match bool::from(is_on_curve) {
true => Ok(Self {
x: v[0],
y: v[1],
infinity: Choice::from(0u8),
}),
false => Err(GroupError::NotOnCurve),
}
}
pub fn to_be_bytes(self) -> [u8; 64] {
let mut res = [0u8; 64];
res[0..32].copy_from_slice(
&Fp::conditional_select(&self.x, &Fp::ZERO, self.infinity).to_be_bytes()[..],
);
res[32..64].copy_from_slice(
&Fp::conditional_select(&self.y, &Fp::ONE, self.infinity).to_be_bytes()[..],
);
res[0] |= u8::conditional_select(&0u8, &(1u8 << 7), self.infinity);
res
}
pub fn to_be_bytes_scrubbed(self) -> [u8; 64] {
let normal_bytes = self.to_be_bytes();
let zero_bytes = [0u8; 64];
let mut result = [0u8; 64];
for i in 0..64 {
result[i] = u8::conditional_select(&normal_bytes[i], &zero_bytes[i], self.infinity);
}
result
}
pub fn from_be_bytes(bytes: &[u8; 64]) -> CtOption<G1Projective> {
Self::from_be_bytes_unchecked(bytes).and_then(|p| {
let infinity_flag = bool::from(p.infinity);
if infinity_flag {
CtOption::new(G1Projective::zero(), Choice::from(1u8))
} else {
match G1Projective::new([p.x, p.y, Fp::ONE]) {
Ok(p) => CtOption::new(p, Choice::from(1u8)),
Err(_) => CtOption::new(G1Projective::zero(), Choice::from(0u8)),
}
}
})
}
fn from_be_bytes_unchecked(bytes: &[u8; 64]) -> CtOption<Self> {
let infinity_flag = Choice::from((bytes[0] >> 7) & 1);
let x = {
let mut tmp = [0u8; 32];
tmp.copy_from_slice(&bytes[0..32]);
tmp[0] &= 0b0111_1111; Fp::from_be_bytes(&tmp)
};
let y = {
let mut tmp = [0u8; 32];
tmp.copy_from_slice(&bytes[32..64]);
Fp::from_be_bytes(&tmp)
};
x.and_then(|x| {
y.and_then(|y| {
let p = Self::conditional_select(
&G1Affine {
x,
y,
infinity: infinity_flag,
},
&G1Affine::zero(),
infinity_flag,
);
let is_some = (!infinity_flag)
| (infinity_flag & Choice::from((x.is_zero() & y.is_one()) as u8));
CtOption::new(p, is_some)
})
})
}
}
impl GroupTrait<1, 1, Fp> for G1Projective {
fn generator() -> Self {
Self::from(G1Affine::generator())
}
fn endomorphism(&self) -> Self {
Self::generator()
}
fn rand<R: CryptoRngCore>(rng: &mut R) -> Self {
Self::generator() * <Fp as FieldExtensionTrait<1, 1>>::rand(rng)
}
fn hash_to_curve<E: Expander>(exp: &E, msg: &[u8]) -> Result<Self, GroupError> {
const COUNT: usize = 2;
const L: usize = 48;
let scalars = exp
.hash_to_field(msg, COUNT, L)
.expect("Hashing to base field failed");
tracing::trace!(?scalars, "GroupTrait::hash_to_curve");
match get_bn254_svdw() {
Ok(bn254_g1_svdw) => {
let a = G1Projective::from(
bn254_g1_svdw
.unchecked_map_to_point(scalars[0])
.expect("Failed to hash"),
);
let b = G1Projective::from(
bn254_g1_svdw
.unchecked_map_to_point(scalars[1])
.expect("Failed to hash"),
);
tracing::trace!(?a, ?b, "GroupTrait::hash_to_curve");
Ok(a + b)
}
_ => Err(GroupError::CannotHashToGroup),
}
}
fn sign_message<E: Expander>(exp: &E, msg: &[u8], private_key: Fp) -> Result<Self, GroupError> {
if let Ok(d) = Self::hash_to_curve(exp, msg) {
return Ok(d * private_key);
}
Err(GroupError::CannotHashToGroup)
}
}
impl G1Projective {
#[allow(dead_code)]
pub fn new(v: [Fp; 3]) -> Result<Self, GroupError> {
let is_on_curve = {
let y2 = v[1].square();
let x2 = v[0].square();
let z2 = v[2].square();
let lhs = y2 * v[2];
let rhs = x2 * v[0] + z2 * v[2] * <Fp as FieldExtensionTrait<1, 1>>::curve_constant();
tracing::trace!(?y2, ?x2, ?z2, ?lhs, ?rhs, "G1Projective::new");
lhs.ct_eq(&rhs) | Choice::from(v[2].is_zero() as u8)
};
tracing::trace!(?is_on_curve, "G1Projective::new");
match bool::from(is_on_curve) {
true => Ok(Self {
x: v[0],
y: v[1],
z: v[2],
}),
false => Err(GroupError::NotOnCurve),
}
}
}
impl<'a> From<&'a [Fp; 2]> for G1Projective {
fn from(value: &'a [Fp; 2]) -> Self {
G1Affine::new(*value)
.expect("Conversion to affine failed")
.into()
}
}
impl From<[Fp; 2]> for G1Projective {
fn from(value: [Fp; 2]) -> Self {
G1Projective::from(&value)
}
}