use crate::sm9::fields::fp::{
fp_add, fp_from_bytes, fp_inv, fp_mul, fp_neg, fp_square, fp_sub, fp_to_bytes, Fp,
};
use subtle::{Choice, ConditionallySelectable};
impl ConditionallySelectable for Fp2 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Fp2 {
c0: Fp::conditional_select(&a.c0, &b.c0, choice),
c1: Fp::conditional_select(&a.c1, &b.c1, choice),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Fp2 {
pub c0: Fp,
pub c1: Fp,
}
impl Fp2 {
pub const ZERO: Self = Fp2 {
c0: Fp::ZERO,
c1: Fp::ZERO,
};
pub const ONE: Self = Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
};
pub fn from_bytes(bytes: &[u8; 64]) -> Self {
let c0 = fp_from_bytes(bytes[0..32].try_into().unwrap());
let c1 = fp_from_bytes(bytes[32..64].try_into().unwrap());
Fp2 { c0, c1 }
}
pub fn to_bytes(&self) -> [u8; 64] {
let mut out = [0u8; 64];
out[0..32].copy_from_slice(&fp_to_bytes(&self.c0));
out[32..64].copy_from_slice(&fp_to_bytes(&self.c1));
out
}
pub fn is_zero(&self) -> bool {
fp_to_bytes(&self.c0).iter().all(|&b| b == 0)
&& fp_to_bytes(&self.c1).iter().all(|&b| b == 0)
}
}
#[inline]
pub fn fp2_add(a: &Fp2, b: &Fp2) -> Fp2 {
Fp2 {
c0: fp_add(&a.c0, &b.c0),
c1: fp_add(&a.c1, &b.c1),
}
}
#[inline]
pub fn fp2_sub(a: &Fp2, b: &Fp2) -> Fp2 {
Fp2 {
c0: fp_sub(&a.c0, &b.c0),
c1: fp_sub(&a.c1, &b.c1),
}
}
#[inline]
pub fn fp2_neg(a: &Fp2) -> Fp2 {
Fp2 {
c0: fp_neg(&a.c0),
c1: fp_neg(&a.c1),
}
}
pub fn fp2_mul(a: &Fp2, b: &Fp2) -> Fp2 {
let a0b0 = fp_mul(&a.c0, &b.c0);
let a1b1 = fp_mul(&a.c1, &b.c1);
let two_a1b1 = fp_add(&a1b1, &a1b1);
let c0 = fp_sub(&a0b0, &two_a1b1);
let a0b1 = fp_mul(&a.c0, &b.c1);
let a1b0 = fp_mul(&a.c1, &b.c0);
let c1 = fp_add(&a0b1, &a1b0);
Fp2 { c0, c1 }
}
pub fn fp2_square(a: &Fp2) -> Fp2 {
let a0sq = fp_square(&a.c0);
let a1sq = fp_square(&a.c1);
let c0 = fp_sub(&a0sq, &fp_add(&a1sq, &a1sq));
let a0a1 = fp_mul(&a.c0, &a.c1);
let c1 = fp_add(&a0a1, &a0a1);
Fp2 { c0, c1 }
}
pub fn fp2_inv(a: &Fp2) -> Option<Fp2> {
let a0sq = fp_square(&a.c0);
let a1sq = fp_square(&a.c1);
let norm = fp_add(&a0sq, &fp_add(&a1sq, &a1sq));
let norm_inv = fp_inv(&norm)?;
Some(Fp2 {
c0: fp_mul(&a.c0, &norm_inv),
c1: fp_neg(&fp_mul(&a.c1, &norm_inv)),
})
}
#[inline]
pub fn fp2_mul_fp(a: &Fp2, b: &Fp) -> Fp2 {
Fp2 {
c0: fp_mul(&a.c0, b),
c1: fp_mul(&a.c1, b),
}
}
#[inline]
pub fn fp2_mul_u(a: &Fp2) -> Fp2 {
let two_a1 = fp_add(&a.c1, &a.c1);
Fp2 {
c0: fp_neg(&two_a1),
c1: a.c0,
}
}
#[inline]
pub fn fp2_frobenius(a: &Fp2) -> Fp2 {
Fp2 {
c0: a.c0,
c1: fp_neg(&a.c1),
}
}
#[inline]
pub fn fp2_conjugate(a: &Fp2) -> Fp2 {
fp2_frobenius(a)
}
#[cfg(test)]
mod tests {
use super::*;
fn fp2_one() -> Fp2 {
Fp2::ONE
}
fn fp2_two() -> Fp2 {
let two = fp_add(&Fp::ONE, &Fp::ONE);
Fp2 {
c0: two,
c1: Fp::ZERO,
}
}
#[test]
fn test_fp2_add_sub() {
let a = fp2_two();
let b = fp2_one();
let c = fp2_add(&a, &b);
let d = fp2_sub(&c, &b);
assert_eq!(d, a);
}
#[test]
fn test_fp2_mul_one() {
let a = fp2_two();
let r = fp2_mul(&a, &Fp2::ONE);
assert_eq!(r, a);
}
#[test]
fn test_fp2_square_vs_mul() {
let a = fp2_two();
let s = fp2_square(&a);
let m = fp2_mul(&a, &a);
assert_eq!(s, m);
}
#[test]
fn test_fp2_inv() {
let a = fp2_two();
let inv = fp2_inv(&a).expect("2^-1 应存在");
assert_eq!(fp2_mul(&a, &inv), Fp2::ONE);
}
#[test]
fn test_fp2_u_squared() {
let u = Fp2 {
c0: Fp::ZERO,
c1: Fp::ONE,
};
let u2 = fp2_square(&u);
let neg_two = fp_neg(&fp_add(&Fp::ONE, &Fp::ONE));
assert_eq!(u2.c0, neg_two);
assert_eq!(fp_to_bytes(&u2.c1), [0u8; 32]);
}
}