use core::fmt;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use alloc::vec::Vec;
use dusk_bytes::Serializable;
use group::prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup};
use group::{Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup};
use rand_core::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use super::{BlsScalar, G2Compressed, G2Uncompressed};
#[cfg(feature = "rkyv-impl")]
mod rkyv;
#[cfg(feature = "rkyv-impl")]
pub use self::rkyv::{ArchivedG2Affine, G2AffineResolver, InvalidG2Affine};
const UNCOMPRESSED_REJECTED_FLAGS: u8 = 0x80 | 0x20;
const H_EFF_G2: [u8; 80] = [
0x51, 0x55, 0xa9, 0xaa, 0x05, 0x00, 0x02, 0xe8, 0xb4, 0xf6, 0xbb, 0xde, 0x0a, 0x4c, 0x89, 0x59,
0xa3, 0xf6, 0x89, 0x66, 0xc0, 0xcb, 0x54, 0xe9, 0x1a, 0x7c, 0x47, 0xd7, 0x69, 0xec, 0xc0, 0x2e,
0xb0, 0x12, 0x12, 0x5d, 0x01, 0xbf, 0x82, 0x6d, 0x95, 0xdb, 0x31, 0x87, 0x17, 0x2f, 0x9c, 0x32,
0xe1, 0xff, 0x08, 0x15, 0x03, 0xff, 0x86, 0x99, 0x68, 0xd7, 0x5a, 0x14, 0xe9, 0xa8, 0xe2, 0x88,
0x28, 0x35, 0x1b, 0xa9, 0x0e, 0x6a, 0x4c, 0x58, 0xb3, 0x75, 0xee, 0xf2, 0x08, 0x9f, 0xc6, 0x0b,
];
const H_EFF_G2_BITS: usize = 636;
fn has_valid_uncompressed_flags(first_byte: u8) -> bool {
first_byte & UNCOMPRESSED_REJECTED_FLAGS == 0
}
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct G2Affine(pub(crate) ::blst::blst_p2_affine);
impl G2Affine {
#[must_use]
pub fn identity() -> Self {
Self(::blst::blst_p2_affine::default())
}
#[must_use]
pub fn generator() -> Self {
Self(unsafe { *::blst::blst_p2_affine_generator() })
}
pub const RAW_SIZE: usize = 193;
#[must_use]
pub fn to_raw_bytes(&self) -> [u8; Self::RAW_SIZE] {
let mut out = [0u8; Self::RAW_SIZE];
if bool::from(self.is_identity()) {
let dusk_identity = dusk_bls12_381::G2Affine::identity();
return dusk_identity.to_raw_bytes();
}
super::write_raw_limbs(
&mut out[..Self::RAW_SIZE - 1],
self.0.x.fp[0]
.l
.iter()
.chain(self.0.x.fp[1].l.iter())
.chain(self.0.y.fp[0].l.iter())
.chain(self.0.y.fp[1].l.iter()),
);
out[Self::RAW_SIZE - 1] = self.is_identity().unwrap_u8();
out
}
#[must_use]
pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self {
if bytes.len() >= Self::RAW_SIZE && bytes[Self::RAW_SIZE - 1] != 0 {
return Self::identity();
}
let mut out = ::blst::blst_p2_affine::default();
let raw = &bytes[..core::cmp::min(bytes.len(), Self::RAW_SIZE - 1)];
let xc0_end = core::cmp::min(raw.len(), 48);
super::read_raw_limbs(&raw[..xc0_end], out.x.fp[0].l.iter_mut());
if raw.len() > 48 {
let xc1_end = core::cmp::min(raw.len(), 96);
super::read_raw_limbs(&raw[48..xc1_end], out.x.fp[1].l.iter_mut());
}
if raw.len() > 96 {
let yc0_end = core::cmp::min(raw.len(), 144);
super::read_raw_limbs(&raw[96..yc0_end], out.y.fp[0].l.iter_mut());
}
if raw.len() > 144 {
let yc1_end = core::cmp::min(raw.len(), 192);
super::read_raw_limbs(&raw[144..yc1_end], out.y.fp[1].l.iter_mut());
}
Self(out)
}
#[must_use]
pub fn is_identity(&self) -> Choice {
let inf = unsafe { ::blst::blst_p2_affine_is_inf(&raw const self.0) };
Choice::from(inf as u8)
}
#[must_use]
pub fn is_torsion_free(&self) -> Choice {
let in_group = unsafe { ::blst::blst_p2_affine_in_g2(&raw const self.0) };
Choice::from(in_group as u8)
}
#[must_use]
pub fn is_on_curve(&self) -> Choice {
let on_curve = unsafe { ::blst::blst_p2_affine_on_curve(&raw const self.0) };
Choice::from(on_curve as u8)
}
#[must_use]
pub fn to_compressed(&self) -> [u8; 96] {
<Self as Serializable<96>>::to_bytes(self)
}
#[must_use]
pub fn to_uncompressed(&self) -> [u8; 192] {
let mut out = [0u8; 192];
unsafe { ::blst::blst_p2_affine_serialize(out.as_mut_ptr(), &raw const self.0) };
out
}
#[must_use]
pub fn from_compressed(bytes: &[u8; 96]) -> CtOption<Self> {
<Self as GroupEncoding>::from_bytes(&G2Compressed(*bytes))
}
#[must_use]
pub fn from_compressed_unchecked(bytes: &[u8; 96]) -> CtOption<Self> {
<Self as GroupEncoding>::from_bytes_unchecked(&G2Compressed(*bytes))
}
#[must_use]
pub fn from_uncompressed(bytes: &[u8; 192]) -> CtOption<Self> {
<Self as UncompressedEncoding>::from_uncompressed(&G2Uncompressed(*bytes))
}
#[must_use]
pub fn from_uncompressed_unchecked(bytes: &[u8; 192]) -> CtOption<Self> {
<Self as UncompressedEncoding>::from_uncompressed_unchecked(&G2Uncompressed(*bytes))
}
}
impl Serializable<96> for G2Affine {
type Error = dusk_bytes::Error;
fn to_bytes(&self) -> [u8; 96] {
let mut out = [0u8; 96];
unsafe { ::blst::blst_p2_affine_compress(out.as_mut_ptr(), &raw const self.0) };
out
}
fn from_bytes(buf: &[u8; 96]) -> Result<Self, Self::Error> {
let mut out = ::blst::blst_p2_affine::default();
let err = unsafe { ::blst::blst_p2_uncompress(&raw mut out, buf.as_ptr()) };
if err != ::blst::BLST_ERROR::BLST_SUCCESS {
return Err(dusk_bytes::Error::InvalidData);
}
let in_group = unsafe { ::blst::blst_p2_affine_in_g2(&raw const out) };
if !in_group {
return Err(dusk_bytes::Error::InvalidData);
}
Ok(Self(out))
}
}
impl fmt::Debug for G2Affine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let b = <Self as Serializable<96>>::to_bytes(self);
write!(f, "G2Affine({:?})", &b[..8])
}
}
impl From<G2Projective> for G2Affine {
fn from(p: G2Projective) -> Self {
let mut out = ::blst::blst_p2_affine::default();
unsafe { ::blst::blst_p2_to_affine(&raw mut out, &raw const p.0) };
Self(out)
}
}
impl From<&G2Projective> for G2Affine {
fn from(p: &G2Projective) -> Self {
Self::from(*p)
}
}
impl Neg for G2Affine {
type Output = Self;
fn neg(self) -> Self {
let mut p = ::blst::blst_p2::default();
unsafe {
::blst::blst_p2_from_affine(&raw mut p, &raw const self.0);
::blst::blst_p2_cneg(&raw mut p, true);
}
Self::from(G2Projective(p))
}
}
impl Neg for &G2Affine {
type Output = G2Affine;
fn neg(self) -> G2Affine {
-(*self)
}
}
impl Mul<BlsScalar> for G2Affine {
type Output = G2Projective;
fn mul(self, rhs: BlsScalar) -> G2Projective {
G2Projective::from(self) * rhs
}
}
impl Mul<BlsScalar> for &G2Affine {
type Output = G2Projective;
fn mul(self, rhs: BlsScalar) -> G2Projective {
(*self) * rhs
}
}
impl Mul<&BlsScalar> for G2Affine {
type Output = G2Projective;
fn mul(self, rhs: &BlsScalar) -> G2Projective {
self * (*rhs)
}
}
impl Mul<&BlsScalar> for &G2Affine {
type Output = G2Projective;
fn mul(self, rhs: &BlsScalar) -> G2Projective {
(*self) * (*rhs)
}
}
impl Add<G2Affine> for G2Affine {
type Output = G2Projective;
fn add(self, rhs: G2Affine) -> G2Projective {
G2Projective::from(self) + G2Projective::from(rhs)
}
}
impl Add<G2Projective> for G2Affine {
type Output = G2Projective;
fn add(self, rhs: G2Projective) -> G2Projective {
G2Projective::from(self) + rhs
}
}
impl Sub<G2Projective> for G2Affine {
type Output = G2Projective;
fn sub(self, rhs: G2Projective) -> G2Projective {
G2Projective::from(self) - rhs
}
}
impl Sub<G2Affine> for G2Affine {
type Output = G2Projective;
fn sub(self, rhs: G2Affine) -> G2Projective {
G2Projective::from(self) - G2Projective::from(rhs)
}
}
impl Add<&G2Affine> for G2Affine {
type Output = G2Projective;
fn add(self, rhs: &G2Affine) -> G2Projective {
self + *rhs
}
}
impl Add<&G2Projective> for G2Affine {
type Output = G2Projective;
fn add(self, rhs: &G2Projective) -> G2Projective {
self + *rhs
}
}
impl Sub<&G2Affine> for G2Affine {
type Output = G2Projective;
fn sub(self, rhs: &G2Affine) -> G2Projective {
self - *rhs
}
}
impl Sub<&G2Projective> for G2Affine {
type Output = G2Projective;
fn sub(self, rhs: &G2Projective) -> G2Projective {
self - *rhs
}
}
impl_ref_binops!(Add, add, G2Affine, G2Affine, G2Projective);
impl_ref_binops!(Add, add, G2Affine, G2Projective, G2Projective);
impl_ref_binops!(Sub, sub, G2Affine, G2Affine, G2Projective);
impl_ref_binops!(Sub, sub, G2Affine, G2Projective, G2Projective);
impl ConstantTimeEq for G2Affine {
fn ct_eq(&self, other: &Self) -> Choice {
<Self as Serializable<96>>::to_bytes(self)
.ct_eq(&<Self as Serializable<96>>::to_bytes(other))
}
}
impl ConditionallySelectable for G2Affine {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut out = ::blst::blst_p2_affine::default();
for i in 0..6 {
out.x.fp[0].l[i] =
u64::conditional_select(&a.0.x.fp[0].l[i], &b.0.x.fp[0].l[i], choice);
out.x.fp[1].l[i] =
u64::conditional_select(&a.0.x.fp[1].l[i], &b.0.x.fp[1].l[i], choice);
out.y.fp[0].l[i] =
u64::conditional_select(&a.0.y.fp[0].l[i], &b.0.y.fp[0].l[i], choice);
out.y.fp[1].l[i] =
u64::conditional_select(&a.0.y.fp[1].l[i], &b.0.y.fp[1].l[i], choice);
}
Self(out)
}
}
impl GroupEncoding for G2Affine {
type Repr = G2Compressed;
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
let mut out = ::blst::blst_p2_affine::default();
let err = unsafe { ::blst::blst_p2_uncompress(&raw mut out, bytes.0.as_ptr()) };
let on_curve = err == ::blst::BLST_ERROR::BLST_SUCCESS;
let in_group = on_curve && unsafe { ::blst::blst_p2_affine_in_g2(&raw const out) };
CtOption::new(Self(out), Choice::from(in_group as u8))
}
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
let mut out = ::blst::blst_p2_affine::default();
let err = unsafe { ::blst::blst_p2_uncompress(&raw mut out, bytes.0.as_ptr()) };
let is_ok = err == ::blst::BLST_ERROR::BLST_SUCCESS;
CtOption::new(Self(out), Choice::from(is_ok as u8))
}
fn to_bytes(&self) -> Self::Repr {
G2Compressed(<Self as Serializable<96>>::to_bytes(self))
}
}
impl UncompressedEncoding for G2Affine {
type Uncompressed = G2Uncompressed;
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self> {
let mut out = ::blst::blst_p2_affine::default();
let valid_flags = has_valid_uncompressed_flags(bytes.0[0]);
let err = if valid_flags {
unsafe { ::blst::blst_p2_deserialize(&raw mut out, bytes.0.as_ptr()) }
} else {
::blst::BLST_ERROR::BLST_BAD_ENCODING
};
let p = Self(out);
let decoded = err == ::blst::BLST_ERROR::BLST_SUCCESS;
let in_group = decoded && unsafe { ::blst::blst_p2_affine_in_g2(&raw const p.0) };
let ok = decoded && in_group;
CtOption::new(p, Choice::from(ok as u8))
}
fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self> {
let mut out = ::blst::blst_p2_affine::default();
let valid_flags = has_valid_uncompressed_flags(bytes.0[0]);
let err = if valid_flags {
unsafe { ::blst::blst_p2_deserialize(&raw mut out, bytes.0.as_ptr()) }
} else {
::blst::BLST_ERROR::BLST_BAD_ENCODING
};
let ok = err == ::blst::BLST_ERROR::BLST_SUCCESS;
CtOption::new(Self(out), Choice::from(ok as u8))
}
fn to_uncompressed(&self) -> Self::Uncompressed {
let mut out = [0u8; 192];
unsafe { ::blst::blst_p2_affine_serialize(out.as_mut_ptr(), &raw const self.0) };
G2Uncompressed(out)
}
}
impl PrimeCurveAffine for G2Affine {
type Scalar = BlsScalar;
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) -> G2Projective {
G2Projective::from(*self)
}
}
impl Mul<G2Affine> for BlsScalar {
type Output = G2Projective;
fn mul(self, rhs: G2Affine) -> G2Projective {
rhs * self
}
}
impl Mul<&G2Affine> for BlsScalar {
type Output = G2Projective;
fn mul(self, rhs: &G2Affine) -> G2Projective {
rhs * self
}
}
impl Mul<G2Affine> for &BlsScalar {
type Output = G2Projective;
fn mul(self, rhs: G2Affine) -> G2Projective {
rhs * self
}
}
impl Mul<&G2Affine> for &BlsScalar {
type Output = G2Projective;
fn mul(self, rhs: &G2Affine) -> G2Projective {
rhs * self
}
}
impl fmt::Display for G2Affine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let bytes = <Self as Serializable<96>>::to_bytes(self);
write!(f, "G2Affine(0x")?;
for b in &bytes {
write!(f, "{b:02x}")?;
}
write!(f, ")")
}
}
#[cfg(feature = "zeroize")]
impl ::zeroize::DefaultIsZeroes for G2Affine {}
#[derive(Copy, Clone)]
pub struct G2Projective(pub(crate) ::blst::blst_p2);
impl PartialEq for G2Projective {
fn eq(&self, other: &Self) -> bool {
unsafe { ::blst::blst_p2_is_equal(&raw const self.0, &raw const other.0) }
}
}
impl Eq for G2Projective {}
impl G2Projective {
#[must_use]
pub fn identity() -> Self {
Self::from(G2Affine::identity())
}
#[must_use]
pub fn generator() -> Self {
Self::from(G2Affine::generator())
}
pub fn batch_normalize(points: &[Self], out: &mut [G2Affine]) {
let n = core::cmp::min(points.len(), out.len());
if n == 0 {
return;
}
let blst_pts: Vec<::blst::blst_p2> = points[..n].iter().map(|p| p.0).collect();
let affines = ::blst::p2_affines::from(&blst_pts);
let affine_slice = affines.as_slice();
for i in 0..n {
out[i] = G2Affine(affine_slice[i]);
}
}
#[must_use]
pub fn is_identity(&self) -> Choice {
let inf = unsafe { ::blst::blst_p2_is_inf(&raw const self.0) };
Choice::from(inf as u8)
}
#[must_use]
pub fn is_on_curve(&self) -> Choice {
let on_curve = unsafe { ::blst::blst_p2_on_curve(&raw const self.0) };
Choice::from(on_curve as u8)
}
#[must_use]
pub fn double(&self) -> Self {
let mut out = ::blst::blst_p2::default();
unsafe { ::blst::blst_p2_double(&raw mut out, &raw const self.0) };
Self(out)
}
#[must_use]
pub fn add(&self, rhs: &Self) -> Self {
let mut out = ::blst::blst_p2::default();
unsafe { ::blst::blst_p2_add_or_double(&raw mut out, &raw const self.0, &raw const rhs.0) };
Self(out)
}
#[must_use]
pub fn add_mixed(&self, rhs: &G2Affine) -> Self {
let mut out = ::blst::blst_p2::default();
unsafe {
::blst::blst_p2_add_or_double_affine(&raw mut out, &raw const self.0, &raw const rhs.0);
}
Self(out)
}
#[must_use]
pub fn clear_cofactor(&self) -> Self {
let mut out = ::blst::blst_p2::default();
unsafe {
::blst::blst_p2_mult(
&raw mut out,
&raw const self.0,
H_EFF_G2.as_ptr(),
H_EFF_G2_BITS,
);
}
Self(out)
}
}
impl Default for G2Projective {
fn default() -> Self {
Self::identity()
}
}
impl fmt::Debug for G2Projective {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "G2Projective({:?})", G2Affine::from(*self))
}
}
impl From<G2Affine> for G2Projective {
fn from(p: G2Affine) -> Self {
let mut out = ::blst::blst_p2::default();
unsafe { ::blst::blst_p2_from_affine(&raw mut out, &raw const p.0) };
Self(out)
}
}
impl From<&G2Affine> for G2Projective {
fn from(p: &G2Affine) -> Self {
Self::from(*p)
}
}
impl Add for G2Projective {
type Output = Self;
fn add(self, rhs: Self) -> Self {
let mut out = ::blst::blst_p2::default();
unsafe { ::blst::blst_p2_add_or_double(&raw mut out, &raw const self.0, &raw const rhs.0) };
Self(out)
}
}
impl AddAssign for G2Projective {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Add<&G2Projective> for G2Projective {
type Output = Self;
fn add(self, rhs: &Self) -> Self {
self + *rhs
}
}
impl AddAssign<&G2Projective> for G2Projective {
fn add_assign(&mut self, rhs: &Self) {
*self = *self + *rhs;
}
}
impl Add<G2Affine> for G2Projective {
type Output = Self;
fn add(self, rhs: G2Affine) -> Self {
let mut out = ::blst::blst_p2::default();
unsafe {
::blst::blst_p2_add_or_double_affine(&raw mut out, &raw const self.0, &raw const rhs.0);
}
Self(out)
}
}
impl AddAssign<G2Affine> for G2Projective {
fn add_assign(&mut self, rhs: G2Affine) {
*self = *self + rhs;
}
}
impl Add<&G2Affine> for G2Projective {
type Output = Self;
fn add(self, rhs: &G2Affine) -> Self {
self + *rhs
}
}
impl AddAssign<&G2Affine> for G2Projective {
fn add_assign(&mut self, rhs: &G2Affine) {
*self = *self + *rhs;
}
}
impl Neg for &G2Projective {
type Output = G2Projective;
fn neg(self) -> G2Projective {
let mut out = self.0;
unsafe { ::blst::blst_p2_cneg(&raw mut out, true) };
G2Projective(out)
}
}
impl Neg for G2Projective {
type Output = Self;
fn neg(self) -> Self {
-&self
}
}
impl Sub for G2Projective {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self + (-rhs)
}
}
impl SubAssign for G2Projective {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Sub<&G2Projective> for G2Projective {
type Output = Self;
fn sub(self, rhs: &Self) -> Self {
self - *rhs
}
}
impl SubAssign<&G2Projective> for G2Projective {
fn sub_assign(&mut self, rhs: &Self) {
*self = *self - *rhs;
}
}
impl Sub<G2Affine> for G2Projective {
type Output = Self;
fn sub(self, rhs: G2Affine) -> Self {
self - Self::from(rhs)
}
}
impl SubAssign<G2Affine> for G2Projective {
fn sub_assign(&mut self, rhs: G2Affine) {
*self = *self - rhs;
}
}
impl Sub<&G2Affine> for G2Projective {
type Output = Self;
fn sub(self, rhs: &G2Affine) -> Self {
self - *rhs
}
}
impl SubAssign<&G2Affine> for G2Projective {
fn sub_assign(&mut self, rhs: &G2Affine) {
*self = *self - *rhs;
}
}
impl_ref_binops!(Add, add, G2Projective, G2Projective, G2Projective);
impl_ref_binops!(Add, add, G2Projective, G2Affine, G2Projective);
impl_ref_binops!(Sub, sub, G2Projective, G2Projective, G2Projective);
impl_ref_binops!(Sub, sub, G2Projective, G2Affine, G2Projective);
impl Mul<BlsScalar> for G2Projective {
type Output = Self;
fn mul(self, rhs: BlsScalar) -> Self {
let bytes = rhs.to_bytes();
let mut out = ::blst::blst_p2::default();
unsafe {
::blst::blst_p2_mult(&raw mut out, &raw const self.0, bytes.as_ptr(), 255);
};
Self(out)
}
}
impl Mul<&BlsScalar> for G2Projective {
type Output = Self;
fn mul(self, rhs: &BlsScalar) -> Self {
self * (*rhs)
}
}
impl Mul<BlsScalar> for &G2Projective {
type Output = G2Projective;
fn mul(self, rhs: BlsScalar) -> G2Projective {
(*self) * rhs
}
}
impl Mul<&BlsScalar> for &G2Projective {
type Output = G2Projective;
fn mul(self, rhs: &BlsScalar) -> G2Projective {
(*self) * (*rhs)
}
}
impl MulAssign<BlsScalar> for G2Projective {
fn mul_assign(&mut self, rhs: BlsScalar) {
*self = *self * rhs;
}
}
impl MulAssign<&BlsScalar> for G2Projective {
fn mul_assign(&mut self, rhs: &BlsScalar) {
*self = *self * *rhs;
}
}
impl core::iter::Sum for G2Projective {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::identity(), |acc, x| acc + x)
}
}
impl<'a> core::iter::Sum<&'a G2Projective> for G2Projective {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(Self::identity(), |acc, x| acc + *x)
}
}
impl ConstantTimeEq for G2Projective {
fn ct_eq(&self, other: &Self) -> Choice {
G2Affine::from(*self).ct_eq(&G2Affine::from(*other))
}
}
impl ConditionallySelectable for G2Projective {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut out = ::blst::blst_p2::default();
for i in 0..6 {
out.x.fp[0].l[i] =
u64::conditional_select(&a.0.x.fp[0].l[i], &b.0.x.fp[0].l[i], choice);
out.x.fp[1].l[i] =
u64::conditional_select(&a.0.x.fp[1].l[i], &b.0.x.fp[1].l[i], choice);
out.y.fp[0].l[i] =
u64::conditional_select(&a.0.y.fp[0].l[i], &b.0.y.fp[0].l[i], choice);
out.y.fp[1].l[i] =
u64::conditional_select(&a.0.y.fp[1].l[i], &b.0.y.fp[1].l[i], choice);
out.z.fp[0].l[i] =
u64::conditional_select(&a.0.z.fp[0].l[i], &b.0.z.fp[0].l[i], choice);
out.z.fp[1].l[i] =
u64::conditional_select(&a.0.z.fp[1].l[i], &b.0.z.fp[1].l[i], choice);
}
Self(out)
}
}
impl GroupEncoding for G2Projective {
type Repr = G2Compressed;
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
<G2Affine as GroupEncoding>::from_bytes(bytes).map(Self::from)
}
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
<G2Affine as GroupEncoding>::from_bytes_unchecked(bytes).map(Self::from)
}
fn to_bytes(&self) -> Self::Repr {
<G2Affine as GroupEncoding>::to_bytes(&G2Affine::from(*self))
}
}
impl Group for G2Projective {
type Scalar = BlsScalar;
fn random(mut rng: impl RngCore) -> Self {
let mut buf = [0u8; 64];
rng.fill_bytes(&mut buf);
let mut out = ::blst::blst_p2::default();
let dst = b"BLS12381G2_XMD:SHA-256_SSWU_RO_";
unsafe {
::blst::blst_hash_to_g2(
&raw mut out,
buf.as_ptr(),
buf.len(),
dst.as_ptr(),
dst.len(),
core::ptr::null(),
0,
)
};
Self(out)
}
fn identity() -> Self {
Self::identity()
}
fn generator() -> Self {
Self::generator()
}
fn is_identity(&self) -> Choice {
self.is_identity()
}
fn double(&self) -> Self {
Self::double(self)
}
}
impl Curve for G2Projective {
type AffineRepr = G2Affine;
fn to_affine(&self) -> Self::AffineRepr {
G2Affine::from(*self)
}
fn batch_normalize(points: &[Self], out: &mut [Self::AffineRepr]) {
Self::batch_normalize(points, out);
}
}
impl PrimeCurve for G2Projective {
type Affine = G2Affine;
}
impl PrimeGroup for G2Projective {}
impl WnafGroup for G2Projective {
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
match num_scalars {
0 => 4,
1 => 4,
2..=3 => 5,
4..=10 => 6,
11..=32 => 7,
_ => 8,
}
}
}
impl Mul<G2Projective> for BlsScalar {
type Output = G2Projective;
fn mul(self, rhs: G2Projective) -> G2Projective {
rhs * self
}
}
impl Mul<&G2Projective> for BlsScalar {
type Output = G2Projective;
fn mul(self, rhs: &G2Projective) -> G2Projective {
rhs * self
}
}
impl Mul<G2Projective> for &BlsScalar {
type Output = G2Projective;
fn mul(self, rhs: G2Projective) -> G2Projective {
rhs * self
}
}
impl Mul<&G2Projective> for &BlsScalar {
type Output = G2Projective;
fn mul(self, rhs: &G2Projective) -> G2Projective {
rhs * self
}
}
impl fmt::Display for G2Projective {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&G2Affine::from(*self), f)
}
}
#[cfg(feature = "zeroize")]
impl ::zeroize::DefaultIsZeroes for G2Projective {}
#[cfg(feature = "serde")]
mod serde_support {
extern crate alloc;
use alloc::format;
use alloc::string::{String, ToString};
use serde::de::Error as SerdeError;
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
use super::*;
fn decode_hex<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let decoded = hex::decode(&s).map_err(SerdeError::custom)?;
let decoded_len = decoded.len();
decoded
.try_into()
.map_err(|_| SerdeError::invalid_length(decoded_len, &N.to_string().as_str()))
}
impl Serialize for G2Affine {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
hex::encode(dusk_bytes::Serializable::to_bytes(self)).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for G2Affine {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let bytes = decode_hex::<D, 96>(deserializer)?;
<Self as dusk_bytes::Serializable<96>>::from_bytes(&bytes)
.map_err(|err| SerdeError::custom(format!("{err:?}")))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use dusk_bytes::Serializable;
fn clear_cofactor_via_dusk_roundtrip(point: &G2Projective) -> G2Projective {
let bytes = <G2Affine as UncompressedEncoding>::to_uncompressed(&G2Affine::from(*point));
let dusk_aff = dusk_bls12_381::G2Affine::from_uncompressed_unchecked(&bytes.0).unwrap();
let cleared = dusk_bls12_381::G2Projective::from(dusk_aff).clear_cofactor();
let cleared_bytes = dusk_bls12_381::G2Affine::from(cleared).to_uncompressed();
let blst_aff = <G2Affine as UncompressedEncoding>::from_uncompressed(
&super::super::G2Uncompressed(cleared_bytes),
)
.unwrap();
G2Projective::from(blst_aff)
}
fn non_subgroup_g2_sample() -> G2Projective {
G2Projective::from(G2Affine(::blst::blst_p2_affine {
x: ::blst::blst_fp2 {
fp: [
::blst::blst_fp {
l: [
0x89f5_50c8_13db_6431,
0xa50b_e8c4_56cd_8a1a,
0xa45b_3741_14ca_e851,
0xbb61_90f5_bf7f_ff63,
0x970c_a02c_3ba8_0bc7,
0x02b8_5d24_e840_fbac,
],
},
::blst::blst_fp {
l: [
0x6888_bc53_d707_16dc,
0x3dea_6b41_1768_2d70,
0xd8f5_f930_500c_a354,
0x6b5e_cb65_56f5_c155,
0xc96b_ef04_3477_8ab0,
0x0508_1505_5150_06ad,
],
},
],
},
y: ::blst::blst_fp2 {
fp: [
::blst::blst_fp {
l: [
0x3cf1_ea0d_434b_0f40,
0x1a0d_c610_e603_e333,
0x7f89_9561_60c7_2fa0,
0x25ee_03de_cf64_31c5,
0xeee8_e206_ec0f_e137,
0x0975_92b2_26df_ef28,
],
},
::blst::blst_fp {
l: [
0x71e8_bb5f_2924_7367,
0xa5fe_049e_2118_31ce,
0x0ce6_b354_502a_3896,
0x93b0_1200_0997_314e,
0x6759_f3b6_aa5b_42ac,
0x1569_44c4_dfe9_2bbb,
],
},
],
},
}))
}
#[test]
fn g2_affine_identity_roundtrip() {
let id = G2Affine::identity();
let bytes = <G2Affine as Serializable<96>>::to_bytes(&id);
let decoded =
<G2Affine as Serializable<96>>::from_bytes(&bytes).expect("identity should roundtrip");
assert_eq!(id, decoded);
}
#[test]
fn g2_affine_generator_roundtrip() {
let g = G2Affine::generator();
let bytes = <G2Affine as Serializable<96>>::to_bytes(&g);
let decoded =
<G2Affine as Serializable<96>>::from_bytes(&bytes).expect("generator should roundtrip");
assert_eq!(g, decoded);
}
#[test]
fn g2_affine_raw_roundtrip() {
let g = G2Affine::generator();
let raw = g.to_raw_bytes();
let decoded = unsafe { G2Affine::from_slice_unchecked(&raw) };
assert_eq!(g, decoded);
}
#[test]
fn g2_affine_raw_matches_dusk() {
let dusk_gen = dusk_bls12_381::G2Affine::generator();
let dusk_id = dusk_bls12_381::G2Affine::identity();
assert_eq!(
G2Affine::generator().to_raw_bytes(),
dusk_gen.to_raw_bytes()
);
assert_eq!(G2Affine::identity().to_raw_bytes(), dusk_id.to_raw_bytes());
let dusk_double = dusk_bls12_381::G2Affine::from(
dusk_bls12_381::G2Projective::generator() + dusk_bls12_381::G2Projective::generator(),
);
let blst_double = G2Affine::from(G2Projective::generator() + G2Projective::generator());
assert_eq!(blst_double.to_raw_bytes(), dusk_double.to_raw_bytes());
}
#[test]
fn g2_rejects_malformed_encodings() {
let compressed = [0u8; 96];
assert!(<G2Affine as Serializable<96>>::from_bytes(&compressed).is_err());
assert!(!bool::from(
G2Affine::from_compressed(&compressed).is_some()
));
assert!(!bool::from(
G2Affine::from_compressed_unchecked(&compressed).is_some()
));
let uncompressed = [0xffu8; 192];
assert!(!bool::from(
G2Affine::from_uncompressed(&uncompressed).is_some()
));
assert!(!bool::from(
G2Affine::from_uncompressed_unchecked(&uncompressed).is_some()
));
}
#[test]
fn g2_uncompressed_decoders_reject_compressed_encodings() {
let compressed = G2Affine::generator().to_compressed();
let mut uncompressed = [0u8; 192];
uncompressed[..96].copy_from_slice(&compressed);
assert!(!bool::from(
G2Affine::from_uncompressed(&uncompressed).is_some()
));
assert!(!bool::from(
G2Affine::from_uncompressed_unchecked(&uncompressed).is_some()
));
}
#[test]
fn g2_checked_decoders_reject_non_subgroup_points() {
let affine = G2Affine::from(non_subgroup_g2_sample());
let compressed = affine.to_compressed();
let uncompressed = affine.to_uncompressed();
assert!(<G2Affine as Serializable<96>>::from_bytes(&compressed).is_err());
assert!(!bool::from(
G2Affine::from_compressed(&compressed).is_some()
));
assert!(!bool::from(
G2Affine::from_uncompressed(&uncompressed).is_some()
));
assert_eq!(
G2Affine::from_compressed_unchecked(&compressed).unwrap(),
affine
);
assert_eq!(
G2Affine::from_uncompressed_unchecked(&uncompressed).unwrap(),
affine
);
}
#[test]
fn g2_projective_eq() {
let a = G2Projective::generator();
let b = G2Projective::generator();
assert_eq!(a, b);
assert_ne!(a, G2Projective::identity());
}
#[test]
fn g2_projective_add_assign() {
let g = G2Projective::generator();
let mut acc = G2Projective::identity();
acc += g;
assert_eq!(acc, g);
acc += g;
let expected = g + g;
assert_eq!(acc, expected);
}
#[test]
fn g2_projective_add_assign_ref() {
let g = G2Projective::generator();
let mut acc = G2Projective::identity();
acc += &g;
assert_eq!(acc, g);
}
#[test]
fn g2_projective_add_affine() {
let a = G2Affine::generator();
let p = G2Projective::generator();
let result = p + a;
let expected = p + G2Projective::from(a);
assert_eq!(result, expected);
}
#[test]
fn g2_projective_add_assign_affine() {
let a = G2Affine::generator();
let mut p = G2Projective::generator();
let expected = p + G2Projective::from(a);
p += a;
assert_eq!(p, expected);
}
#[test]
fn g2_projective_sub_assign() {
let g = G2Projective::generator();
let mut acc = g + g;
acc -= g;
assert_eq!(acc, g);
}
#[test]
fn g2_projective_mul_assign() {
let g = G2Projective::generator();
let two = BlsScalar::from(2u64);
let mut p = g;
p *= two;
assert_eq!(p, g + g);
}
#[test]
fn g2_projective_mul_assign_ref() {
let g = G2Projective::generator();
let two = BlsScalar::from(2u64);
let mut p = g;
p *= &two;
assert_eq!(p, g + g);
}
#[test]
fn g2_projective_sum() {
let g = G2Projective::generator();
let pts = [g, g, g];
let total: G2Projective = pts.iter().copied().sum();
assert_eq!(total, g + g + g);
}
#[test]
fn g2_projective_sum_refs() {
let g = G2Projective::generator();
let pts = [g, g];
let total: G2Projective = pts.iter().sum();
assert_eq!(total, g + g);
}
#[test]
fn g2_projective_group_trait() {
let id = <G2Projective as group::Group>::identity();
let generator = <G2Projective as group::Group>::generator();
assert!(bool::from(id.is_identity()));
assert!(!bool::from(generator.is_identity()));
assert_eq!(generator.double(), generator + generator);
}
#[test]
fn g2_projective_curve_trait() {
let p = G2Projective::generator();
let a = <G2Projective as group::Curve>::to_affine(&p);
assert_eq!(a, G2Affine::generator());
}
#[test]
fn g2_reference_variants_compile_and_match() {
let a = G2Projective::generator();
let b = G2Projective::generator();
assert_eq!(&a + &b, a + b);
assert_eq!(-&a, -a);
assert_eq!(G2Affine::from(&a), G2Affine::from(a));
let aa = G2Affine::generator();
let bb = G2Affine::generator();
assert_eq!(&aa + &bb, aa + bb);
assert_eq!(&aa - &bb, aa - bb);
}
#[test]
fn g2_affine_conditional_select_choice_0_returns_a() {
let a = G2Affine::generator();
let b = G2Affine::identity();
let sel = G2Affine::conditional_select(&a, &b, Choice::from(0));
assert_eq!(sel, a);
}
#[test]
fn g2_affine_conditional_select_choice_1_returns_b() {
let a = G2Affine::generator();
let b = G2Affine::identity();
let sel = G2Affine::conditional_select(&a, &b, Choice::from(1));
assert_eq!(sel, b);
}
#[test]
fn g2_projective_conditional_select() {
let a = G2Projective::generator();
let b = G2Projective::identity();
assert_eq!(G2Projective::conditional_select(&a, &b, Choice::from(0)), a);
assert_eq!(G2Projective::conditional_select(&a, &b, Choice::from(1)), b);
}
#[test]
fn g2_conditional_select_non_identity_points() {
let generator = G2Projective::generator();
let a = generator + generator;
let b = generator * BlsScalar::from(5u64);
let sel_a = G2Projective::conditional_select(&a, &b, Choice::from(0));
let sel_b = G2Projective::conditional_select(&a, &b, Choice::from(1));
assert_ne!(a, b);
assert_eq!(sel_a, a);
assert_eq!(sel_b, b);
assert_ne!(sel_a, sel_b);
let a_affine = G2Affine::from(a);
let b_affine = G2Affine::from(b);
let sel_a_affine = G2Affine::conditional_select(&a_affine, &b_affine, Choice::from(0));
let sel_b_affine = G2Affine::conditional_select(&a_affine, &b_affine, Choice::from(1));
assert_ne!(a_affine, b_affine);
assert_eq!(sel_a_affine, a_affine);
assert_eq!(sel_b_affine, b_affine);
assert_ne!(sel_a_affine, sel_b_affine);
}
#[test]
fn g2_clear_cofactor_matches_dusk() {
let blst_g = G2Projective::generator();
let dusk_g = dusk_bls12_381::G2Projective::generator();
let blst_cleared = G2Affine::from(blst_g.clear_cofactor());
let dusk_cleared = dusk_bls12_381::G2Affine::from(dusk_g.clear_cofactor());
assert_eq!(blst_cleared.to_raw_bytes(), dusk_cleared.to_raw_bytes());
assert!(bool::from(blst_cleared.is_torsion_free()));
}
#[test]
fn g2_clear_cofactor_identity_is_identity() {
let cleared = G2Projective::identity().clear_cofactor();
assert!(bool::from(cleared.is_identity()));
}
#[test]
fn g2_clear_cofactor_non_subgroup_matches_dusk_roundtrip() {
let point = non_subgroup_g2_sample();
assert!(bool::from(point.is_on_curve()));
assert!(!bool::from(G2Affine::from(point).is_torsion_free()));
let blst_cleared = G2Affine::from(point.clear_cofactor());
let dusk_cleared = G2Affine::from(clear_cofactor_via_dusk_roundtrip(&point));
assert_eq!(blst_cleared.to_raw_bytes(), dusk_cleared.to_raw_bytes());
assert!(bool::from(blst_cleared.is_torsion_free()));
}
#[test]
fn g2_affine_inherent_encoding_matches_traits() {
let g = G2Affine::generator();
let compressed = g.to_compressed();
let uncompressed = g.to_uncompressed();
assert_eq!(compressed, <G2Affine as Serializable<96>>::to_bytes(&g));
assert_eq!(
uncompressed,
<G2Affine as UncompressedEncoding>::to_uncompressed(&g).0
);
assert_eq!(G2Affine::from_compressed(&compressed).unwrap(), g);
assert_eq!(G2Affine::from_compressed_unchecked(&compressed).unwrap(), g);
assert_eq!(G2Affine::from_uncompressed(&uncompressed).unwrap(), g);
assert_eq!(
G2Affine::from_uncompressed_unchecked(&uncompressed).unwrap(),
g
);
}
#[test]
fn g2_projective_inherent_arithmetic_matches_trait_impl() {
let g = G2Projective::generator();
assert_eq!(g.double(), g + g);
assert_eq!(g.add(&g), g + g);
let g_aff = G2Affine::generator();
assert_eq!(g.add_mixed(&g_aff), g + G2Projective::from(g_aff));
assert!(bool::from(g.is_on_curve()));
}
#[cfg(feature = "zeroize")]
#[test]
fn g2_zeroize_resets_points_to_default() {
use zeroize::Zeroize;
let mut affine = G2Affine::generator();
affine.zeroize();
assert_eq!(affine, G2Affine::default());
let mut projective = G2Projective::generator();
projective.zeroize();
assert_eq!(projective, G2Projective::default());
}
}