use crate::fields::fp::{FieldExtensionTrait, Fp, Fr};
use crate::fields::fp2::Fp2;
use crate::groups::group::{GroupAffine, GroupError, GroupProjective, GroupTrait};
use crate::hasher::Expander;
use crypto_bigint::rand_core::CryptoRngCore;
use crypto_bigint::U256;
use num_traits::{One, Zero};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
const G2_X: Fp2 = Fp2::new(&[
Fp::new(U256::from_words([
5106727233969649389,
7440829307424791261,
4785637993704342649,
1729627375292849782,
])),
Fp::new(U256::from_words([
10945020018377822914,
17413811393473931026,
8241798111626485029,
1841571559660931130,
])),
]);
const G2_Y: Fp2 = Fp2::new(&[
Fp::new(U256::from_words([
5541340697920699818,
16416156555105522555,
5380518976772849807,
1353435754470862315,
])),
Fp::new(U256::from_words([
6173549831154472795,
13567992399387660019,
17050234209342075797,
650358724130500725,
])),
]);
pub(crate) const EPS_EXP0: Fp2 = Fp2::new(&[
Fp::new(U256::from_words([
11088870908804158781,
13226160682434769676,
5479733118184829251,
3437169660107756023,
])),
Fp::new(U256::from_words([
1613930359396748194,
3651902652079185358,
5450706350010664852,
1642095672556236320,
])),
]);
pub(crate) const EPS_EXP1: Fp2 = Fp2::new(&[
Fp::new(U256::from_words([
15876315988453495642,
15828711151707445656,
15879347695360604601,
449501266848708060,
])),
Fp::new(U256::from_words([
9427018508834943203,
2414067704922266578,
505728791003885355,
558513134835401882,
])),
]);
pub(crate) const BLS_X: Fp = Fp::new(U256::from_words([4965661367192848881, 0, 0, 0]));
pub type G2Affine = GroupAffine<2, 2, Fp2>;
pub type G2Projective = GroupProjective<2, 2, Fp2>;
impl GroupTrait<2, 2, Fp2> for G2Affine {
fn generator() -> Self {
Self {
x: G2_X,
y: G2_Y,
infinity: Choice::from(0u8),
}
}
fn endomorphism(&self) -> Self {
if self.is_zero() {
return *self;
}
let x_frob = self.x.frobenius(1);
let y_frob = self.y.frobenius(1);
let x_endo = EPS_EXP0 * x_frob;
let y_endo = EPS_EXP1 * y_frob;
tracing::trace!(?x_frob, ?y_frob, ?x_endo, ?y_endo, "G2Affine::endomorphism");
Self::new_unchecked([x_endo, y_endo]).expect("Endomorphism failed")
}
fn rand<R: CryptoRngCore>(rng: &mut R) -> Self {
Self::from(G2Projective::rand(rng))
}
fn hash_to_curve<E: Expander>(_exp: &E, _msg: &[u8]) -> Result<Self, GroupError> {
unimplemented!()
}
fn sign_message<E: Expander>(
_exp: &E,
_msg: &[u8],
_private_key: Fp2,
) -> Result<Self, GroupError> {
unimplemented!()
}
}
impl GroupTrait<2, 2, Fp2> for G2Projective {
fn generator() -> Self {
let _generator = G2Affine::generator();
Self {
x: _generator.x,
y: _generator.y,
z: Fp2::one(),
}
}
fn endomorphism(&self) -> Self {
Self::from(G2Affine::from(self).endomorphism())
}
fn rand<R: CryptoRngCore>(rng: &mut R) -> Self {
const C2: Fp = Fp::new(U256::from_words([
17887900258952609094,
8020209761171036667,
0,
0,
]));
let rando = Fp::new(Fr::rand(rng).value());
let mut tmp = Self::generator() * rando;
tracing::trace!(?rando, ?tmp, "G2Projective::rand");
tmp = tmp * C2; Self::new([tmp.x, tmp.y, tmp.z]).expect("Generator failed to make new value in torsion")
}
fn hash_to_curve<E: Expander>(_exp: &E, _msg: &[u8]) -> Result<Self, GroupError> {
unimplemented!()
}
fn sign_message<E: Expander>(
_exp: &E,
_msg: &[u8],
_private_key: Fp2,
) -> Result<Self, GroupError> {
unimplemented!()
}
}
impl G2Affine {
fn new_unchecked(v: [Fp2; 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 = <Fp2 as FieldExtensionTrait<2, 2>>::curve_constant();
tracing::trace!(?y2, ?x2, ?lhs, ?rhs, "G2Affine::new_unchecked");
lhs.ct_eq(&rhs)
};
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; 128] {
let mut res = [0u8; 128];
let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity);
let y = Fp2::conditional_select(&self.y, &Fp2::one(), self.infinity);
res[0..32].copy_from_slice(&x.0[1].to_be_bytes()[..]);
res[32..64].copy_from_slice(&x.0[0].to_be_bytes()[..]);
res[64..96].copy_from_slice(&y.0[1].to_be_bytes()[..]);
res[96..128].copy_from_slice(&y.0[0].to_be_bytes()[..]);
res[0] |= u8::conditional_select(&0u8, &(1u8 << 7), self.infinity);
res
}
pub fn from_be_bytes(bytes: &[u8; 128]) -> CtOption<G2Projective> {
Self::from_be_bytes_unchecked(bytes).and_then(|p| {
let infinity_flag = bool::from(p.infinity);
if infinity_flag {
CtOption::new(G2Projective::zero(), Choice::from(1u8))
} else {
match G2Projective::new([p.x, p.y, Fp2::one()]) {
Ok(valid) => CtOption::new(valid, Choice::from(1u8)),
Err(_) => CtOption::new(G2Projective::zero(), Choice::from(0u8)),
}
}
})
}
fn from_be_bytes_unchecked(bytes: &[u8; 128]) -> CtOption<Self> {
let infinity_flag = Choice::from((bytes[0] >> 7) & 1);
let xc1 = {
let mut tmp = [0u8; 32];
tmp.copy_from_slice(&bytes[0..32]);
tmp[0] &= 0b0111_1111;
Fp::from_be_bytes(&tmp)
};
let xc0 = {
let mut tmp = [0u8; 32];
tmp.copy_from_slice(&bytes[32..64]);
Fp::from_be_bytes(&tmp)
};
let yc1 = {
let mut tmp = [0u8; 32];
tmp.copy_from_slice(&bytes[64..96]);
Fp::from_be_bytes(&tmp)
};
let yc0 = {
let mut tmp = [0u8; 32];
tmp.copy_from_slice(&bytes[96..128]);
Fp::from_be_bytes(&tmp)
};
xc1.and_then(|xc1| {
xc0.and_then(|xc0| {
yc1.and_then(|yc1| {
yc0.and_then(|yc0| {
let x = Fp2::new(&[xc0, xc1]);
let y = Fp2::new(&[yc0, yc1]);
let p = G2Affine::conditional_select(
&G2Affine {
x,
y,
infinity: infinity_flag,
},
&G2Affine::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 G2Projective {
pub fn new(v: [Fp2; 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] * <Fp2 as FieldExtensionTrait<2, 2>>::curve_constant();
tracing::trace!(?y2, ?x2, ?z2, ?lhs, ?rhs, "G2Projective::new");
lhs.ct_eq(&rhs) | Choice::from(v[2].is_zero() as u8)
};
let is_torsion_free = {
let tmp = G2Projective {
x: v[0],
y: v[1],
z: v[2],
};
let mut a = tmp * BLS_X; let b = a.endomorphism(); a = a + tmp; let mut rhs = b.endomorphism(); let lhs = rhs + b + a; rhs = rhs.endomorphism().double() - lhs; tracing::trace!(
?v,
?a,
?b,
?lhs,
?rhs,
"G2Projective::_g2projective_is_torsion_free"
);
Choice::from(rhs.is_zero() as u8) & is_on_curve
};
match bool::from(is_on_curve) {
true => match bool::from(is_torsion_free) {
true => Ok(Self {
x: v[0],
y: v[1],
z: v[2],
}),
_ => Err(GroupError::NotInSubgroup),
},
false => Err(GroupError::NotOnCurve),
}
}
}