use crypto_bigint::U256;
use crate::error::Error;
use crate::sm9::fields::fp::{fp_to_bytes, Fp};
use crate::sm9::fields::fp12::LineEval;
use crate::sm9::fields::fp2::{
fp2_add, fp2_inv, fp2_mul, fp2_mul_u, fp2_neg, fp2_square, fp2_sub, Fp2,
};
pub const G2X0: Fp = Fp::new(&U256::from_be_hex(
"3722755292130B08D2AAB97FD34EC120EE265948D19C17ABF9B7213BAF82D65B",
));
pub const G2X1: Fp = Fp::new(&U256::from_be_hex(
"85AEF3D078640C98597B6027B441A01FF1DD2C190F5E93C454806C11D8806141",
));
pub const G2Y0: Fp = Fp::new(&U256::from_be_hex(
"A7CF28D519BE3DA65F3170153D278FF247EFBA98A71A08116215BBA5C999A7C7",
));
pub const G2Y1: Fp = Fp::new(&U256::from_be_hex(
"17509B092E845C1266BA0D262CBEE6ED0736A96FA347C8BD856DC76B84EBEB96",
));
const G2B: Fp2 = Fp2 {
c0: Fp::ZERO,
c1: Fp::new(&U256::from_be_hex(
"0000000000000000000000000000000000000000000000000000000000000005",
)),
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G2Affine {
pub x: Fp2,
pub y: Fp2,
}
#[derive(Clone, Copy, Debug)]
pub struct G2Jacobian {
pub x: Fp2,
pub y: Fp2,
pub z: Fp2,
}
impl G2Jacobian {
pub const INFINITY: Self = G2Jacobian {
x: Fp2::ONE,
y: Fp2::ONE,
z: Fp2::ZERO,
};
pub fn from_affine(p: &G2Affine) -> Self {
G2Jacobian {
x: p.x,
y: p.y,
z: Fp2::ONE,
}
}
pub fn to_affine(&self) -> Result<G2Affine, Error> {
if self.is_infinity() {
return Err(Error::PointAtInfinity);
}
let z_inv = fp2_inv(&self.z).ok_or(Error::PointAtInfinity)?;
let z_inv2 = fp2_square(&z_inv);
let z_inv3 = fp2_mul(&z_inv2, &z_inv);
Ok(G2Affine {
x: fp2_mul(&self.x, &z_inv2),
y: fp2_mul(&self.y, &z_inv3),
})
}
pub fn is_infinity(&self) -> bool {
let b = self.z.to_bytes();
b.iter().all(|&v| v == 0)
}
pub fn double_with_line(&self) -> (Self, LineEval) {
let (x1, y1, z1) = (&self.x, &self.y, &self.z);
let a = fp2_square(x1); let b = fp2_square(y1); let c = fp2_square(&b); let tmp = fp2_square(&fp2_add(x1, &b));
let tmp = fp2_sub(&tmp, &a);
let tmp = fp2_sub(&tmp, &c);
let d = fp2_add(&tmp, &tmp); let e = fp2_add(&fp2_add(&a, &a), &a);
let x3 = fp2_sub(&fp2_square(&e), &fp2_add(&d, &d));
let z3 = fp2_add(&fp2_mul(y1, z1), &fp2_mul(y1, z1));
let eight_c = {
let c2 = fp2_add(&c, &c);
let c4 = fp2_add(&c2, &c2);
fp2_add(&c4, &c4)
};
let y3 = fp2_sub(&fp2_mul(&e, &fp2_sub(&d, &x3)), &eight_c);
let z1sq = fp2_square(z1);
let line = LineEval {
a: fp2_mul_u(&fp2_mul(&z3, &z1sq)), b: fp2_sub(&fp2_mul(x1, &e), &fp2_add(&b, &b)), c: fp2_neg(&fp2_mul(&e, &z1sq)), };
(
G2Jacobian {
x: x3,
y: y3,
z: z3,
},
line,
)
}
pub fn add_with_line(&self, q: &G2Affine) -> (Self, LineEval) {
let (x1, y1, z1) = (&self.x, &self.y, &self.z);
let (x2, y2) = (&q.x, &q.y);
let z1sq = fp2_square(z1);
let u2 = fp2_mul(x2, &z1sq); let s2 = fp2_mul(y2, &fp2_mul(z1, &z1sq)); let h = fp2_sub(&u2, x1);
let r = fp2_sub(&s2, y1);
let h2 = fp2_square(&h);
let h3 = fp2_mul(&h, &h2);
let x1h2 = fp2_mul(x1, &h2);
let x3 = fp2_sub(&fp2_sub(&fp2_square(&r), &h3), &fp2_add(&x1h2, &x1h2));
let y3 = fp2_sub(&fp2_mul(&r, &fp2_sub(&x1h2, &x3)), &fp2_mul(y1, &h3));
let z3 = fp2_mul(&fp2_mul(&h, z1), &Fp2::ONE);
let line = LineEval {
a: fp2_mul_u(&z3), b: fp2_sub(&fp2_mul(&fp2_mul(x1, z1), y2), &fp2_mul(x2, y1)), c: fp2_neg(&r), };
(
G2Jacobian {
x: x3,
y: y3,
z: z3,
},
line,
)
}
pub fn double(&self) -> Self {
self.double_with_line().0
}
pub fn add_jac(p: &G2Jacobian, q: &G2Jacobian) -> G2Jacobian {
if p.is_infinity() {
return *q;
}
if q.is_infinity() {
return *p;
}
let z1sq = fp2_square(&p.z);
let z2sq = fp2_square(&q.z);
let u1 = fp2_mul(&p.x, &z2sq);
let u2 = fp2_mul(&q.x, &z1sq);
let s1 = fp2_mul(&p.y, &fp2_mul(&q.z, &z2sq));
let s2 = fp2_mul(&q.y, &fp2_mul(&p.z, &z1sq));
let h = fp2_sub(&u2, &u1);
let r = fp2_sub(&s2, &s1);
if h.is_zero() {
return if r.is_zero() {
p.double()
} else {
G2Jacobian::INFINITY
};
}
let h2 = fp2_square(&h);
let h3 = fp2_mul(&h, &h2);
let u1h2 = fp2_mul(&u1, &h2);
let x3 = fp2_sub(&fp2_sub(&fp2_square(&r), &h3), &fp2_add(&u1h2, &u1h2));
let y3 = fp2_sub(&fp2_mul(&r, &fp2_sub(&u1h2, &x3)), &fp2_mul(&s1, &h3));
let z3 = fp2_mul(&fp2_mul(&h, &p.z), &q.z);
G2Jacobian {
x: x3,
y: y3,
z: z3,
}
}
pub fn scalar_mul(k: &U256, p: &G2Jacobian) -> G2Jacobian {
let mut result = G2Jacobian::INFINITY;
let addend = *p;
let mut started = false;
for byte in &k.to_be_bytes() {
for bit in (0..8).rev() {
if started {
result = result.double();
}
if (byte >> bit) & 1 == 1 {
if started {
result = G2Jacobian::add_jac(&result, &addend);
} else {
result = addend;
started = true;
}
}
}
}
result
}
pub fn scalar_mul_g2(k: &U256) -> G2Jacobian {
let g2 = G2Jacobian::from_affine(&G2Affine::generator());
Self::scalar_mul(k, &g2)
}
}
impl G2Affine {
pub fn generator() -> Self {
G2Affine {
x: Fp2 { c0: G2X0, c1: G2X1 },
y: Fp2 { c0: G2Y0, c1: G2Y1 },
}
}
pub fn is_on_curve(&self) -> bool {
let x3 = fp2_mul(&fp2_square(&self.x), &self.x);
let rhs = fp2_add(&x3, &G2B);
fp2_square(&self.y) == rhs
}
pub fn from_bytes(bytes: &[u8; 128]) -> Result<Self, Error> {
let x0: [u8; 32] = bytes[0..32].try_into().unwrap();
let x1: [u8; 32] = bytes[32..64].try_into().unwrap();
let y0: [u8; 32] = bytes[64..96].try_into().unwrap();
let y1: [u8; 32] = bytes[96..128].try_into().unwrap();
use crate::sm9::fields::fp::FIELD_MODULUS;
use crypto_bigint::subtle::ConstantTimeGreater;
for b in [&x0, &x1, &y0, &y1] {
let v = U256::from_be_slice(b);
if bool::from(v.ct_gt(&FIELD_MODULUS)) || v == FIELD_MODULUS {
return Err(Error::InvalidPublicKey);
}
}
use crate::sm9::fields::fp::fp_from_bytes as ffb;
let p = G2Affine {
x: Fp2 {
c0: ffb(&x0),
c1: ffb(&x1),
},
y: Fp2 {
c0: ffb(&y0),
c1: ffb(&y1),
},
};
if !p.is_on_curve() {
return Err(Error::InvalidPublicKey);
}
Ok(p)
}
pub fn to_bytes(&self) -> [u8; 128] {
let mut out = [0u8; 128];
out[0..32].copy_from_slice(&fp_to_bytes(&self.x.c0));
out[32..64].copy_from_slice(&fp_to_bytes(&self.x.c1));
out[64..96].copy_from_slice(&fp_to_bytes(&self.y.c0));
out[96..128].copy_from_slice(&fp_to_bytes(&self.y.c1));
out
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_g2_generator_on_curve() {
assert!(G2Affine::generator().is_on_curve());
}
#[test]
fn test_g2_double_stays_in_group() {
let g = G2Jacobian::from_affine(&G2Affine::generator());
let g2 = g.double().to_affine().unwrap();
assert!(g2.is_on_curve());
}
#[test]
fn test_g2_scalar_mul_one() {
let g2 = G2Jacobian::scalar_mul_g2(&U256::ONE).to_affine().unwrap();
assert!(g2.is_on_curve());
let x = g2.x.to_bytes();
let gx = G2Affine::generator().x.to_bytes();
assert_eq!(x, gx);
}
}