Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BLS12-381 #162

Merged
merged 5 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 31 additions & 19 deletions derive/src/field/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream {
let modulus_limbs_32_ident = quote! {[#(#modulus_limbs_32,)*]};

let to_token = |e: &BigUint| big_to_token(e, num_limbs);
let half_modulus = (&modulus - 1usize) >> 1;
let half_modulus = to_token(&half_modulus);

// binary modulus
let t = BigUint::from(1u64) << (num_limbs * limb_size as usize);
Expand Down Expand Up @@ -282,6 +284,17 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream {
}
}

impl crate::serde::endian::EndianRepr for #field {
const ENDIAN: crate::serde::endian::Endian = crate::serde::endian::Endian::#endian;

fn to_bytes(&self) -> Vec<u8> {
self.to_bytes().to_vec()
}

fn from_bytes(bytes: &[u8]) -> subtle::CtOption<Self> {
#field::from_bytes(bytes[..#field::SIZE].try_into().unwrap())
}
}

impl #field {
pub const SIZE: usize = #num_limbs * 8;
Expand Down Expand Up @@ -313,20 +326,20 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream {
/// Attempts to convert a <#endian>-endian byte representation of
/// a scalar into a `$field`, failing if the input is not canonical.
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> subtle::CtOption<Self> {
use crate::serde::endian::EndianRepr;
let mut el = #field::default();
use crate::serde::endian::Endian;
crate::serde::endian::#endian::from_bytes(bytes, &mut el.0);
#field::ENDIAN.from_bytes(bytes, &mut el.0);
subtle::CtOption::new(el * Self::R2, subtle::Choice::from(Self::is_less_than_modulus(&el.0) as u8))
}


/// Converts an element of `$field` into a byte representation in
/// <#endian>-endian byte order.
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
use crate::serde::endian::Endian;
use crate::serde::endian::EndianRepr;
let el = self.from_mont();
let mut res = [0; Self::SIZE];
crate::serde::endian::#endian::to_bytes(&mut res, &el);
#field::ENDIAN.to_bytes(&mut res, &el);
res.into()
}

Expand All @@ -341,18 +354,6 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream {
crate::ff_ext::jacobi::jacobi::<#jacobi_constant>(&self.0, &#modulus_limbs_ident)
}

// Returns the multiplicative inverse of the element. If it is zero, the method fails.
#[inline(always)]
fn invert(&self) -> subtle::CtOption<Self> {
const BYINVERTOR: crate::ff_ext::inverse::BYInverter<#by_inverter_constant> =
crate::ff_ext::inverse::BYInverter::<#by_inverter_constant>::new(&#modulus_limbs_ident, &#r2);

if let Some(inverse) = BYINVERTOR.invert::<{ Self::NUM_LIMBS }>(&self.0) {
subtle::CtOption::new(Self(inverse), subtle::Choice::from(1))
} else {
subtle::CtOption::new(Self::zero(), subtle::Choice::from(0))
}
}

#[inline(always)]
pub(crate) fn is_less_than_modulus(limbs: &[u64; Self::NUM_LIMBS]) -> bool {
Expand All @@ -361,6 +362,18 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream {
});
(borrow as u8) & 1 == 1
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Returns whether or not this element is strictly lexicographically
/// larger than its negation.

/// Returns whether or not this element is strictly lexicographically
/// larger than its negation.
pub fn lexicographically_largest(&self) -> Choice {
const HALF_MODULUS: [u64; #num_limbs]= #half_modulus;
let tmp = self.from_mont();
let borrow = tmp
.into_iter()
.zip(HALF_MODULUS.into_iter())
.fold(0, |borrow, (t, m)| crate::arithmetic::sbb(t, m, borrow).1);
!Choice::from((borrow as u8) & 1)
}
}

impl ff::Field for #field {
Expand Down Expand Up @@ -450,16 +463,15 @@ pub(crate) fn impl_field(input: TokenStream) -> TokenStream {

fn from_repr(repr: Self::Repr) -> subtle::CtOption<Self> {
let mut el = #field::default();
use crate::serde::endian::Endian;
crate::serde::endian::LE::from_bytes(repr.as_ref(), &mut el.0);
crate::serde::endian::Endian::LE.from_bytes(repr.as_ref(), &mut el.0);
subtle::CtOption::new(el * Self::R2, subtle::Choice::from(Self::is_less_than_modulus(&el.0) as u8))
}

fn to_repr(&self) -> Self::Repr {
use crate::serde::endian::Endian;
let el = self.from_mont();
let mut res = [0; #size];
crate::serde::endian::LE::to_bytes(&mut res, &el);
crate::serde::endian::Endian::LE.to_bytes(&mut res, &el);
res.into()
}

Expand Down
126 changes: 126 additions & 0 deletions src/bls12381/engine.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use super::fq12::Fq12;
use super::fq2::Fq2;
use super::{Fr, G1Affine, G2Affine, BLS_X, G1, G2};
use crate::ff_ext::quadratic::QuadSparseMul;
use crate::ff_ext::ExtField;
use core::borrow::Borrow;
use core::iter::Sum;
use core::ops::{Add, Mul, Neg, Sub};
use ff::Field;
use ff::PrimeField;
use group::prime::PrimeCurveAffine;
use group::Group;
use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine};
use rand::RngCore;
use std::ops::MulAssign;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};

crate::impl_gt!(Gt, Fq12, Fr);
crate::impl_miller_loop_components!(Bls12381, G1, G1Affine, G2, G2Affine, Fq12, Gt, Fr);

impl MillerLoopResult for Fq12 {
type Gt = Gt;

fn final_exponentiation(&self) -> Gt {
#[must_use]
fn exp_by_x(f: Fq12) -> Fq12 {
let mut acc = Fq12::one();
for (i, b) in BLS_X.into_iter().enumerate() {
(i != 0).then(|| acc.cyclotomic_square());
(b == 1).then(|| acc *= f);
}
acc.conjugate();
acc
}

let mut t0 = *self;
t0.frobenius_map(6);

Gt(self
.invert()
.map(|mut t1| {
let mut t2 = t0 * t1;
t1 = t2;
t2.frobenius_map(2);
t2 *= t1;
t1 = t2;
t1.cyclotomic_square();
t1.conjugate();
let mut t3 = exp_by_x(t2);
let mut t4 = t3;
t4.cyclotomic_square();
let mut t5 = t1 * t3;
t1 = exp_by_x(t5);
t0 = exp_by_x(t1);
let mut t6 = exp_by_x(t0) * t4;
t4 = exp_by_x(t6);
t5.conjugate();
t4 *= t5 * t2;
t1 *= t2;
t1.frobenius_map(3);
t2.conjugate();
t6 *= t2;
t6.frobenius_map(1);
t3 *= t0;
t3.frobenius_map(2);
t3 * t4 * t1 * t6
})
.unwrap())
}
}

pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Affine)]) -> Fq12 {
let terms = terms
.iter()
.filter_map(|&(p, q)| {
if bool::from(p.is_identity()) || bool::from(q.is_identity()) {
None
} else {
Some((p, q))
}
})
.collect::<Vec<_>>();

let mut f = Fq12::one();
let mut r = terms.iter().map(|(_, q)| q.to_curve()).collect::<Vec<_>>();

for (i, x) in BLS_X.iter().map(|&b| b == 1).skip(1).enumerate() {
if i != 0 {
f.square_assign();
}

terms.iter().zip(r.iter_mut()).for_each(|((p, _), r)| {
double(&mut f, r, p);
});

if x {
for ((p, q), r) in terms.iter().zip(r.iter_mut()) {
add(&mut f, r, q, p);
}
}
}

f.conjugate();
f
}

fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) {
let mut c0 = coeffs.0;
let mut c1 = coeffs.1;
c0.c0.mul_assign(&p.y);
c0.c1.mul_assign(&p.y);
c1.c0.mul_assign(&p.x);
c1.c1.mul_assign(&p.x);
Fq12::mul_by_014(f, &coeffs.2, &c1, &c0);
}

#[cfg(test)]
mod test {
use super::super::{Bls12381, Fr, G1, G2};
use super::{multi_miller_loop, Fq12, G1Affine, G2Affine, Gt};
use ff::Field;
use group::{prime::PrimeCurveAffine, Curve, Group};
use pairing::{Engine as _, MillerLoopResult, PairingCurveAffine};
use rand_core::OsRng;
crate::test_pairing!(Bls12381, G1, G1Affine, G2, G2Affine, Fq12, Gt, Fr);
}
56 changes: 56 additions & 0 deletions src/bls12381/fq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use core::convert::TryInto;
use halo2derive::impl_field;
use rand::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};

impl_field!(
bls12381_base,
Fq,
modulus = "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab",
mul_gen = "2",
zeta = "1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac",
from_uniform = [64, 96],
endian = "big",
);

crate::extend_field_legendre!(Fq);
crate::impl_binops_calls!(Fq);
crate::impl_binops_additive!(Fq, Fq);
crate::impl_binops_multiplicative!(Fq, Fq);
crate::field_bits!(Fq);
crate::serialize_deserialize_primefield!(Fq);
crate::impl_from_u64!(Fq);

use ff::Field;

use crate::ff_ext::ExtField;
const NEGATIVE_ONE: Fq = Fq::ZERO.sub_const(&Fq::ONE);
impl ExtField for Fq {
const NON_RESIDUE: Self = NEGATIVE_ONE;
fn mul_by_nonresidue(&self) -> Self {
self.neg()
}
fn frobenius_map(&mut self, _: usize) {}
}

#[cfg(test)]
mod test {
use super::*;
crate::field_testing_suite!(Fq, "field_arithmetic");
crate::field_testing_suite!(Fq, "conversion");
crate::field_testing_suite!(Fq, "serialization");
crate::field_testing_suite!(Fq, "quadratic_residue");
crate::field_testing_suite!(Fq, "bits");
crate::field_testing_suite!(Fq, "serialization_check");
crate::field_testing_suite!(Fq, "constants");
crate::field_testing_suite!(Fq, "sqrt");
crate::field_testing_suite!(Fq, "zeta");
crate::field_testing_suite!(Fq, "from_uniform_bytes", 64, 96);
#[test]
fn test_fq_mul_nonresidue() {
let e = Fq::random(rand_core::OsRng);
let a0 = e.mul_by_nonresidue();
let a1 = e * Fq::NON_RESIDUE;
assert_eq!(a0, a1);
}
}
Loading
Loading