use crate::sm9::fields::fp::Fp;
use crate::sm9::fields::fp2::{
fp2_add, fp2_frobenius, fp2_inv, fp2_mul, fp2_mul_u, fp2_neg, fp2_square, fp2_sub, Fp2,
};
use crypto_bigint::U256;
use subtle::{Choice, ConditionallySelectable};
const FROB_V1_0: Fp2 = Fp2 {
c0: Fp::new(&U256::from_be_hex(
"0000000000000000F300000002A3A6F2780272354F8B78F4D5FC11967BE65334",
)),
c1: Fp::ZERO,
};
const FROB_V1_1: Fp2 = Fp2 {
c0: Fp::new(&U256::from_be_hex(
"0000000000000000F300000002A3A6F2780272354F8B78F4D5FC11967BE65333",
)),
c1: Fp::ZERO,
};
const FROB_W1: Fp2 = Fp2 {
c0: Fp::new(&U256::from_be_hex(
"3F23EA58E5720BDB843C6CFA9C08674947C5C86E0DDD04EDA91D8354377B698B",
)),
c1: Fp::ZERO,
};
pub const G2_FROB_X1: Fp2 = FROB_V1_0;
pub const G2_FROB_Y1: Fp2 = Fp2 {
c0: Fp::new(&U256::from_be_hex(
"6C648DE5DC0A3F2CF55ACC93EE0BAF159F9D411806DC5177F5B21FD3DA24D011",
)),
c1: Fp::ZERO,
};
pub const G2_FROB_X2: Fp2 = Fp2 {
c0: Fp::new(&U256::from_be_hex(
"0000000000000000F300000002A3A6F2780272354F8B78F4D5FC11967BE65333",
)),
c1: Fp::ZERO,
};
pub const G2_FROB_Y2_IS_NEG_ONE: bool = true;
pub const G2_FROB_X1_INV: Fp2 = Fp2 {
c0: Fp::new(&U256::from_be_hex(
"B640000002A3A6F0E303AB4FF2EB2052A9F02115CAEF75E70F738991676AF24A",
)),
c1: Fp::ZERO,
};
pub const G2_FROB_Y1_INV: Fp2 = Fp2 {
c0: Fp::new(&U256::from_be_hex(
"49DB721A269967C4E0A8DEBC0783182F82555233139E9D63EFBD7B54092C756C",
)),
c1: Fp::ZERO,
};
pub const G2_FROB_X2_INV: Fp2 = Fp2 {
c0: Fp::new(&U256::from_be_hex(
"B640000002A3A6F0E303AB4FF2EB2052A9F02115CAEF75E70F738991676AF249",
)),
c1: Fp::ZERO,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Fp6 {
pub c0: Fp2,
pub c1: Fp2,
pub c2: Fp2,
}
impl ConditionallySelectable for Fp6 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Fp6 {
c0: Fp2::conditional_select(&a.c0, &b.c0, choice),
c1: Fp2::conditional_select(&a.c1, &b.c1, choice),
c2: Fp2::conditional_select(&a.c2, &b.c2, choice),
}
}
}
impl Fp6 {
pub const ZERO: Self = Fp6 {
c0: Fp2::ZERO,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
};
pub const ONE: Self = Fp6 {
c0: Fp2::ONE,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
};
}
#[inline]
pub fn fp6_add(a: &Fp6, b: &Fp6) -> Fp6 {
Fp6 {
c0: fp2_add(&a.c0, &b.c0),
c1: fp2_add(&a.c1, &b.c1),
c2: fp2_add(&a.c2, &b.c2),
}
}
#[inline]
pub fn fp6_sub(a: &Fp6, b: &Fp6) -> Fp6 {
Fp6 {
c0: fp2_sub(&a.c0, &b.c0),
c1: fp2_sub(&a.c1, &b.c1),
c2: fp2_sub(&a.c2, &b.c2),
}
}
#[inline]
pub fn fp6_neg(a: &Fp6) -> Fp6 {
Fp6 {
c0: fp2_neg(&a.c0),
c1: fp2_neg(&a.c1),
c2: fp2_neg(&a.c2),
}
}
pub fn fp6_mul(a: &Fp6, b: &Fp6) -> Fp6 {
let t0 = fp2_mul(&a.c0, &b.c0);
let t1 = fp2_mul(&a.c1, &b.c1);
let t2 = fp2_mul(&a.c2, &b.c2);
let a01 = fp2_add(&a.c0, &a.c1);
let b01 = fp2_add(&b.c0, &b.c1);
let a12 = fp2_add(&a.c1, &a.c2);
let b12 = fp2_add(&b.c1, &b.c2);
let a02 = fp2_add(&a.c0, &a.c2);
let b02 = fp2_add(&b.c0, &b.c2);
let m01 = fp2_mul(&a01, &b01); let m12 = fp2_mul(&a12, &b12); let m02 = fp2_mul(&a02, &b02);
let c01 = fp2_sub(&fp2_sub(&m01, &t0), &t1); let c12 = fp2_sub(&fp2_sub(&m12, &t1), &t2); let c02 = fp2_sub(&fp2_sub(&m02, &t0), &t2);
let c0_new = fp2_add(&t0, &fp2_mul_u(&c12));
let c1_new = fp2_add(&c01, &fp2_mul_u(&t2));
let c2_new = fp2_add(&c02, &t1);
Fp6 {
c0: c0_new,
c1: c1_new,
c2: c2_new,
}
}
pub fn fp6_square(a: &Fp6) -> Fp6 {
fp6_mul(a, a)
}
#[inline]
pub fn fp6_mul_v(a: &Fp6) -> Fp6 {
Fp6 {
c0: fp2_mul_u(&a.c2),
c1: a.c0,
c2: a.c1,
}
}
#[inline]
pub fn fp6_mul_fp2(a: &Fp6, b: &Fp2) -> Fp6 {
Fp6 {
c0: fp2_mul(&a.c0, b),
c1: fp2_mul(&a.c1, b),
c2: fp2_mul(&a.c2, b),
}
}
pub fn fp6_inv(a: &Fp6) -> Option<Fp6> {
let t0 = fp2_mul(&a.c0, &a.c0);
let t1 = fp2_mul(&a.c1, &a.c1);
let t2 = fp2_mul(&a.c2, &a.c2);
let t3 = fp2_mul(&a.c0, &a.c1);
let t4 = fp2_mul(&a.c0, &a.c2);
let t5 = fp2_mul(&a.c1, &a.c2);
let ca = fp2_sub(&t0, &fp2_mul_u(&t5)); let cb = fp2_sub(&fp2_mul_u(&t2), &t3); let cc = fp2_sub(&t1, &t4);
let t_a1cc = fp2_mul(&a.c1, &cc);
let t_a2cb = fp2_mul(&a.c2, &cb);
let inner = fp2_add(&t_a1cc, &t_a2cb);
let norm = fp2_add(&fp2_mul(&a.c0, &ca), &fp2_mul_u(&inner));
let norm_inv = fp2_inv(&norm)?;
Some(Fp6 {
c0: fp2_mul(&ca, &norm_inv),
c1: fp2_mul(&cb, &norm_inv),
c2: fp2_mul(&cc, &norm_inv),
})
}
pub fn fp6_frobenius_p(a: &Fp6) -> Fp6 {
Fp6 {
c0: fp2_frobenius(&a.c0),
c1: fp2_mul(&fp2_frobenius(&a.c1), &FROB_V1_0),
c2: fp2_mul(&fp2_frobenius(&a.c2), &FROB_V1_1),
}
}
pub fn fp6_frobenius_p2(a: &Fp6) -> Fp6 {
fp6_frobenius_p(&fp6_frobenius_p(a))
}
pub fn fp6_frobenius_p3(a: &Fp6) -> Fp6 {
fp6_frobenius_p(&fp6_frobenius_p2(a))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Fp12 {
pub c0: Fp6,
pub c1: Fp6,
}
impl ConditionallySelectable for Fp12 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Fp12 {
c0: Fp6::conditional_select(&a.c0, &b.c0, choice),
c1: Fp6::conditional_select(&a.c1, &b.c1, choice),
}
}
}
impl Fp12 {
pub const ZERO: Self = Fp12 {
c0: Fp6::ZERO,
c1: Fp6::ZERO,
};
pub const ONE: Self = Fp12 {
c0: Fp6::ONE,
c1: Fp6::ZERO,
};
}
#[inline]
pub fn fp12_add(a: &Fp12, b: &Fp12) -> Fp12 {
Fp12 {
c0: fp6_add(&a.c0, &b.c0),
c1: fp6_add(&a.c1, &b.c1),
}
}
#[inline]
pub fn fp12_sub(a: &Fp12, b: &Fp12) -> Fp12 {
Fp12 {
c0: fp6_sub(&a.c0, &b.c0),
c1: fp6_sub(&a.c1, &b.c1),
}
}
#[inline]
pub fn fp12_neg(a: &Fp12) -> Fp12 {
Fp12 {
c0: fp6_neg(&a.c0),
c1: fp6_neg(&a.c1),
}
}
pub fn fp12_mul(a: &Fp12, b: &Fp12) -> Fp12 {
let t0 = fp6_mul(&a.c0, &b.c0);
let t1 = fp6_mul(&a.c1, &b.c1);
let c0 = fp6_add(&t0, &fp6_mul_v(&t1));
let a01 = fp6_add(&a.c0, &a.c1);
let b01 = fp6_add(&b.c0, &b.c1);
let c1 = fp6_sub(&fp6_sub(&fp6_mul(&a01, &b01), &t0), &t1);
Fp12 { c0, c1 }
}
pub fn fp12_square(a: &Fp12) -> Fp12 {
fp12_mul(a, a)
}
pub fn fp12_cyclotomic_square(a: &Fp12) -> Fp12 {
let f0 = a.c0.c0;
let f1 = a.c1.c0;
let f2 = a.c0.c1;
let f3 = a.c1.c1;
let f4 = a.c0.c2;
let f5 = a.c1.c2;
let (t0, t1) = fp2_pair_square(&f0, &f1);
let (t2, t3) = fp2_pair_square(&f4, &f2); let (t4, t5) = fp2_pair_square(&f3, &f5);
let g0 = fp2_sub(&fp2_add(&fp2_add(&t0, &t0), &t0), &fp2_add(&f0, &f0));
let g1 = fp2_add(&fp2_add(&t1, &t1), &fp2_add(&t1, &fp2_add(&f1, &f1)));
let g2 = fp2_add(&fp2_add(&t2, &t2), &fp2_add(&t2, &fp2_add(&f4, &f4)));
let g3 = fp2_sub(&fp2_add(&fp2_add(&t3, &t3), &t3), &fp2_add(&f2, &f2));
let g4 = fp2_sub(&fp2_add(&fp2_add(&t4, &t4), &t4), &fp2_add(&f3, &f3));
let g5 = fp2_add(&fp2_add(&t5, &t5), &fp2_add(&t5, &fp2_add(&f5, &f5)));
Fp12 {
c0: Fp6 {
c0: g0,
c1: g3,
c2: g4,
},
c1: Fp6 {
c0: g1,
c1: g2,
c2: g5,
},
}
}
fn fp2_pair_square(a: &Fp2, b: &Fp2) -> (Fp2, Fp2) {
let a2 = fp2_square(a);
let b2 = fp2_square(b);
let ab = fp2_mul(a, b);
let new_a = fp2_add(&a2, &fp2_mul_u(&b2));
let new_b = fp2_add(&ab, &ab);
(new_a, new_b)
}
pub fn fp12_inv(a: &Fp12) -> Option<Fp12> {
let norm0 = fp6_mul(&a.c0, &a.c0); let norm1 = fp6_mul_v(&fp6_mul(&a.c1, &a.c1)); let norm = fp6_sub(&norm0, &norm1); let norm_inv = fp6_inv(&norm)?;
Some(Fp12 {
c0: fp6_mul(&a.c0, &norm_inv),
c1: fp6_neg(&fp6_mul(&a.c1, &norm_inv)),
})
}
pub fn fp12_frobenius_p(a: &Fp12) -> Fp12 {
Fp12 {
c0: fp6_frobenius_p(&a.c0),
c1: fp6_mul_fp2(&fp6_frobenius_p(&a.c1), &FROB_W1),
}
}
pub fn fp12_frobenius_p2(a: &Fp12) -> Fp12 {
fp12_frobenius_p(&fp12_frobenius_p(a))
}
pub fn fp12_frobenius_p3(a: &Fp12) -> Fp12 {
fp12_frobenius_p(&fp12_frobenius_p2(a))
}
#[inline]
pub fn fp12_conjugate(a: &Fp12) -> Fp12 {
Fp12 {
c0: a.c0,
c1: fp6_neg(&a.c1),
}
}
pub fn fp12_to_bytes(a: &Fp12) -> [u8; 384] {
let mut out = [0u8; 384];
let parts = [a.c0.c0, a.c0.c1, a.c0.c2, a.c1.c0, a.c1.c1, a.c1.c2];
for (i, fp2) in parts.iter().enumerate() {
let b = fp2.to_bytes();
out[i * 64..(i + 1) * 64].copy_from_slice(&b);
}
out
}
#[derive(Clone, Copy, Debug)]
pub struct LineEval {
pub a: Fp2,
pub b: Fp2,
pub c: Fp2,
}
pub fn fp12_mul_by_line(f: &Fp12, l: &LineEval) -> Fp12 {
let line_fp12 = Fp12 {
c0: Fp6 {
c0: l.a,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
c1: Fp6 {
c0: Fp2::ZERO,
c1: l.b,
c2: l.c,
},
};
fp12_mul(f, &line_fp12)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fp6_add_neg() {
let a = Fp6 {
c0: Fp2::ONE,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
};
let neg_a = fp6_neg(&a);
let sum = fp6_add(&a, &neg_a);
assert_eq!(sum, Fp6::ZERO);
}
#[test]
fn test_fp6_mul_one() {
let a = Fp6 {
c0: Fp2::ONE,
c1: Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
},
c2: Fp2::ZERO,
};
let r = fp6_mul(&a, &Fp6::ONE);
assert_eq!(r, a);
}
#[test]
fn test_fp12_mul_one() {
let a = Fp12 {
c0: Fp6 {
c0: Fp2::ONE,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
c1: Fp6::ZERO,
};
let r = fp12_mul(&a, &Fp12::ONE);
assert_eq!(r, a);
}
#[test]
fn test_fp12_inv() {
let a = Fp12 {
c0: Fp6 {
c0: Fp2 {
c0: Fp::ONE,
c1: Fp::ONE,
},
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
c1: Fp6::ZERO,
};
let inv = fp12_inv(&a).expect("逆元应存在");
let prod = fp12_mul(&a, &inv);
assert_eq!(prod, Fp12::ONE);
}
#[test]
fn test_fp12_mul_by_line_matches_full_mul() {
let f = Fp12 {
c0: Fp6 {
c0: Fp2 {
c0: Fp::ONE,
c1: Fp::ONE,
},
c1: Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
},
c2: Fp2::ZERO,
},
c1: Fp6 {
c0: Fp2 {
c0: Fp::ZERO,
c1: Fp::ONE,
},
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
};
let l = LineEval {
a: Fp2 {
c0: Fp::ONE,
c1: Fp::ONE,
},
b: Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
},
c: Fp2 {
c0: Fp::ZERO,
c1: Fp::ONE,
},
};
let sparse = fp12_mul_by_line(&f, &l);
let line_full = Fp12 {
c0: Fp6 {
c0: l.a,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
c1: Fp6 {
c0: Fp2::ZERO,
c1: l.b,
c2: l.c,
},
};
let full = fp12_mul(&f, &line_full);
assert_eq!(sparse, full, "稀疏线函数乘法与全量乘法结果不一致");
}
#[test]
fn test_frob_w3_derivation() {
let f = Fp12 {
c0: Fp6 {
c0: Fp2 {
c0: Fp::ONE,
c1: Fp::ONE,
},
c1: Fp2::ONE,
c2: Fp2::ZERO,
},
c1: Fp6 {
c0: Fp2::ONE,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
};
let fp1 = fp12_frobenius_p(&f);
let fp1p1 = fp12_frobenius_p(&fp1); let fp2 = fp12_frobenius_p2(&f);
assert_eq!(
fp1p1, fp2,
"frob_p(frob_p(f)) != frob_p2(f):fp12 Frobenius 不一致"
);
let fp2p1 = fp12_frobenius_p(&fp2); let fp3 = fp12_frobenius_p3(&f);
assert_eq!(
fp2p1, fp3,
"frob_p(frob_p2(f)) != frob_p3(f):fp12_frobenius_p3 系数错误"
);
}
#[test]
fn test_frobenius_one() {
let one = Fp6::ONE;
let f_p = fp6_frobenius_p(&one);
let f_p2 = fp6_frobenius_p2(&one);
let f_p3 = fp6_frobenius_p3(&one);
assert_eq!(f_p, one, "frobenius_p(ONE) != ONE");
assert_eq!(f_p2, one, "frobenius_p2(ONE) != ONE");
assert_eq!(f_p3, one, "frobenius_p3(ONE) != ONE");
}
#[test]
fn test_frob_v1_squared() {
use crate::sm9::fields::fp2::fp2_mul;
let v1_sq = fp2_mul(&FROB_V1_0, &FROB_V1_0);
assert_eq!(
v1_sq, FROB_V1_1,
"FROB_V1_0² 应等于 FROB_V1_1(fp6 Frobenius 一致性)"
);
}
#[test]
fn test_frob_v1_0_value_correct() {
use crate::sm9::fields::fp::FIELD_MODULUS;
use crate::sm9::fields::fp2::fp2_mul;
let pm1 = FIELD_MODULUS.wrapping_sub(&crypto_bigint::U256::ONE);
let (pm1_div3, rem) =
pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(3u32)).unwrap());
assert_eq!(rem, crypto_bigint::U256::ZERO, "(p-1) 应被 3 整除");
let (pm1_div6, _) =
pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(6u32)).unwrap());
fn fp2_pow_exp(base: &Fp2, exp: &crypto_bigint::U256) -> Fp2 {
use crate::sm9::fields::fp2::{fp2_mul, fp2_square};
use subtle::ConditionallySelectable;
let mut result = Fp2::ONE;
let mut b = *base;
for byte in exp.to_be_bytes().iter().rev() {
for bit in 0..8 {
let product = fp2_mul(&result, &b);
let choice = subtle::Choice::from((byte >> bit) & 1);
result = Fp2::conditional_select(&result, &product, choice);
b = fp2_square(&b);
}
}
result
}
let u = Fp2 {
c0: crate::sm9::fields::fp::Fp::ZERO,
c1: crate::sm9::fields::fp::Fp::ONE,
};
let correct_v1_0 = fp2_pow_exp(&u, &pm1_div3);
let correct_w1 = fp2_pow_exp(&u, &pm1_div6);
let w1_sq = fp2_mul(&correct_w1, &correct_w1);
assert_eq!(w1_sq, correct_v1_0, "u^{{(p-1)/6}}^2 应等于 u^{{(p-1)/3}}");
assert_eq!(
correct_v1_0,
FROB_V1_0,
"FROB_V1_0 需更新:正确值={:02X?}, FROB_W1 正确值 c0={:02X?} c1={:02X?}",
correct_v1_0.c0.retrieve().to_be_bytes(),
correct_w1.c0.retrieve().to_be_bytes(),
correct_w1.c1.retrieve().to_be_bytes(),
);
}
}
#[cfg(test)]
mod g2_frob_tests {
use super::*;
#[test]
fn test_compute_g2_frobenius_constants() {
use crate::sm9::fields::fp::FIELD_MODULUS;
fn fp2_pow_exp(base: &Fp2, exp: &crypto_bigint::U256) -> Fp2 {
use crate::sm9::fields::fp2::{fp2_mul, fp2_square};
use subtle::ConditionallySelectable;
let mut result = Fp2::ONE;
let mut b = *base;
for byte in exp.to_be_bytes().iter().rev() {
for bit in 0..8 {
let product = fp2_mul(&result, &b);
let choice = subtle::Choice::from((byte >> bit) & 1);
result = Fp2::conditional_select(&result, &product, choice);
b = fp2_square(&b);
}
}
result
}
let p = FIELD_MODULUS;
let pm1 = p.wrapping_sub(&crypto_bigint::U256::ONE);
let u = Fp2 {
c0: Fp::ZERO,
c1: Fp::ONE,
};
let pm1_div2 = pm1.wrapping_shr(1);
let u_pm1_div2 = fp2_pow_exp(&u, &pm1_div2);
let (pm1_div3, _) =
pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(3u32)).unwrap());
let u_pm1_div3 = fp2_pow_exp(&u, &pm1_div3);
let pp1 = p.wrapping_add(&crypto_bigint::U256::ONE);
let u_pm21_div3 = fp2_pow_exp(&u_pm1_div3, &pp1);
assert_eq!(u_pm1_div2, G2_FROB_Y1, "u^(p-1)/2 应等于 G2_FROB_Y1");
assert_eq!(u_pm21_div3, G2_FROB_X2, "u^(p2-1)/3 应等于 G2_FROB_X2");
}
}