Implementation of basic pairing API that does not require alloc.
This commit is contained in:
parent
450587f19b
commit
afe30519f8
|
@ -31,5 +31,5 @@ default-features = false
|
|||
[features]
|
||||
default = ["groups", "pairings"]
|
||||
groups = []
|
||||
pairings = []
|
||||
pairings = ["groups"]
|
||||
nightly = ["subtle/nightly"]
|
||||
|
|
|
@ -7,6 +7,14 @@ use bls12_381::*;
|
|||
use criterion::{black_box, Criterion};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
// Pairings
|
||||
{
|
||||
let g = G1Affine::generator();
|
||||
let h = G2Affine::generator();
|
||||
c.bench_function("full pairing", move |b| {
|
||||
b.iter(|| pairing(black_box(&g), black_box(&h)))
|
||||
});
|
||||
}
|
||||
// G1Affine
|
||||
{
|
||||
let name = "G1Affine";
|
||||
|
|
|
@ -0,0 +1,638 @@
|
|||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
use crate::fp6::*;
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$.
|
||||
pub struct Fp12 {
|
||||
pub c0: Fp6,
|
||||
pub c1: Fp6,
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp12 {
|
||||
fn from(f: Fp) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: Fp6::from(f),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp2> for Fp12 {
|
||||
fn from(f: Fp2) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: Fp6::from(f),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp6> for Fp12 {
|
||||
fn from(f: Fp6) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: f,
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Fp12 {
|
||||
fn eq(&self, other: &Fp12) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for Fp12 {}
|
||||
impl Clone for Fp12 {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp12 {
|
||||
fn default() -> Self {
|
||||
Fp12::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fp12 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?} + ({:?})*w", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp12 {
|
||||
#[inline(always)]
|
||||
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 ConstantTimeEq for Fp12 {
|
||||
#[inline(always)]
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fp12 {
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::zero(),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn one() -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::one(),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 {
|
||||
let aa = self.c0.mul_by_01(c0, c1);
|
||||
let bb = self.c1.mul_by_1(c4);
|
||||
let o = c1 + c4;
|
||||
let c1 = self.c1 + self.c0;
|
||||
let c1 = c1.mul_by_01(c0, &o);
|
||||
let c1 = c1 - aa - bb;
|
||||
let c0 = bb;
|
||||
let c0 = c0.mul_by_nonresidue();
|
||||
let c0 = c0 + aa;
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.c0.is_zero() & self.c1.is_zero()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn conjugate(&self) -> Self {
|
||||
Fp12 {
|
||||
c0: self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
let c0 = self.c0.frobenius_map();
|
||||
let c1 = self.c1.frobenius_map();
|
||||
|
||||
// c1 = c1 * (u + 1)^((p - 1) / 6)
|
||||
let c1 = c1
|
||||
* Fp6::from(Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x7089552b319d465,
|
||||
0xc6695f92b50a8313,
|
||||
0x97e83cccd117228f,
|
||||
0xa35baecab2dc29ee,
|
||||
0x1ce393ea5daace4d,
|
||||
0x8f2220fb0fb66eb,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xb2f66aad4ce5d646,
|
||||
0x5842a06bfc497cec,
|
||||
0xcf4895d42599d394,
|
||||
0xc11b9cba40a8e8d0,
|
||||
0x2e3813cbe5a0de89,
|
||||
0x110eefda88847faf,
|
||||
]),
|
||||
});
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square(&self) -> Self {
|
||||
let ab = self.c0 * self.c1;
|
||||
let c0c1 = self.c0 + self.c1;
|
||||
let c0 = self.c1.mul_by_nonresidue();
|
||||
let c0 = c0 + self.c0;
|
||||
let c0 = c0 * c0c1;
|
||||
let c0 = c0 - ab;
|
||||
let c1 = ab + ab;
|
||||
let c0 = c0 - ab.mul_by_nonresidue();
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
(self.c0.square() - self.c1.square().mul_by_nonresidue())
|
||||
.invert()
|
||||
.map(|t| Fp12 {
|
||||
c0: self.c0 * t,
|
||||
c1: self.c1 * -t,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: &'b Fp12) -> Self::Output {
|
||||
let aa = self.c0 * other.c0;
|
||||
let bb = self.c1 * other.c1;
|
||||
let o = other.c0 + other.c1;
|
||||
let c1 = self.c1 + self.c0;
|
||||
let c1 = c1 * o;
|
||||
let c1 = c1 - aa;
|
||||
let c1 = c1 - bb;
|
||||
let c0 = bb.mul_by_nonresidue();
|
||||
let c0 = c0 + aa;
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp12) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: self.c0 + rhs.c0,
|
||||
c1: self.c1 + rhs.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: -self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp12) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: self.c0 - rhs.c0,
|
||||
c1: self.c1 - rhs.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp12, Fp12);
|
||||
impl_binops_multiplicative!(Fp12, Fp12);
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic() {
|
||||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
|
||||
let a = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9cb98b1b82d58,
|
||||
0x5fe911eba3aa1d9d,
|
||||
0x96bf1b5f4dd81db3,
|
||||
0x8100d27cc9259f5b,
|
||||
0xafa20b9674640eab,
|
||||
0x9bbcea7d8d9497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x303cb98b1662daa,
|
||||
0xd93110aa0a621d5a,
|
||||
0xbfa9820c5be4a468,
|
||||
0xba3643ecb05a348,
|
||||
0xdc3534bb1f1c25a6,
|
||||
0x6c305bb19c0e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9cb98b162d858,
|
||||
0xbe9109cf7aa1d57,
|
||||
0xc791bc55fece41d2,
|
||||
0xf84c57704e385ec2,
|
||||
0xcb49c1d9c010e60f,
|
||||
0xacdb8e158bfe3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aefcb98b15f8306,
|
||||
0x3ea1108fe4f21d54,
|
||||
0xcf79f69fa1b7df3b,
|
||||
0xe4f54aa1d16b1a3c,
|
||||
0xba5e4ef86105a679,
|
||||
0xed86c0797bee5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5cb98b15c2db4,
|
||||
0x71591082d23a1d51,
|
||||
0xd76230e944a17ca4,
|
||||
0xd19e3dd3549dd5b6,
|
||||
0xa972dc1701fa66e3,
|
||||
0x12e31f2dd6bde7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2acb98b1732d9d,
|
||||
0x2cfd10dd06961d64,
|
||||
0x7396b86c6ef24e8,
|
||||
0xbd76e2fdb1bfc820,
|
||||
0x6afea7f6de94d0d5,
|
||||
0x10994b0c5744c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9cb98b1b82d58,
|
||||
0x5fe911eba3aa1d9d,
|
||||
0x96bf1b5f4dd81db3,
|
||||
0x8100d27cc9259f5b,
|
||||
0xafa20b9674640eab,
|
||||
0x9bbcea7d8d9497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x303cb98b1662daa,
|
||||
0xd93110aa0a621d5a,
|
||||
0xbfa9820c5be4a468,
|
||||
0xba3643ecb05a348,
|
||||
0xdc3534bb1f1c25a6,
|
||||
0x6c305bb19c0e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9cb98b162d858,
|
||||
0xbe9109cf7aa1d57,
|
||||
0xc791bc55fece41d2,
|
||||
0xf84c57704e385ec2,
|
||||
0xcb49c1d9c010e60f,
|
||||
0xacdb8e158bfe3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aefcb98b15f8306,
|
||||
0x3ea1108fe4f21d54,
|
||||
0xcf79f69fa1b7df3b,
|
||||
0xe4f54aa1d16b1a3c,
|
||||
0xba5e4ef86105a679,
|
||||
0xed86c0797bee5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5cb98b15c2db4,
|
||||
0x71591082d23a1d51,
|
||||
0xd76230e944a17ca4,
|
||||
0xd19e3dd3549dd5b6,
|
||||
0xa972dc1701fa66e3,
|
||||
0x12e31f2dd6bde7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2acb98b1732d9d,
|
||||
0x2cfd10dd06961d64,
|
||||
0x7396b86c6ef24e8,
|
||||
0xbd76e2fdb1bfc820,
|
||||
0x6afea7f6de94d0d5,
|
||||
0x10994b0c5744c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let b = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9cb98b1b82d58,
|
||||
0x5fe911eba3aa1d9d,
|
||||
0x96bf1b5f4dd81db3,
|
||||
0x8100d272c9259f5b,
|
||||
0xafa20b9674640eab,
|
||||
0x9bbcea7d8d9497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x303cb98b1662daa,
|
||||
0xd93110aa0a621d5a,
|
||||
0xbfa9820c5be4a468,
|
||||
0xba3643ecb05a348,
|
||||
0xdc3534bb1f1c25a6,
|
||||
0x6c305bb19c0e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9cb98b162d858,
|
||||
0xbe9109cf7aa1d57,
|
||||
0xc791bc55fece41d2,
|
||||
0xf84c57704e385ec2,
|
||||
0xcb49c1d9c010e60f,
|
||||
0xacdb8e158bfe348,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aefcb98b15f8306,
|
||||
0x3ea1108fe4f21d54,
|
||||
0xcf79f69fa1b7df3b,
|
||||
0xe4f54aa1d16b1a3c,
|
||||
0xba5e4ef86105a679,
|
||||
0xed86c0797bee5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5cb98b15c2db4,
|
||||
0x71591082d23a1d51,
|
||||
0xd76230e944a17ca4,
|
||||
0xd19e3dd3549dd5b6,
|
||||
0xa972dc1701fa66e3,
|
||||
0x12e31f2dd6bde7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2acb98b1732d9d,
|
||||
0x2cfd10dd06961d64,
|
||||
0x7396b86c6ef24e8,
|
||||
0xbd76e2fdb1bfc820,
|
||||
0x6afea7f6de94d0d5,
|
||||
0x10994b0c5744c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9cb98b1b82d58,
|
||||
0x5fe911eba3aa1d9d,
|
||||
0x96bf1b5f4dd21db3,
|
||||
0x8100d27cc9259f5b,
|
||||
0xafa20b9674640eab,
|
||||
0x9bbcea7d8d9497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x303cb98b1662daa,
|
||||
0xd93110aa0a621d5a,
|
||||
0xbfa9820c5be4a468,
|
||||
0xba3643ecb05a348,
|
||||
0xdc3534bb1f1c25a6,
|
||||
0x6c305bb19c0e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9cb98b162d858,
|
||||
0xbe9109cf7aa1d57,
|
||||
0xc791bc55fece41d2,
|
||||
0xf84c57704e385ec2,
|
||||
0xcb49c1d9c010e60f,
|
||||
0xacdb8e158bfe3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aefcb98b15f8306,
|
||||
0x3ea1108fe4f21d54,
|
||||
0xcf79f69fa117df3b,
|
||||
0xe4f54aa1d16b1a3c,
|
||||
0xba5e4ef86105a679,
|
||||
0xed86c0797bee5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5cb98b15c2db4,
|
||||
0x71591082d23a1d51,
|
||||
0xd76230e944a17ca4,
|
||||
0xd19e3dd3549dd5b6,
|
||||
0xa972dc1701fa66e3,
|
||||
0x12e31f2dd6bde7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2acb98b1732d9d,
|
||||
0x2cfd10dd06961d64,
|
||||
0x7396b86c6ef24e8,
|
||||
0xbd76e2fdb1bfc820,
|
||||
0x6afea7f6de94d0d5,
|
||||
0x10994b0c5744c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let c = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9cb9871b82d58,
|
||||
0x5fe911eba3aa1d9d,
|
||||
0x96bf1b5f4dd81db3,
|
||||
0x8100d27cc9259f5b,
|
||||
0xafa20b9674640eab,
|
||||
0x9bbcea7d8d9497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x303cb98b1662daa,
|
||||
0xd93110aa0a621d5a,
|
||||
0xbfa9820c5be4a468,
|
||||
0xba3643ecb05a348,
|
||||
0xdc3534bb1f1c25a6,
|
||||
0x6c305bb19c0e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9cb98b162d858,
|
||||
0xbe9109cf7aa1d57,
|
||||
0x7791bc55fece41d2,
|
||||
0xf84c57704e385ec2,
|
||||
0xcb49c1d9c010e60f,
|
||||
0xacdb8e158bfe3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aefcb98b15f8306,
|
||||
0x3ea1108fe4f21d54,
|
||||
0xcf79f69fa1b7df3b,
|
||||
0xe4f54aa1d16b133c,
|
||||
0xba5e4ef86105a679,
|
||||
0xed86c0797bee5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5cb98b15c2db4,
|
||||
0x71591082d23a1d51,
|
||||
0xd76240e944a17ca4,
|
||||
0xd19e3dd3549dd5b6,
|
||||
0xa972dc1701fa66e3,
|
||||
0x12e31f2dd6bde7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2acb98b1732d9d,
|
||||
0x2cfd10dd06961d64,
|
||||
0x7396b86c6ef24e8,
|
||||
0xbd76e2fdb1bfc820,
|
||||
0x6afea7f6de94d0d5,
|
||||
0x10994b0c1744c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9cb98b1b82d58,
|
||||
0x5fe911eba3aa1d9d,
|
||||
0x96bf1b5f4dd81db3,
|
||||
0x8100d27cc9259f5b,
|
||||
0xafa20b9674640eab,
|
||||
0x9bbcea7d8d9497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x303cb98b1662daa,
|
||||
0xd93110aa0a621d5a,
|
||||
0xbfa9820c5be4a468,
|
||||
0xba3643ecb05a348,
|
||||
0xdc3534bb1f1c25a6,
|
||||
0x6c305bb19c0e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9cb98b162d858,
|
||||
0xbe9109cf7aa1d57,
|
||||
0xc791bc55fece41d2,
|
||||
0xf84c57704e385ec2,
|
||||
0xcb49c1d3c010e60f,
|
||||
0xacdb8e158bfe3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aefcb98b15f8306,
|
||||
0x3ea1108fe4f21d54,
|
||||
0xcf79f69fa1b7df3b,
|
||||
0xe4f54aa1d16b1a3c,
|
||||
0xba5e4ef86105a679,
|
||||
0xed86c0797bee5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5cb98b15c2db4,
|
||||
0x71591082d23a1d51,
|
||||
0xd76230e944a17ca4,
|
||||
0xd19e3dd3549dd5b6,
|
||||
0xa972dc1701fa66e3,
|
||||
0x12e31f2dd6bde7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2acb98b1732d9d,
|
||||
0x2cfd10dd06961d64,
|
||||
0x7396b86c6ef24e8,
|
||||
0xbd76e2fdb1bfc820,
|
||||
0x6afea7f6de94d0d5,
|
||||
0x10994b0c57441040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// because a and b and c are similar to each other and
|
||||
// I was lazy, this is just some arbitrary way to make
|
||||
// them a little more different
|
||||
let a = &a.square().invert().unwrap().square() + &c;
|
||||
let b = &b.square().invert().unwrap().square() + &a;
|
||||
let c = &c.square().invert().unwrap().square() + &b;
|
||||
|
||||
assert_eq!(a.square(), &a * &a);
|
||||
assert_eq!(b.square(), &b * &b);
|
||||
assert_eq!(c.square(), &c * &c);
|
||||
|
||||
assert_eq!(
|
||||
(a + b) * c.square(),
|
||||
&(&(&c * &c) * &a) + &(&(&c * &c) * &b)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
&a.invert().unwrap() * &b.invert().unwrap(),
|
||||
(&a * &b).invert().unwrap()
|
||||
);
|
||||
assert_eq!(&a.invert().unwrap() * &a, Fp12::one());
|
||||
|
||||
assert!(a != a.frobenius_map());
|
||||
assert_eq!(
|
||||
a,
|
||||
a.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
);
|
||||
}
|
38
src/fp2.rs
38
src/fp2.rs
|
@ -25,6 +25,15 @@ impl Default for Fp2 {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp2 {
|
||||
fn from(f: Fp) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: f,
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp2 {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
|
||||
|
@ -117,6 +126,35 @@ impl Fp2 {
|
|||
self.c0.is_zero() & self.c1.is_zero()
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
// This is always just a conjugation. If you're curious why, here's
|
||||
// an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/
|
||||
self.conjugate()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn conjugate(&self) -> Self {
|
||||
Fp2 {
|
||||
c0: self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn mul_by_nonresidue(&self) -> Fp2 {
|
||||
// Multiply a + bu by u + 1, getting
|
||||
// au + a + bu^2 + bu
|
||||
// and because u^2 = -1, we get
|
||||
// (a - b) + (a + b)u
|
||||
|
||||
Fp2 {
|
||||
c0: self.c0 - self.c1,
|
||||
c1: self.c0 + self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not this element is strictly lexicographically
|
||||
/// larger than its negation.
|
||||
#[inline]
|
||||
|
|
|
@ -0,0 +1,507 @@
|
|||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$.
|
||||
pub struct Fp6 {
|
||||
pub c0: Fp2,
|
||||
pub c1: Fp2,
|
||||
pub c2: Fp2,
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp6 {
|
||||
fn from(f: Fp) -> Fp6 {
|
||||
Fp6 {
|
||||
c0: Fp2::from(f),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp2> for Fp6 {
|
||||
fn from(f: Fp2) -> Fp6 {
|
||||
Fp6 {
|
||||
c0: f,
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Fp6 {
|
||||
fn eq(&self, other: &Fp6) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for Fp6 {}
|
||||
impl Clone for Fp6 {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp6 {
|
||||
fn default() -> Self {
|
||||
Fp6::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fp6 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp6 {
|
||||
#[inline(always)]
|
||||
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 ConstantTimeEq for Fp6 {
|
||||
#[inline(always)]
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fp6 {
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::zero(),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn one() -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::one(),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 {
|
||||
let b_b = self.c1 * c1;
|
||||
|
||||
let t1 = (self.c1 + self.c2) * c1 - b_b;
|
||||
let t1 = t1.mul_by_nonresidue();
|
||||
|
||||
let t2 = (self.c0 + self.c1) * c1 - b_b;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: b_b,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 {
|
||||
let a_a = self.c0 * c0;
|
||||
let b_b = self.c1 * c1;
|
||||
|
||||
let t1 = (self.c1 + self.c2) * c1 - b_b;
|
||||
let t1 = t1.mul_by_nonresidue() + a_a;
|
||||
|
||||
let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b;
|
||||
|
||||
let t3 = (self.c0 + self.c2) * c0 - a_a + b_b;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: t3,
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply by quadratic nonresidue v.
|
||||
pub fn mul_by_nonresidue(&self) -> Self {
|
||||
// Given a + bv + cv^2, this produces
|
||||
// av + bv^2 + cv^3
|
||||
// but because v^3 = u + 1, we have
|
||||
// c(u + 1) + av + v^2
|
||||
|
||||
Fp6 {
|
||||
c0: self.c2.mul_by_nonresidue(),
|
||||
c1: self.c0,
|
||||
c2: self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
let c0 = self.c0.frobenius_map();
|
||||
let c1 = self.c1.frobenius_map();
|
||||
let c2 = self.c2.frobenius_map();
|
||||
|
||||
// c1 = c1 * (u + 1)^((p - 1) / 3)
|
||||
let c1 = c1
|
||||
* Fp2 {
|
||||
c0: Fp::zero(),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xcd03c9e48671f071,
|
||||
0x5dab22461fcda5d2,
|
||||
0x587042afd3851b95,
|
||||
0x8eb60ebe01bacb9e,
|
||||
0x3f97d6e83d050d2,
|
||||
0x18f0206554638741,
|
||||
]),
|
||||
};
|
||||
|
||||
// c2 = c2 * (u + 1)^((2p - 2) / 3)
|
||||
let c2 = c2
|
||||
* Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x890dc9e4867545c3,
|
||||
0x2af322533285a5d5,
|
||||
0x50880866309b7e2c,
|
||||
0xa20d1b8c7e881024,
|
||||
0x14e4f04fe2db9068,
|
||||
0x14e56d3f1564853a,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
};
|
||||
|
||||
Fp6 { c0, c1, c2 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square(&self) -> Self {
|
||||
let s0 = self.c0.square();
|
||||
let ab = self.c0 * self.c1;
|
||||
let s1 = ab + ab;
|
||||
let s2 = (self.c0 - self.c1 + self.c2).square();
|
||||
let bc = self.c1 * self.c2;
|
||||
let s3 = bc + bc;
|
||||
let s4 = self.c2.square();
|
||||
|
||||
Fp6 {
|
||||
c0: s3.mul_by_nonresidue() + s0,
|
||||
c1: s4.mul_by_nonresidue() + s1,
|
||||
c2: s1 + s2 + s3 - s0 - s4,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
let c0 = (self.c1 * self.c2).mul_by_nonresidue();
|
||||
let c0 = self.c0.square() - c0;
|
||||
|
||||
let c1 = self.c2.square().mul_by_nonresidue();
|
||||
let c1 = c1 - (self.c0 * self.c1);
|
||||
|
||||
let c2 = self.c1.square();
|
||||
let c2 = c2 - (self.c0 * self.c2);
|
||||
|
||||
let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue();
|
||||
let tmp = tmp + (self.c0 * c0);
|
||||
|
||||
tmp.invert().map(|t| Fp6 {
|
||||
c0: t * c0,
|
||||
c1: t * c1,
|
||||
c2: t * c2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: &'b Fp6) -> Self::Output {
|
||||
let aa = self.c0 * other.c0;
|
||||
let bb = self.c1 * other.c1;
|
||||
let cc = self.c2 * other.c2;
|
||||
|
||||
let t1 = other.c1 + other.c2;
|
||||
let tmp = self.c1 + self.c2;
|
||||
let t1 = t1 * tmp;
|
||||
let t1 = t1 - bb;
|
||||
let t1 = t1 - cc;
|
||||
let t1 = t1.mul_by_nonresidue();
|
||||
let t1 = t1 + aa;
|
||||
|
||||
let t3 = other.c0 + other.c2;
|
||||
let tmp = self.c0 + self.c2;
|
||||
let t3 = t3 * tmp;
|
||||
let t3 = t3 - aa;
|
||||
let t3 = t3 + bb;
|
||||
let t3 = t3 - cc;
|
||||
|
||||
let t2 = other.c0 + other.c1;
|
||||
let tmp = self.c0 + self.c1;
|
||||
let t2 = t2 * tmp;
|
||||
let t2 = t2 - aa;
|
||||
let t2 = t2 - bb;
|
||||
let cc = cc.mul_by_nonresidue();
|
||||
let t2 = t2 + cc;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: t3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp6) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: self.c0 + rhs.c0,
|
||||
c1: self.c1 + rhs.c1,
|
||||
c2: self.c2 + rhs.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: -self.c0,
|
||||
c1: -self.c1,
|
||||
c2: -self.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp6) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: self.c0 - rhs.c0,
|
||||
c1: self.c1 - rhs.c1,
|
||||
c2: self.c2 - rhs.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp6, Fp6);
|
||||
impl_binops_multiplicative!(Fp6, Fp6);
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic() {
|
||||
use crate::fp::*;
|
||||
|
||||
let a = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9cb98b1b82d58,
|
||||
0x5fe911eba3aa1d9d,
|
||||
0x96bf1b5f4dd81db3,
|
||||
0x8100d27cc9259f5b,
|
||||
0xafa20b9674640eab,
|
||||
0x9bbcea7d8d9497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x303cb98b1662daa,
|
||||
0xd93110aa0a621d5a,
|
||||
0xbfa9820c5be4a468,
|
||||
0xba3643ecb05a348,
|
||||
0xdc3534bb1f1c25a6,
|
||||
0x6c305bb19c0e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9cb98b162d858,
|
||||
0xbe9109cf7aa1d57,
|
||||
0xc791bc55fece41d2,
|
||||
0xf84c57704e385ec2,
|
||||
0xcb49c1d9c010e60f,
|
||||
0xacdb8e158bfe3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aefcb98b15f8306,
|
||||
0x3ea1108fe4f21d54,
|
||||
0xcf79f69fa1b7df3b,
|
||||
0xe4f54aa1d16b1a3c,
|
||||
0xba5e4ef86105a679,
|
||||
0xed86c0797bee5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5cb98b15c2db4,
|
||||
0x71591082d23a1d51,
|
||||
0xd76230e944a17ca4,
|
||||
0xd19e3dd3549dd5b6,
|
||||
0xa972dc1701fa66e3,
|
||||
0x12e31f2dd6bde7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2acb98b1732d9d,
|
||||
0x2cfd10dd06961d64,
|
||||
0x7396b86c6ef24e8,
|
||||
0xbd76e2fdb1bfc820,
|
||||
0x6afea7f6de94d0d5,
|
||||
0x10994b0c5744c040,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
let b = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xf120cb98b16fd84b,
|
||||
0x5fb510cff3de1d61,
|
||||
0xf21a5d069d8c251,
|
||||
0xaa1fd62f34f2839a,
|
||||
0x5a1335157f89913f,
|
||||
0x14a3fe329643c247,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x3516cb98b16c82f9,
|
||||
0x926d10c2e1261d5f,
|
||||
0x1709e01a0cc25fba,
|
||||
0x96c8c960b8253f14,
|
||||
0x4927c234207e51a9,
|
||||
0x18aeb158d542c44e,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xbf0dcb98b16982fc,
|
||||
0xa67910b71d1a1d5c,
|
||||
0xb7c147c2b8fb06ff,
|
||||
0x1efa710d47d2e7ce,
|
||||
0xed20a79c7e27653c,
|
||||
0x2b85294dac1dfba,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x9d52cb98b18082e5,
|
||||
0x621d111151761d6f,
|
||||
0xe79882603b48af43,
|
||||
0xad31637a4f4da37,
|
||||
0xaeac737c5ac1cf2e,
|
||||
0x6e7e735b48b824,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xe148cb98b17d2d93,
|
||||
0x94d511043ebe1d6c,
|
||||
0xef80bca9de324cac,
|
||||
0xf77c0969282795b1,
|
||||
0x9dc1009afbb68f97,
|
||||
0x47931999a47ba2b,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x253ecb98b179d841,
|
||||
0xc78d10f72c061d6a,
|
||||
0xf768f6f3811bea15,
|
||||
0xe424fc9aab5a512b,
|
||||
0x8cd58db99cab5001,
|
||||
0x883e4bfd946bc32,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
let c = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x6934cb98b17682ef,
|
||||
0xfa4510ea194e1d67,
|
||||
0xff51313d2405877e,
|
||||
0xd0cdefcc2e8d0ca5,
|
||||
0x7bea1ad83da0106b,
|
||||
0xc8e97e61845be39,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x4779cb98b18d82d8,
|
||||
0xb5e911444daa1d7a,
|
||||
0x2f286bdaa6532fc2,
|
||||
0xbca694f68baeff0f,
|
||||
0x3d75e6b81a3a7a5d,
|
||||
0xa44c3c498cc96a3,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x8b6fcb98b18a2d86,
|
||||
0xe8a111373af21d77,
|
||||
0x3710a624493ccd2b,
|
||||
0xa94f88280ee1ba89,
|
||||
0x2c8a73d6bb2f3ac7,
|
||||
0xe4f76ead7cb98aa,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xcf65cb98b186d834,
|
||||
0x1b59112a283a1d74,
|
||||
0x3ef8e06dec266a95,
|
||||
0x95f87b5992147603,
|
||||
0x1b9f00f55c23fb31,
|
||||
0x125a2a1116ca9ab1,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x135bcb98b18382e2,
|
||||
0x4e11111d15821d72,
|
||||
0x46e11ab78f1007fe,
|
||||
0x82a16e8b1547317d,
|
||||
0xab38e13fd18bb9b,
|
||||
0x1664dd3755c99cb8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xce65cb98b1318334,
|
||||
0xc7590fdb7c3a1d2e,
|
||||
0x6fcb81649d1c8eb3,
|
||||
0xd44004d1727356a,
|
||||
0x3746b738a7d0d296,
|
||||
0x136c144a96b134fc,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(a.square(), &a * &a);
|
||||
assert_eq!(b.square(), &b * &b);
|
||||
assert_eq!(c.square(), &c * &c);
|
||||
|
||||
assert_eq!(
|
||||
(a + b) * c.square(),
|
||||
&(&(&c * &c) * &a) + &(&(&c * &c) * &b)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
&a.invert().unwrap() * &b.invert().unwrap(),
|
||||
(&a * &b).invert().unwrap()
|
||||
);
|
||||
assert_eq!(&a.invert().unwrap() * &a, Fp6::one());
|
||||
}
|
|
@ -15,8 +15,8 @@ use crate::Scalar;
|
|||
/// "unchecked" API was misused.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct G1Affine {
|
||||
x: Fp,
|
||||
y: Fp,
|
||||
pub(crate) x: Fp,
|
||||
pub(crate) y: Fp,
|
||||
infinity: Choice,
|
||||
}
|
||||
|
||||
|
|
10
src/g2.rs
10
src/g2.rs
|
@ -16,8 +16,8 @@ use crate::Scalar;
|
|||
/// "unchecked" API was misused.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct G2Affine {
|
||||
x: Fp2,
|
||||
y: Fp2,
|
||||
pub(crate) x: Fp2,
|
||||
pub(crate) y: Fp2,
|
||||
infinity: Choice,
|
||||
}
|
||||
|
||||
|
@ -465,9 +465,9 @@ impl G2Affine {
|
|||
/// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct G2Projective {
|
||||
x: Fp2,
|
||||
y: Fp2,
|
||||
z: Fp2,
|
||||
pub(crate) x: Fp2,
|
||||
pub(crate) y: Fp2,
|
||||
pub(crate) z: Fp2,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a G2Affine> for G2Projective {
|
||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -15,6 +15,11 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::unreadable_literal)]
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
// This lint is described at
|
||||
// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
|
||||
// In our library, some of the arithmetic involving extension fields will necessarily
|
||||
// involve various binary operators, and so this lint is triggered unnecessarily.
|
||||
#![allow(clippy::suspicious_arithmetic_impl)]
|
||||
|
||||
#[cfg(feature = "pairings")]
|
||||
extern crate alloc;
|
||||
|
@ -54,6 +59,20 @@ pub use g1::{G1Affine, G1Projective};
|
|||
#[cfg(feature = "groups")]
|
||||
pub use g2::{G2Affine, G2Projective};
|
||||
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp12;
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp6;
|
||||
|
||||
// The BLS parameter x for BLS12-381 is -0xd201000000010000
|
||||
const BLS_X: u64 = 0xd201000000010000;
|
||||
const BLS_X_IS_NEGATIVE: bool = true;
|
||||
|
||||
#[cfg(feature = "groups")]
|
||||
mod pairings;
|
||||
|
||||
pub use pairings::{pairing, Gt, MillerLoopResult};
|
||||
|
||||
// TODO: This should be upstreamed to subtle.
|
||||
// See https://github.com/dalek-cryptography/subtle/pull/48
|
||||
trait CtOptionExt<T> {
|
||||
|
|
|
@ -0,0 +1,416 @@
|
|||
use crate::fp12::Fp12;
|
||||
use crate::fp2::Fp2;
|
||||
use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE};
|
||||
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
/// Represents results of a Miller loop, one of the most expensive portions
|
||||
/// of the pairing function. `MillerLoopResult`s cannot be compared with each
|
||||
/// other until `.final_exponentiation()` is called, which is also expensive.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MillerLoopResult(pub(crate) Fp12);
|
||||
|
||||
impl ConditionallySelectable for MillerLoopResult {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
MillerLoopResult(Fp12::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
impl MillerLoopResult {
|
||||
/// This performs a "final exponentiation" routine to convert the result
|
||||
/// of a Miller loop into an element of `Gt` so that it can be compared
|
||||
/// with other elements of `Gt`.
|
||||
pub fn final_exponentiation(&self) -> Gt {
|
||||
let r = &self.0;
|
||||
|
||||
#[must_use]
|
||||
fn exp_by_x(f: Fp12, x: u64) -> Fp12 {
|
||||
let mut tmp = Fp12::one();
|
||||
let mut found_one = false;
|
||||
for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) {
|
||||
if found_one {
|
||||
tmp = tmp.square()
|
||||
} else {
|
||||
found_one = i;
|
||||
}
|
||||
|
||||
if i {
|
||||
tmp *= f;
|
||||
}
|
||||
}
|
||||
|
||||
if BLS_X_IS_NEGATIVE {
|
||||
tmp = tmp.conjugate();
|
||||
}
|
||||
|
||||
tmp
|
||||
}
|
||||
|
||||
let f1 = r.conjugate();
|
||||
|
||||
Gt(r.invert()
|
||||
.map(|mut f2| {
|
||||
let mut r = f1;
|
||||
r *= f2;
|
||||
f2 = r;
|
||||
r = r.frobenius_map().frobenius_map();
|
||||
r *= f2;
|
||||
let mut x = BLS_X;
|
||||
let y0 = r.square();
|
||||
let mut y1 = y0;
|
||||
y1 = exp_by_x(y1, x);
|
||||
x >>= 1;
|
||||
let mut y2 = y1;
|
||||
y2 = exp_by_x(y2, x);
|
||||
x <<= 1;
|
||||
let mut y3 = r;
|
||||
y3 = y3.conjugate();
|
||||
y1 *= y3;
|
||||
y1 = y1.conjugate();
|
||||
y1 *= y2;
|
||||
y2 = y1;
|
||||
y2 = exp_by_x(y2, x);
|
||||
y3 = y2;
|
||||
y3 = exp_by_x(y3, x);
|
||||
y1 = y1.conjugate();
|
||||
y3 *= y1;
|
||||
y1 = y1.conjugate();
|
||||
y1 = y1.frobenius_map().frobenius_map().frobenius_map();
|
||||
y2 = y2.frobenius_map().frobenius_map();
|
||||
y1 *= y2;
|
||||
y2 = y3;
|
||||
y2 = exp_by_x(y2, x);
|
||||
y2 *= y0;
|
||||
y2 *= r;
|
||||
y1 *= y2;
|
||||
y2 = y3;
|
||||
y2 = y2.frobenius_map();
|
||||
y1 *= y2;
|
||||
|
||||
y1
|
||||
})
|
||||
// We unwrap() because `MillerLoopResult` can only be constructed
|
||||
// by a function within this crate, and we uphold the invariant
|
||||
// that the enclosed value is nonzero.
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b MillerLoopResult> for &'a MillerLoopResult {
|
||||
type Output = MillerLoopResult;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b MillerLoopResult) -> MillerLoopResult {
|
||||
MillerLoopResult(self.0 * rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopResult);
|
||||
|
||||
/// This is an element of $\mathbb{G}_T$, the target group of the pairing function. As with
|
||||
/// $\mathbb{G}_1$ and $\mathbb{G}_2$ this group has order $q$.
|
||||
///
|
||||
/// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to
|
||||
/// keep code and abstractions consistent.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Gt(Fp12);
|
||||
|
||||
impl ConstantTimeEq for Gt {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.0.ct_eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Gt {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Gt(Fp12::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Gt {}
|
||||
impl PartialEq for Gt {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
bool::from(self.ct_eq(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Gt {
|
||||
/// Returns the group identity, which is $1$.
|
||||
pub fn identity() -> Gt {
|
||||
Gt(Fp12::one())
|
||||
}
|
||||
|
||||
/// Doubles this group element.
|
||||
pub fn double(&self) -> Gt {
|
||||
Gt(self.0.square())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Gt {
|
||||
// The element is unitary, so we just conjugate.
|
||||
Gt(self.0.conjugate())
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Gt {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Gt> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Gt) -> Gt {
|
||||
Gt(self.0 * rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Gt> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Gt) -> Gt {
|
||||
self + (-rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Scalar> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
fn mul(self, other: &'b Scalar) -> Self::Output {
|
||||
let mut acc = Gt::identity();
|
||||
|
||||
// This is a simple double-and-add implementation of group element
|
||||
// multiplication, moving from most significant to least
|
||||
// significant bit of the scalar.
|
||||
//
|
||||
// We skip the leading bit because it's always unset for Fq
|
||||
// elements.
|
||||
for bit in other
|
||||
.to_bytes()
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
||||
.skip(1)
|
||||
{
|
||||
acc = acc.double();
|
||||
acc = Gt::conditional_select(&acc, &(acc + self), bit);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Gt, Gt);
|
||||
impl_binops_multiplicative!(Gt, Scalar);
|
||||
|
||||
/// Invoke the pairing function without the use of precomputation and other optimizations.
|
||||
pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt {
|
||||
struct Adder {
|
||||
cur: G2Projective,
|
||||
base: G2Affine,
|
||||
}
|
||||
|
||||
impl MillerLoopDriver for Adder {
|
||||
type Output = Fp12;
|
||||
|
||||
fn doubling_step(&mut self, f: Self::Output, p: &G1Affine) -> Self::Output {
|
||||
let coeffs = doubling_step(&mut self.cur);
|
||||
ell(f, &coeffs, p)
|
||||
}
|
||||
fn addition_step(&mut self, f: Self::Output, p: &G1Affine) -> Self::Output {
|
||||
let coeffs = addition_step(&mut self.cur, &self.base);
|
||||
ell(f, &coeffs, p)
|
||||
}
|
||||
fn square_output(f: Self::Output) -> Self::Output {
|
||||
f.square()
|
||||
}
|
||||
fn conjugate(f: Self::Output) -> Self::Output {
|
||||
f.conjugate()
|
||||
}
|
||||
fn one() -> Self::Output {
|
||||
Fp12::one()
|
||||
}
|
||||
}
|
||||
|
||||
let either_identity = p.is_identity() | q.is_identity();
|
||||
let p = G1Affine::conditional_select(&p, &G1Affine::generator(), either_identity);
|
||||
let q = G2Affine::conditional_select(&q, &G2Affine::generator(), either_identity);
|
||||
|
||||
let mut adder = Adder {
|
||||
cur: G2Projective::from(q),
|
||||
base: q,
|
||||
};
|
||||
|
||||
let tmp = miller_loop(&mut adder, &p);
|
||||
let tmp = MillerLoopResult(Fp12::conditional_select(
|
||||
&tmp,
|
||||
&Fp12::one(),
|
||||
either_identity,
|
||||
));
|
||||
tmp.final_exponentiation()
|
||||
}
|
||||
|
||||
trait MillerLoopDriver {
|
||||
type Output;
|
||||
|
||||
fn doubling_step(&mut self, f: Self::Output, p: &G1Affine) -> Self::Output;
|
||||
fn addition_step(&mut self, f: Self::Output, p: &G1Affine) -> Self::Output;
|
||||
fn square_output(f: Self::Output) -> Self::Output;
|
||||
fn conjugate(f: Self::Output) -> Self::Output;
|
||||
fn one() -> Self::Output;
|
||||
}
|
||||
|
||||
/// This is a "generic" implementation of the Miller loop to avoid duplicating code
|
||||
/// structure elsewhere; instead, we'll write concrete instantiations of
|
||||
/// `MillerLoopDriver` for whatever purposes we need (such as caching modes).
|
||||
fn miller_loop<D: MillerLoopDriver>(driver: &mut D, p: &G1Affine) -> D::Output {
|
||||
let mut f = D::one();
|
||||
|
||||
let mut found_one = false;
|
||||
for i in (0..64).rev().map(|b| (((BLS_X >> 1) >> b) & 1) == 1) {
|
||||
if !found_one {
|
||||
found_one = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
f = driver.doubling_step(f, p);
|
||||
|
||||
if i {
|
||||
f = driver.addition_step(f, p);
|
||||
}
|
||||
|
||||
f = D::square_output(f);
|
||||
}
|
||||
|
||||
f = driver.doubling_step(f, p);
|
||||
|
||||
if BLS_X_IS_NEGATIVE {
|
||||
f = D::conjugate(f);
|
||||
}
|
||||
|
||||
f
|
||||
}
|
||||
|
||||
fn ell(f: Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) -> Fp12 {
|
||||
let mut c0 = coeffs.0;
|
||||
let mut c1 = coeffs.1;
|
||||
|
||||
c0.c0 *= p.y;
|
||||
c0.c1 *= p.y;
|
||||
|
||||
c1.c0 *= p.x;
|
||||
c1.c1 *= p.x;
|
||||
|
||||
f.mul_by_014(&coeffs.2, &c1, &c0)
|
||||
}
|
||||
|
||||
fn doubling_step(r: &mut G2Projective) -> (Fp2, Fp2, Fp2) {
|
||||
// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
|
||||
let tmp0 = r.x.square();
|
||||
let tmp1 = r.y.square();
|
||||
let tmp2 = tmp1.square();
|
||||
let tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2;
|
||||
let tmp3 = tmp3 + tmp3;
|
||||
let tmp4 = tmp0 + tmp0 + tmp0;
|
||||
let tmp6 = r.x + tmp4;
|
||||
let tmp5 = tmp4.square();
|
||||
let zsquared = r.z.square();
|
||||
r.x = tmp5 - tmp3 - tmp3;
|
||||
r.z = (r.z + r.y).square() - tmp1 - zsquared;
|
||||
r.y = (tmp3 - r.x) * tmp4;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
r.y -= tmp2;
|
||||
let tmp3 = tmp4 * zsquared;
|
||||
let tmp3 = tmp3 + tmp3;
|
||||
let tmp3 = -tmp3;
|
||||
let tmp6 = tmp6.square() - tmp0 - tmp5;
|
||||
let tmp1 = tmp1 + tmp1;
|
||||
let tmp1 = tmp1 + tmp1;
|
||||
let tmp6 = tmp6 - tmp1;
|
||||
let tmp0 = r.z * zsquared;
|
||||
let tmp0 = tmp0 + tmp0;
|
||||
|
||||
(tmp0, tmp3, tmp6)
|
||||
}
|
||||
|
||||
fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) {
|
||||
// Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
|
||||
let zsquared = r.z.square();
|
||||
let ysquared = q.y.square();
|
||||
let t0 = zsquared * q.x;
|
||||
let t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared;
|
||||
let t2 = t0 - r.x;
|
||||
let t3 = t2.square();
|
||||
let t4 = t3 + t3;
|
||||
let t4 = t4 + t4;
|
||||
let t5 = t4 * t2;
|
||||
let t6 = t1 - r.y - r.y;
|
||||
let t9 = t6 * q.x;
|
||||
let t7 = t4 * r.x;
|
||||
r.x = t6.square() - t5 - t7 - t7;
|
||||
r.z = (r.z + t2).square() - zsquared - t3;
|
||||
let t10 = q.y + r.z;
|
||||
let t8 = (t7 - r.x) * t6;
|
||||
let t0 = r.y * t5;
|
||||
let t0 = t0 + t0;
|
||||
r.y = t8 - t0;
|
||||
let t10 = t10.square() - ysquared;
|
||||
let ztsquared = r.z.square();
|
||||
let t10 = t10 - ztsquared;
|
||||
let t9 = t9 + t9 - t10;
|
||||
let t10 = r.z + r.z;
|
||||
let t6 = -t6;
|
||||
let t1 = t6 + t6;
|
||||
|
||||
(t10, t1, t9)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bilinearity() {
|
||||
use crate::Scalar;
|
||||
|
||||
let a = Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square();
|
||||
let b = Scalar::from_raw([5, 6, 7, 8]).invert().unwrap().square();
|
||||
let c = a * b;
|
||||
|
||||
let g = G1Affine::from(G1Affine::generator() * a);
|
||||
let h = G2Affine::from(G2Affine::generator() * b);
|
||||
let p = pairing(&g, &h);
|
||||
|
||||
assert!(p != Gt::identity());
|
||||
|
||||
let expected = G1Affine::from(G1Affine::generator() * c);
|
||||
|
||||
assert_eq!(p, pairing(&expected, &G2Affine::generator()));
|
||||
assert_eq!(
|
||||
p,
|
||||
pairing(&G1Affine::generator(), &G2Affine::generator()) * c
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unitary() {
|
||||
let g = G1Affine::generator();
|
||||
let h = G2Affine::generator();
|
||||
let p = -pairing(&g, &h);
|
||||
let q = pairing(&g, &-h);
|
||||
let r = pairing(&-g, &h);
|
||||
|
||||
assert_eq!(p, q);
|
||||
assert_eq!(q, r);
|
||||
}
|
67
src/util.rs
67
src/util.rs
|
@ -19,17 +19,8 @@ pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) {
|
|||
(ret as u64, (ret >> 64) as u64)
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_additive_specify_output {
|
||||
macro_rules! impl_add_binop_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Sub<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b $rhs) -> $output {
|
||||
&self - rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Add<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
|
@ -39,15 +30,6 @@ macro_rules! impl_binops_additive_specify_output {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
self - &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
|
@ -57,15 +39,6 @@ macro_rules! impl_binops_additive_specify_output {
|
|||
}
|
||||
}
|
||||
|
||||
impl Sub<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
&self - &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
|
@ -77,6 +50,44 @@ macro_rules! impl_binops_additive_specify_output {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_sub_binop_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Sub<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b $rhs) -> $output {
|
||||
&self - rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
self - &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
&self - &rhs
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_additive_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl_add_binop_specify_output!($lhs, $rhs, $output);
|
||||
impl_sub_binop_specify_output!($lhs, $rhs, $output);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_multiplicative_mixed {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Mul<&'b $rhs> for $lhs {
|
||||
|
|
Loading…
Reference in New Issue