bls12_381: Implement group traits
This commit is contained in:
parent
964532ec9f
commit
d11b60030f
|
@ -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"]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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()));
|
||||
|
|
Loading…
Reference in New Issue