340 lines
7.9 KiB
Rust
340 lines
7.9 KiB
Rust
use fields::{FieldElement, Fq2, Fq, Fq6, const_fp};
|
|
use std::ops::{Add, Sub, Mul, Neg};
|
|
use rand::Rng;
|
|
|
|
use arith::U256;
|
|
|
|
fn frobenius_coeffs_c1(power: usize) -> Fq2 {
|
|
match power % 12 {
|
|
0 => Fq2::one(),
|
|
1 => Fq2::new(
|
|
const_fp([12653890742059813127, 14585784200204367754, 1278438861261381767, 212598772761311868]),
|
|
const_fp([11683091849979440498, 14992204589386555739, 15866167890766973222, 1200023580730561873])
|
|
),
|
|
2 => Fq2::new(
|
|
const_fp([14595462726357228530, 17349508522658994025, 1017833795229664280, 299787779797702374]),
|
|
Fq::zero()
|
|
),
|
|
3 => Fq2::new(
|
|
const_fp([3914496794763385213, 790120733010914719, 7322192392869644725, 581366264293887267]),
|
|
const_fp([12817045492518885689, 4440270538777280383, 11178533038884588256, 2767537931541304486])
|
|
),
|
|
_ => unimplemented!()
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub struct Fq12 {
|
|
c0: Fq6,
|
|
c1: Fq6
|
|
}
|
|
|
|
impl Fq12 {
|
|
pub fn new(c0: Fq6, c1: Fq6) -> Self {
|
|
Fq12 {
|
|
c0: c0,
|
|
c1: c1
|
|
}
|
|
}
|
|
|
|
fn final_exponentiation_first_chunk(&self) -> Option<Fq12> {
|
|
match self.inverse() {
|
|
Some(b) => {
|
|
let a = self.unitary_inverse();
|
|
let c = a * b;
|
|
let d = c.frobenius_map(2);
|
|
|
|
Some(d * c)
|
|
},
|
|
None => None
|
|
}
|
|
}
|
|
|
|
fn final_exponentiation_last_chunk(&self) -> Fq12 {
|
|
let a = self.exp_by_neg_z();
|
|
let b = a.cyclotomic_squared();
|
|
let c = b.cyclotomic_squared();
|
|
let d = c * b;
|
|
|
|
let e = d.exp_by_neg_z();
|
|
let f = e.cyclotomic_squared();
|
|
let g = f.exp_by_neg_z();
|
|
let h = d.unitary_inverse();
|
|
let i = g.unitary_inverse();
|
|
|
|
let j = i * e;
|
|
let k = j * h;
|
|
let l = k * b;
|
|
let m = k * e;
|
|
let n = *self * m;
|
|
|
|
let o = l.frobenius_map(1);
|
|
let p = o * n;
|
|
|
|
let q = k.frobenius_map(2);
|
|
let r = q * p;
|
|
|
|
let s = self.unitary_inverse();
|
|
let t = s * l;
|
|
let u = t.frobenius_map(3);
|
|
let v = u * r;
|
|
|
|
v
|
|
}
|
|
|
|
pub fn final_exponentiation(&self) -> Option<Fq12> {
|
|
self.final_exponentiation_first_chunk().map(|a| a.final_exponentiation_last_chunk())
|
|
}
|
|
|
|
pub fn frobenius_map(&self, power: usize) -> Self {
|
|
Fq12 {
|
|
c0: self.c0.frobenius_map(power),
|
|
c1: self.c1.frobenius_map(power).scale(frobenius_coeffs_c1(power))
|
|
}
|
|
}
|
|
|
|
pub fn exp_by_neg_z(&self) -> Fq12 {
|
|
self.cyclotomic_pow(
|
|
U256::from([4965661367192848881, 0, 0, 0])
|
|
).unitary_inverse()
|
|
}
|
|
|
|
pub fn unitary_inverse(&self) -> Fq12 {
|
|
Fq12::new(self.c0, -self.c1)
|
|
}
|
|
|
|
pub fn mul_by_024(&self,
|
|
ell_0: Fq2,
|
|
ell_vw: Fq2,
|
|
ell_vv: Fq2) -> Fq12 {
|
|
let z0 = self.c0.c0;
|
|
let z1 = self.c0.c1;
|
|
let z2 = self.c0.c2;
|
|
let z3 = self.c1.c0;
|
|
let z4 = self.c1.c1;
|
|
let z5 = self.c1.c2;
|
|
|
|
let x0 = ell_0;
|
|
let x2 = ell_vv;
|
|
let x4 = ell_vw;
|
|
|
|
let d0 = z0 * x0;
|
|
let d2 = z2 * x2;
|
|
let d4 = z4 * x4;
|
|
let t2 = z0 + z4;
|
|
let t1 = z0 + z2;
|
|
let s0 = z1 + z3 + z5;
|
|
|
|
let s1 = z1 * x2;
|
|
let t3 = s1 + d4;
|
|
let t4 = t3.mul_by_nonresidue() + d0;
|
|
let z0 = t4;
|
|
|
|
let t3 = z5 * x4;
|
|
let s1 = s1 + t3;
|
|
let t3 = t3 + d2;
|
|
let t4 = t3.mul_by_nonresidue();
|
|
let t3 = z1 * x0;
|
|
let s1 = s1 + t3;
|
|
let t4 = t4 + t3;
|
|
let z1 = t4;
|
|
|
|
let t0 = x0 + x2;
|
|
let t3 = t1 * t0 - d0 - d2;
|
|
let t4 = z3 * x4;
|
|
let s1 = s1 + t4;
|
|
let t3 = t3 + t4;
|
|
|
|
let t0 = z2 + z4;
|
|
let z2 = t3;
|
|
|
|
let t1 = x2 + x4;
|
|
let t3 = t0 * t1 - d2 - d4;
|
|
let t4 = t3.mul_by_nonresidue();
|
|
let t3 = z3 * x0;
|
|
let s1 = s1 + t3;
|
|
let t4 = t4 + t3;
|
|
let z3 = t4;
|
|
|
|
let t3 = z5 * x2;
|
|
let s1 = s1 + t3;
|
|
let t4 = t3.mul_by_nonresidue();
|
|
let t0 = x0 + x4;
|
|
let t3 = t2 * t0 - d0 - d4;
|
|
let t4 = t4 + t3;
|
|
let z4 = t4;
|
|
|
|
let t0 = x0 + x2 + x4;
|
|
let t3 = s0 * t0 - s1;
|
|
let z5 = t3;
|
|
|
|
Fq12 {
|
|
c0: Fq6::new(z0, z1, z2),
|
|
c1: Fq6::new(z3, z4, z5)
|
|
}
|
|
}
|
|
|
|
pub fn cyclotomic_squared(&self) -> Self {
|
|
let z0 = self.c0.c0;
|
|
let z4 = self.c0.c1;
|
|
let z3 = self.c0.c2;
|
|
let z2 = self.c1.c0;
|
|
let z1 = self.c1.c1;
|
|
let z5 = self.c1.c2;
|
|
|
|
let tmp = z0 * z1;
|
|
let t0 = (z0 + z1) * (z1.mul_by_nonresidue() + z0) - tmp - tmp.mul_by_nonresidue();
|
|
let t1 = tmp + tmp;
|
|
|
|
let tmp = z2 * z3;
|
|
let t2 = (z2 + z3) * (z3.mul_by_nonresidue() + z2) - tmp - tmp.mul_by_nonresidue();
|
|
let t3 = tmp + tmp;
|
|
|
|
let tmp = z4 * z5;
|
|
let t4 = (z4 + z5) * (z5.mul_by_nonresidue() + z4) - tmp - tmp.mul_by_nonresidue();
|
|
let t5 = tmp + tmp;
|
|
|
|
let z0 = t0 - z0;
|
|
let z0 = z0 + z0;
|
|
let z0 = z0 + t0;
|
|
|
|
let z1 = t1 + z1;
|
|
let z1 = z1 + z1;
|
|
let z1 = z1 + t1;
|
|
|
|
let tmp = t5.mul_by_nonresidue();
|
|
let z2 = tmp + z2;
|
|
let z2 = z2 + z2;
|
|
let z2 = z2 + tmp;
|
|
|
|
let z3 = t4 - z3;
|
|
let z3 = z3 + z3;
|
|
let z3 = z3 + t4;
|
|
|
|
let z4 = t2 - z4;
|
|
let z4 = z4 + z4;
|
|
let z4 = z4 + t2;
|
|
|
|
let z5 = t3 + z5;
|
|
let z5 = z5 + z5;
|
|
let z5 = z5 + t3;
|
|
|
|
Fq12 {
|
|
c0: Fq6::new(z0, z4, z3),
|
|
c1: Fq6::new(z2, z1, z5)
|
|
}
|
|
}
|
|
|
|
pub fn cyclotomic_pow<I: Into<U256>>(&self, by: I) -> Self {
|
|
let mut res = Self::one();
|
|
|
|
let mut found_one = false;
|
|
|
|
for i in by.into().bits() {
|
|
if found_one {
|
|
res = res.cyclotomic_squared();
|
|
}
|
|
|
|
if i {
|
|
found_one = true;
|
|
res = *self * res;
|
|
}
|
|
}
|
|
|
|
res
|
|
}
|
|
}
|
|
|
|
impl FieldElement for Fq12 {
|
|
fn zero() -> Self {
|
|
Fq12 {
|
|
c0: Fq6::zero(),
|
|
c1: Fq6::zero()
|
|
}
|
|
}
|
|
|
|
fn one() -> Self {
|
|
Fq12 {
|
|
c0: Fq6::one(),
|
|
c1: Fq6::zero()
|
|
}
|
|
}
|
|
|
|
fn random<R: Rng>(rng: &mut R) -> Self {
|
|
Fq12 {
|
|
c0: Fq6::random(rng),
|
|
c1: Fq6::random(rng)
|
|
}
|
|
}
|
|
|
|
fn is_zero(&self) -> bool {
|
|
self.c0.is_zero() && self.c1.is_zero()
|
|
}
|
|
|
|
fn squared(&self) -> Self {
|
|
let ab = self.c0 * self.c1;
|
|
|
|
Fq12 {
|
|
c0: (self.c1.mul_by_nonresidue() + self.c0) * (self.c0 + self.c1) - ab - ab.mul_by_nonresidue(),
|
|
c1: ab + ab
|
|
}
|
|
}
|
|
|
|
fn inverse(self) -> Option<Self> {
|
|
match (self.c0.squared() - (self.c1.squared().mul_by_nonresidue())).inverse() {
|
|
Some(t) => Some(Fq12 {
|
|
c0: self.c0 * t,
|
|
c1: -(self.c1 * t)
|
|
}),
|
|
None => None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul for Fq12 {
|
|
type Output = Fq12;
|
|
|
|
fn mul(self, other: Fq12) -> Fq12 {
|
|
let aa = self.c0 * other.c0;
|
|
let bb = self.c1 * other.c1;
|
|
|
|
Fq12 {
|
|
c0: bb.mul_by_nonresidue() + aa,
|
|
c1: (self.c0 + self.c1) * (other.c0 + other.c1) - aa - bb
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Sub for Fq12 {
|
|
type Output = Fq12;
|
|
|
|
fn sub(self, other: Fq12) -> Fq12 {
|
|
Fq12 {
|
|
c0: self.c0 - other.c0,
|
|
c1: self.c1 - other.c1
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Add for Fq12 {
|
|
type Output = Fq12;
|
|
|
|
fn add(self, other: Fq12) -> Fq12 {
|
|
Fq12 {
|
|
c0: self.c0 + other.c0,
|
|
c1: self.c1 + other.c1
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Neg for Fq12 {
|
|
type Output = Fq12;
|
|
|
|
fn neg(self) -> Fq12 {
|
|
Fq12 {
|
|
c0: -self.c0,
|
|
c1: -self.c1
|
|
}
|
|
}
|
|
}
|