Skip to content

Commit

Permalink
Add BLS12-381 (#162)
Browse files Browse the repository at this point in the history
* unify field representation

* allow flexible flag config

* add bls12381

* fix rebase residues

* add suggestions
  • Loading branch information
kilic authored Jul 17, 2024
1 parent d34e9e4 commit 758cbf9
Show file tree
Hide file tree
Showing 20 changed files with 2,706 additions and 467 deletions.
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
}

/// 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

0 comments on commit 758cbf9

Please sign in to comment.