bls12_381: Implement group traits

This commit is contained in:
Jack Grigg 2020-05-20 12:35:35 +12:00
parent 964532ec9f
commit d11b60030f
5 changed files with 515 additions and 5 deletions

View File

@ -29,6 +29,12 @@ path = "../ff"
version = "0.6"
default-features = false
[dependencies.group]
path = "../group"
version = "0.6"
default-features = false
optional = true
[dependencies.rand_core]
version = "0.5"
default-features = false
@ -39,7 +45,7 @@ default-features = false
[features]
default = ["groups", "pairings", "alloc"]
groups = []
groups = ["group"]
pairings = ["groups"]
alloc = []
nightly = ["subtle/nightly"]

View File

@ -4,7 +4,7 @@
use core::convert::TryFrom;
use core::fmt;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use rand_core::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::util::{adc, mac, sbb};
@ -97,6 +97,16 @@ const R2: Fp = Fp([
0x1198_8fe5_92ca_e3aa,
]);
/// R3 = 2^(384*3) mod p
const R3: Fp = Fp([
0xed48_ac6b_d94c_a1e0,
0x315f_831e_03a7_adf8,
0x9a53_352a_615e_29dd,
0x34c0_4e5e_921e_1761,
0x2512_d435_6572_4728,
0x0aa6_3460_9175_5d4d,
]);
impl<'a> Neg for &'a Fp {
type Output = Fp;
@ -214,6 +224,48 @@ impl Fp {
res
}
pub(crate) fn random<R: RngCore + ?Sized>(rng: &mut R) -> Fp {
let mut bytes = [0u8; 96];
rng.fill_bytes(&mut bytes);
// Parse the random bytes as a big-endian number, to match Fp encoding order.
Fp::from_u768([
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[64..72]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[72..80]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[80..88]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[88..96]).unwrap()),
])
}
/// Reduces a big-endian 64-bit limb representation of a 768-bit number.
fn from_u768(limbs: [u64; 12]) -> Fp {
// We reduce an arbitrary 768-bit number by decomposing it into two 384-bit digits
// with the higher bits multiplied by 2^384. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by R^2, as normal
// 2. the upper bits are multiplied by R^2 * 2^384 = R^3
//
// and computing their sum in the field. It remains to see that arbitrary 384-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than R=2^384 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^384 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `R2` or `R3`.
let d1 = Fp([limbs[11], limbs[10], limbs[9], limbs[8], limbs[7], limbs[6]]);
let d0 = Fp([limbs[5], limbs[4], limbs[3], limbs[2], limbs[1], limbs[0]]);
// Convert to Montgomery form
d0 * R2 + d1 * R3
}
/// Returns whether or not this element is strictly lexicographically
/// larger than its negation.
pub fn lexicographically_largest(&self) -> Choice {

View File

@ -2,7 +2,7 @@
use core::fmt;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use rand_core::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::fp::Fp;
@ -126,6 +126,13 @@ impl Fp2 {
self.c0.is_zero() & self.c1.is_zero()
}
pub(crate) fn random<R: RngCore + ?Sized>(rng: &mut R) -> Fp2 {
Fp2 {
c0: Fp::random(rng),
c1: Fp::random(rng),
}
}
/// Raises this element to p.
#[inline(always)]
pub fn frobenius_map(&self) -> Self {

View File

@ -1,9 +1,14 @@
//! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381.
use core::borrow::Borrow;
use core::fmt;
use core::iter::Sum;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use group::{
prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup},
Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup,
};
use rand_core::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::fp::Fp;
@ -28,6 +33,12 @@ impl Default for G1Affine {
}
}
impl fmt::Display for G1Affine {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl<'a> From<&'a G1Projective> for G1Affine {
fn from(p: &'a G1Projective) -> G1Affine {
let zinv = p.z.invert().unwrap_or(Fp::zero());
@ -410,6 +421,18 @@ pub struct G1Projective {
z: Fp,
}
impl Default for G1Projective {
fn default() -> G1Projective {
G1Projective::identity()
}
}
impl fmt::Display for G1Projective {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl<'a> From<&'a G1Affine> for G1Projective {
fn from(p: &'a G1Affine) -> G1Projective {
G1Projective {
@ -835,6 +858,206 @@ impl G1Projective {
}
}
pub struct G1Compressed([u8; 48]);
impl fmt::Debug for G1Compressed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0[..].fmt(f)
}
}
impl Default for G1Compressed {
fn default() -> Self {
G1Compressed([0; 48])
}
}
impl AsRef<[u8]> for G1Compressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for G1Compressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
pub struct G1Uncompressed([u8; 96]);
impl fmt::Debug for G1Uncompressed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0[..].fmt(f)
}
}
impl Default for G1Uncompressed {
fn default() -> Self {
G1Uncompressed([0; 96])
}
}
impl AsRef<[u8]> for G1Uncompressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for G1Uncompressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl Group for G1Projective {
type Scalar = Scalar;
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
loop {
let x = Fp::random(rng);
let flip_sign = rng.next_u32() % 2 != 0;
// Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4)
let p = ((x.square() * x) + B).sqrt().map(|y| G1Affine {
x,
y: if flip_sign { -y } else { y },
infinity: 0.into(),
});
if p.is_some().into() {
let p = p.unwrap().to_curve().clear_cofactor();
if bool::from(!p.is_identity()) {
return p;
}
}
}
}
fn identity() -> Self {
Self::identity()
}
fn generator() -> Self {
Self::generator()
}
fn is_identity(&self) -> Choice {
self.is_identity()
}
#[must_use]
fn double(&self) -> Self {
self.double()
}
}
impl WnafGroup for G1Projective {
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
const RECOMMENDATIONS: [usize; 12] =
[1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569];
let mut ret = 4;
for r in &RECOMMENDATIONS {
if num_scalars > *r {
ret += 1;
} else {
break;
}
}
ret
}
}
impl PrimeGroup for G1Projective {}
impl Curve for G1Projective {
type AffineRepr = G1Affine;
fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) {
Self::batch_normalize(p, q);
}
fn to_affine(&self) -> Self::AffineRepr {
self.into()
}
}
impl PrimeCurve for G1Projective {
type Affine = G1Affine;
}
impl PrimeCurveAffine for G1Affine {
type Scalar = Scalar;
type Curve = G1Projective;
fn identity() -> Self {
Self::identity()
}
fn generator() -> Self {
Self::generator()
}
fn is_identity(&self) -> Choice {
self.is_identity()
}
fn to_curve(&self) -> Self::Curve {
self.into()
}
}
impl GroupEncoding for G1Projective {
type Repr = G1Compressed;
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
G1Affine::from_bytes(bytes).map(Self::from)
}
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
G1Affine::from_bytes_unchecked(bytes).map(Self::from)
}
fn to_bytes(&self) -> Self::Repr {
G1Affine::from(self).to_bytes()
}
}
impl GroupEncoding for G1Affine {
type Repr = G1Compressed;
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
Self::from_compressed(&bytes.0)
}
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
Self::from_compressed_unchecked(&bytes.0)
}
fn to_bytes(&self) -> Self::Repr {
G1Compressed(self.to_compressed())
}
}
impl UncompressedEncoding for G1Affine {
type Uncompressed = G1Uncompressed;
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self> {
Self::from_uncompressed(&bytes.0)
}
fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self> {
Self::from_uncompressed_unchecked(&bytes.0)
}
fn to_uncompressed(&self) -> Self::Uncompressed {
G1Uncompressed(self.to_uncompressed())
}
}
#[test]
fn test_is_on_curve() {
assert!(bool::from(G1Affine::identity().is_on_curve()));

View File

@ -1,9 +1,14 @@
//! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381.
use core::borrow::Borrow;
use core::fmt;
use core::iter::Sum;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use group::{
prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup},
Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup,
};
use rand_core::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::fp::Fp;
@ -29,6 +34,12 @@ impl Default for G2Affine {
}
}
impl fmt::Display for G2Affine {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl<'a> From<&'a G2Projective> for G2Affine {
fn from(p: &'a G2Projective) -> G2Affine {
let zinv = p.z.invert().unwrap_or(Fp2::zero());
@ -482,6 +493,18 @@ pub struct G2Projective {
pub(crate) z: Fp2,
}
impl Default for G2Projective {
fn default() -> G2Projective {
G2Projective::identity()
}
}
impl fmt::Display for G2Projective {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl<'a> From<&'a G2Affine> for G2Projective {
fn from(p: &'a G2Affine) -> G2Projective {
G2Projective {
@ -1025,6 +1048,205 @@ impl G2Projective {
}
}
pub struct G2Compressed([u8; 96]);
impl fmt::Debug for G2Compressed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0[..].fmt(f)
}
}
impl Default for G2Compressed {
fn default() -> Self {
G2Compressed([0; 96])
}
}
impl AsRef<[u8]> for G2Compressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for G2Compressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
pub struct G2Uncompressed([u8; 192]);
impl fmt::Debug for G2Uncompressed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0[..].fmt(f)
}
}
impl Default for G2Uncompressed {
fn default() -> Self {
G2Uncompressed([0; 192])
}
}
impl AsRef<[u8]> for G2Uncompressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for G2Uncompressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl Group for G2Projective {
type Scalar = Scalar;
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
loop {
let x = Fp2::random(rng);
let flip_sign = rng.next_u32() % 2 != 0;
// Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4)
let p = ((x.square() * x) + B).sqrt().map(|y| G2Affine {
x,
y: if flip_sign { -y } else { y },
infinity: 0.into(),
});
if p.is_some().into() {
let p = p.unwrap().to_curve().clear_cofactor();
if bool::from(!p.is_identity()) {
return p;
}
}
}
}
fn identity() -> Self {
Self::identity()
}
fn generator() -> Self {
Self::generator()
}
fn is_identity(&self) -> Choice {
self.is_identity()
}
#[must_use]
fn double(&self) -> Self {
self.double()
}
}
impl WnafGroup for G2Projective {
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
const RECOMMENDATIONS: [usize; 11] = [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071];
let mut ret = 4;
for r in &RECOMMENDATIONS {
if num_scalars > *r {
ret += 1;
} else {
break;
}
}
ret
}
}
impl PrimeGroup for G2Projective {}
impl Curve for G2Projective {
type AffineRepr = G2Affine;
fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) {
Self::batch_normalize(p, q);
}
fn to_affine(&self) -> Self::AffineRepr {
self.into()
}
}
impl PrimeCurve for G2Projective {
type Affine = G2Affine;
}
impl PrimeCurveAffine for G2Affine {
type Scalar = Scalar;
type Curve = G2Projective;
fn identity() -> Self {
Self::identity()
}
fn generator() -> Self {
Self::generator()
}
fn is_identity(&self) -> Choice {
self.is_identity()
}
fn to_curve(&self) -> Self::Curve {
self.into()
}
}
impl GroupEncoding for G2Projective {
type Repr = G2Compressed;
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
G2Affine::from_bytes(bytes).map(Self::from)
}
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
G2Affine::from_bytes_unchecked(bytes).map(Self::from)
}
fn to_bytes(&self) -> Self::Repr {
G2Affine::from(self).to_bytes()
}
}
impl GroupEncoding for G2Affine {
type Repr = G2Compressed;
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
Self::from_compressed(&bytes.0)
}
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
Self::from_compressed_unchecked(&bytes.0)
}
fn to_bytes(&self) -> Self::Repr {
G2Compressed(self.to_compressed())
}
}
impl UncompressedEncoding for G2Affine {
type Uncompressed = G2Uncompressed;
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self> {
Self::from_uncompressed(&bytes.0)
}
fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self> {
Self::from_uncompressed_unchecked(&bytes.0)
}
fn to_uncompressed(&self) -> Self::Uncompressed {
G2Uncompressed(self.to_uncompressed())
}
}
#[test]
fn test_is_on_curve() {
assert!(bool::from(G2Affine::identity().is_on_curve()));