Implementation of pairing.

This commit is contained in:
Sean Bowe
2016-09-10 12:21:32 -06:00
parent 72a722aa43
commit b93e312eb0
6 changed files with 1148 additions and 64 deletions
+342
View File
@@ -0,0 +1,342 @@
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 {
c0: self.c0,
c1: -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
}
}
}
+27 -4
View File
@@ -5,12 +5,20 @@ use rand::Rng;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
#[inline]
fn non_residue() -> Fq {
fn fq_non_residue() -> Fq {
// (q - 1) is a quadratic nonresidue in Fq
// 21888242871839275222246405745257275088696311157297823662689037894645226208582
const_fp([0x68c3488912edefaa, 0x8d087f6872aabf4f, 0x51e1a24709081231, 0x2259d6b14729c0fa])
}
#[inline]
pub fn fq2_nonresidue() -> Fq2 {
Fq2::new(
const_fp([0xf60647ce410d7ff7, 0x2f3d6f4dd31bd011, 0x2943337e3940c6d1, 0x1d9598e8a7e39857]),
const_fp([0xd35d438dc58f0d9d, 0x0a78eb28f5c70b3d, 0x666ea36f7879462c, 0x0e0a77c19a07df2f])
)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Fq2 {
c0: Fq,
@@ -51,6 +59,21 @@ impl Fq2 {
c1: self.c1 * by
}
}
pub fn mul_by_nonresidue(&self) -> Self {
*self * fq2_nonresidue()
}
pub fn frobenius_map(&self, power: usize) -> Self {
if power % 2 == 0 {
*self
} else {
Fq2 {
c0: self.c0,
c1: self.c1 * fq_non_residue()
}
}
}
}
impl FieldElement for Fq2 {
@@ -87,7 +110,7 @@ impl FieldElement for Fq2 {
let ab = self.c0 * self.c1;
Fq2 {
c0: (self.c1 * non_residue() + self.c0) * (self.c0 + self.c1) - ab - ab * non_residue(),
c0: (self.c1 * fq_non_residue() + self.c0) * (self.c0 + self.c1) - ab - ab * fq_non_residue(),
c1: ab + ab
}
}
@@ -96,7 +119,7 @@ impl FieldElement for Fq2 {
// "High-Speed Software Implementation of the Optimal Ate Pairing
// over BarretoNaehrig Curves"; Algorithm 8
match (self.c0.squared() - (self.c1.squared() * non_residue())).inverse() {
match (self.c0.squared() - (self.c1.squared() * fq_non_residue())).inverse() {
Some(t) => Some(Fq2 {
c0: self.c0 * t,
c1: -(self.c1 * t)
@@ -118,7 +141,7 @@ impl Mul for Fq2 {
let bb = self.c1 * other.c1;
Fq2 {
c0: bb * non_residue() + aa,
c0: bb * fq_non_residue() + aa,
c1: (self.c0 + self.c1) * (other.c0 + other.c1) - aa - bb
}
}
+193
View File
@@ -0,0 +1,193 @@
use fields::{FieldElement, Fq, Fq2, const_fp};
use std::ops::{Add, Sub, Mul, Neg};
use rand::Rng;
fn frobenius_coeffs_c1(n: usize) -> Fq2 {
match n % 6 {
0 => Fq2::one(),
1 => Fq2::new(
const_fp([13075984984163199792, 3782902503040509012, 8791150885551868305, 1825854335138010348]),
const_fp([7963664994991228759, 12257807996192067905, 13179524609921305146, 2767831111890561987])
),
2 => Fq2::new(
const_fp([3697675806616062876, 9065277094688085689, 6918009208039626314, 2775033306905974752]),
Fq::zero()
),
3 => Fq2::new(
const_fp([14532872967180610477, 12903226530429559474, 1868623743233345524, 2316889217940299650]),
const_fp([12447993766991532972, 4121872836076202828, 7630813605053367399, 740282956577754197])
),
_ => unimplemented!()
}
}
fn frobenius_coeffs_c2(n: usize) -> Fq2 {
match n % 6 {
0 => Fq2::one(),
1 => Fq2::new(
const_fp([8314163329781907090, 11942187022798819835, 11282677263046157209, 1576150870752482284]),
const_fp([6763840483288992073, 7118829427391486816, 4016233444936635065, 2630958277570195709])
),
2 => Fq2::new(
const_fp([8183898218631979349, 12014359695528440611, 12263358156045030468, 3187210487005268291]),
Fq::zero()
),
3 => Fq2::new(
const_fp([4938922280314430175, 13823286637238282975, 15589480384090068090, 481952561930628184]),
const_fp([3105754162722846417, 11647802298615474591, 13057042392041828081, 1660844386505564338])
),
_ => unimplemented!()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Fq6 {
pub c0: Fq2,
pub c1: Fq2,
pub c2: Fq2
}
impl Fq6 {
pub fn new(c0: Fq2, c1: Fq2, c2: Fq2) -> Self {
Fq6 {
c0: c0,
c1: c1,
c2: c2
}
}
pub fn mul_by_nonresidue(&self) -> Self {
Fq6 {
c0: self.c2.mul_by_nonresidue(),
c1: self.c0,
c2: self.c1
}
}
pub fn scale(&self, by: Fq2) -> Self {
Fq6 {
c0: self.c0 * by,
c1: self.c1 * by,
c2: self.c2 * by
}
}
pub fn frobenius_map(&self, power: usize) -> Self {
Fq6 {
c0: self.c0.frobenius_map(power),
c1: self.c1.frobenius_map(power) * frobenius_coeffs_c1(power),
c2: self.c2.frobenius_map(power) * frobenius_coeffs_c2(power)
}
}
}
impl FieldElement for Fq6 {
fn zero() -> Self {
Fq6 {
c0: Fq2::zero(),
c1: Fq2::zero(),
c2: Fq2::zero()
}
}
fn one() -> Self {
Fq6 {
c0: Fq2::one(),
c1: Fq2::zero(),
c2: Fq2::zero()
}
}
fn random<R: Rng>(rng: &mut R) -> Self {
Fq6 {
c0: Fq2::random(rng),
c1: Fq2::random(rng),
c2: Fq2::random(rng)
}
}
fn is_zero(&self) -> bool {
self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero()
}
fn squared(&self) -> Self {
let s0 = self.c0.squared();
let ab = self.c0 * self.c1;
let s1 = ab + ab;
let s2 = (self.c0 - self.c1 + self.c2).squared();
let bc = self.c1 * self.c2;
let s3 = bc + bc;
let s4 = self.c2.squared();
Fq6 {
c0: s0 + s3.mul_by_nonresidue(),
c1: s1 + s4.mul_by_nonresidue(),
c2: s1 + s2 + s3 - s0 - s4
}
}
fn inverse(self) -> Option<Self> {
let c0 = self.c0.squared() - self.c1 * self.c2.mul_by_nonresidue();
let c1 = self.c2.squared().mul_by_nonresidue() - self.c0 * self.c1;
let c2 = self.c1.squared() - self.c0 * self.c2;
match ((self.c2 * c1 + self.c1 * c2).mul_by_nonresidue() + self.c0 * c0).inverse() {
Some(t) => Some(Fq6 {
c0: t * c0,
c1: t * c1,
c2: t * c2
}),
None => None
}
}
}
impl Mul for Fq6 {
type Output = Fq6;
fn mul(self, other: Fq6) -> Fq6 {
let a_a = self.c0 * other.c0;
let b_b = self.c1 * other.c1;
let c_c = self.c2 * other.c2;
Fq6 {
c0: ((self.c1 + self.c2) * (other.c1 + other.c2) - b_b - c_c).mul_by_nonresidue() + a_a,
c1: (self.c0 + self.c1) * (other.c0 + other.c1) - a_a - b_b + c_c.mul_by_nonresidue(),
c2: (self.c0 + self.c2) * (other.c0 + other.c2) - a_a + b_b - c_c
}
}
}
impl Sub for Fq6 {
type Output = Fq6;
fn sub(self, other: Fq6) -> Fq6 {
Fq6 {
c0: self.c0 - other.c0,
c1: self.c1 - other.c1,
c2: self.c2 - other.c2
}
}
}
impl Add for Fq6 {
type Output = Fq6;
fn add(self, other: Fq6) -> Fq6 {
Fq6 {
c0: self.c0 + other.c0,
c1: self.c1 + other.c1,
c2: self.c2 + other.c2
}
}
}
impl Neg for Fq6 {
type Output = Fq6;
fn neg(self) -> Fq6 {
Fq6 {
c0: -self.c0,
c1: -self.c1,
c2: -self.c2
}
}
}
+135 -1
View File
@@ -1,5 +1,7 @@
mod fp;
mod fq2;
mod fq6;
mod fq12;
use arith::U256;
use rand::Rng;
@@ -7,7 +9,9 @@ use std::ops::{Add, Sub, Mul, Neg};
use std::fmt::Debug;
pub use self::fp::{Fq,Fr,const_fp};
pub use self::fq2::Fq2;
pub use self::fq2::{Fq2, fq2_nonresidue};
pub use self::fq6::Fq6;
pub use self::fq12::Fq12;
pub trait FieldElement: Sized
+ Copy
@@ -65,3 +69,133 @@ fn test_str() {
assert_eq!(-Fr::one(), Fr::from_str("21888242871839275222246405745257275088548364400416034343698204186575808495616").unwrap());
assert_eq!(-Fq::one(), Fq::from_str("21888242871839275222246405745257275088696311157297823662689037894645226208582").unwrap());
}
#[test]
fn test_fq6() {
tests::field_trials::<Fq6>();
}
#[test]
fn test_fq12() {
tests::field_trials::<Fq12>();
}
#[test]
fn fq12_test_vector() {
let start = Fq12::new(
Fq6::new(
Fq2::new(
Fq::from_str("19797905000333868150253315089095386158892526856493194078073564469188852136946").unwrap(),
Fq::from_str("10509658143212501778222314067134547632307419253211327938344904628569123178733").unwrap()
),
Fq2::new(
Fq::from_str("208316612133170645758860571704540129781090973693601051684061348604461399206").unwrap(),
Fq::from_str("12617661120538088237397060591907161689901553895660355849494983891299803248390").unwrap()
),
Fq2::new(
Fq::from_str("2897490589776053688661991433341220818937967872052418196321943489809183508515").unwrap(),
Fq::from_str("2730506433347642574983433139433778984782882168213690554721050571242082865799").unwrap()
)
),
Fq6::new(
Fq2::new(
Fq::from_str("17870056122431653936196746815433147921488990391314067765563891966783088591110").unwrap(),
Fq::from_str("14314041658607615069703576372547568077123863812415914883625850585470406221594").unwrap()
),
Fq2::new(
Fq::from_str("10123533891707846623287020000407963680629966110211808794181173248765209982878").unwrap(),
Fq::from_str("5062091880848845693514855272640141851746424235009114332841857306926659567101").unwrap()
),
Fq2::new(
Fq::from_str("9839781502639936537333620974973645053542086898304697594692219798017709586567").unwrap(),
Fq::from_str("1583892292110602864638265389721494775152090720173641072176370350017825640703").unwrap()
)
)
);
// Do a bunch of arbitrary stuff to the element
let mut next = start.clone();
for _ in 0..100 {
next = next * start;
}
let cpy = next.clone();
for _ in 0..10 {
next = next.squared();
}
for _ in 0..10 {
next = next + start;
next = next - cpy;
next = -next;
}
next = next.squared();
let finally = Fq12::new(
Fq6::new(
Fq2::new(
Fq::from_str("18388750939593263065521177085001223024106699964957029146547831509155008229833").unwrap(),
Fq::from_str("18370529854582635460997127698388761779167953912610241447912705473964014492243").unwrap()
),
Fq2::new(
Fq::from_str("3691824277096717481466579496401243638295254271265821828017111951446539785268").unwrap(),
Fq::from_str("20513494218085713799072115076991457239411567892860153903443302793553884247235").unwrap()
),
Fq2::new(
Fq::from_str("12214155472433286415803224222551966441740960297013786627326456052558698216399").unwrap(),
Fq::from_str("10987494248070743195602580056085773610850106455323751205990078881956262496575").unwrap()
)
),
Fq6::new(
Fq2::new(
Fq::from_str("5134522153456102954632718911439874984161223687865160221119284322136466794876").unwrap(),
Fq::from_str("20119236909927036376726859192821071338930785378711977469360149362002019539920").unwrap()
),
Fq2::new(
Fq::from_str("8839766648621210419302228913265679710586991805716981851373026244791934012854").unwrap(),
Fq::from_str("9103032146464138788288547957401673544458789595252696070370942789051858719203").unwrap()
),
Fq2::new(
Fq::from_str("10378379548636866240502412547812481928323945124508039853766409196375806029865").unwrap(),
Fq::from_str("9021627154807648093720460686924074684389554332435186899318369174351765754041").unwrap()
)
)
);
assert_eq!(finally, next);
}
#[test]
fn test_cyclotomic_exp() {
let orig = Fq12::new(
Fq6::new(
Fq2::new(Fq::from_str("2259924035228092997691937637688451143058635253053054071159756458902878894295").unwrap(), Fq::from_str("13145690032701362144460254305183927872683620413225364127064863863535255135244").unwrap()),
Fq2::new(Fq::from_str("9910063591662383599552477067956819406417086889312288278252482503717089428441").unwrap(), Fq::from_str("537414042055419261990282459138081732565514913399498746664966841152381183961").unwrap()),
Fq2::new(Fq::from_str("15311812409497308894370893420777496684951030254049554818293571309705780605004").unwrap(), Fq::from_str("13657107176064455789881282546557276003626320193974643644160350907227082365810").unwrap())
),
Fq6::new(Fq2::new(Fq::from_str("4913017949003742946864670837361832856526234260447029873580022776602534856819").unwrap(), Fq::from_str("7834351480852267338070670220119081676575418514182895774094743209915633114041").unwrap()),
Fq2::new(Fq::from_str("12837298223308203788092748646758194441270207338661891973231184407371206766993").unwrap(), Fq::from_str("12756474445699147370503225379431475413909971718057034061593007812727141391799").unwrap()),
Fq2::new(Fq::from_str("9473802207170192255373153510655867502408045964296373712891954747252332944018").unwrap(), Fq::from_str("4583089109360519374075173304035813179013579459429335467869926761027310749713").unwrap())
)
);
let expected = Fq12::new(
Fq6::new(
Fq2::new(Fq::from_str("14722956046055152398903846391223329501345567382234608299399030576415080188350").unwrap(), Fq::from_str("14280703280777926697010730619606819467080027543707671882210769811674790473417").unwrap()),
Fq2::new(Fq::from_str("19969875076083990244184003223190771301761436396530543002586073549972410735411").unwrap(), Fq::from_str("10717335566913889643303549252432531178405520196706173198634734518494041323243").unwrap()),
Fq2::new(Fq::from_str("6063612626166484870786832843320782567259894784043383626084549455432890717937").unwrap(), Fq::from_str("17089783040131779205038789608891431427943860868115199598200376195935079808729").unwrap())
),
Fq6::new(
Fq2::new(Fq::from_str("10029863438921507421569931792104023129735006154272482043027653425575205672906").unwrap(), Fq::from_str("6406252222753462799887280578845937185621081001436094637606245493619821542775").unwrap()),
Fq2::new(Fq::from_str("1048245462913506652602966692378792381004227332967846949234978073448561848050").unwrap(), Fq::from_str("1444281375189053827455518242624554285012408033699861764136810522738182087554").unwrap()),
Fq2::new(Fq::from_str("8839610992666735109106629514135300820412539620261852250193684883379364789120").unwrap(), Fq::from_str("11347360242067273846784836674906058940820632082713814508736182487171407730718").unwrap())
)
);
let e = orig.exp_by_neg_z();
assert_eq!(e, expected);
}