use core::borrow::Borrow;
use core::iter::Sum;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::fp::Fp;
use crate::Scalar;
#[derive(Copy, Clone, Debug)]
pub struct G1Affine {
pub(crate) x: Fp,
pub(crate) y: Fp,
infinity: Choice,
}
impl Default for G1Affine {
fn default() -> G1Affine {
G1Affine::identity()
}
}
impl<'a> From<&'a G1Projective> for G1Affine {
fn from(p: &'a G1Projective) -> G1Affine {
let zinv = p.z.invert().unwrap_or(Fp::zero());
let zinv2 = zinv.square();
let x = p.x * zinv2;
let zinv3 = zinv2 * zinv;
let y = p.y * zinv3;
let tmp = G1Affine {
x,
y,
infinity: Choice::from(0u8),
};
G1Affine::conditional_select(&tmp, &G1Affine::identity(), zinv.is_zero())
}
}
impl From<G1Projective> for G1Affine {
fn from(p: G1Projective) -> G1Affine {
G1Affine::from(&p)
}
}
impl Serialize for G1Affine {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeTuple;
let mut tup = serializer.serialize_tuple(48)?;
for byte in self.to_compressed().iter() {
tup.serialize_element(byte)?;
}
tup.end()
}
}
impl<'de> Deserialize<'de> for G1Affine {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct G1AffineVisitor;
impl<'de> Visitor<'de> for G1AffineVisitor {
type Value = G1Affine;
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
formatter.write_str("a 48-byte cannonical compressed G1Affine point from Bls12_381")
}
fn visit_seq<A>(self, mut seq: A) -> Result<G1Affine, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut bytes = [0u8; 48];
for i in 0..48 {
bytes[i] = seq
.next_element()?
.ok_or(serde::de::Error::invalid_length(i, &"expected 48 bytes"))?;
}
let res = G1Affine::from_compressed(&bytes);
if res.is_some().unwrap_u8() == 1u8 {
return Ok(res.unwrap());
} else {
return Err(serde::de::Error::custom(
&"compressed G1Affine was not canonically encoded",
));
}
}
}
deserializer.deserialize_tuple(48, G1AffineVisitor)
}
}
impl ConstantTimeEq for G1Affine {
fn ct_eq(&self, other: &Self) -> Choice {
(self.infinity & other.infinity)
| ((!self.infinity)
& (!other.infinity)
& self.x.ct_eq(&other.x)
& self.y.ct_eq(&other.y))
}
}
impl ConditionallySelectable for G1Affine {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
G1Affine {
x: Fp::conditional_select(&a.x, &b.x, choice),
y: Fp::conditional_select(&a.y, &b.y, choice),
infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice),
}
}
}
impl Eq for G1Affine {}
impl PartialEq for G1Affine {
#[inline]
fn eq(&self, other: &Self) -> bool {
bool::from(self.ct_eq(other))
}
}
impl<'a> Neg for &'a G1Affine {
type Output = G1Affine;
#[inline]
fn neg(self) -> G1Affine {
G1Affine {
x: self.x,
y: Fp::conditional_select(&-self.y, &Fp::one(), self.infinity),
infinity: self.infinity,
}
}
}
impl Neg for G1Affine {
type Output = G1Affine;
#[inline]
fn neg(self) -> G1Affine {
-&self
}
}
impl<'a, 'b> Add<&'b G1Projective> for &'a G1Affine {
type Output = G1Projective;
#[inline]
fn add(self, rhs: &'b G1Projective) -> G1Projective {
rhs.add_mixed(self)
}
}
impl<'a, 'b> Add<&'b G1Affine> for &'a G1Projective {
type Output = G1Projective;
#[inline]
fn add(self, rhs: &'b G1Affine) -> G1Projective {
self.add_mixed(rhs)
}
}
impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Affine {
type Output = G1Projective;
#[inline]
fn sub(self, rhs: &'b G1Projective) -> G1Projective {
self + (-rhs)
}
}
impl<'a, 'b> Sub<&'b G1Affine> for &'a G1Projective {
type Output = G1Projective;
#[inline]
fn sub(self, rhs: &'b G1Affine) -> G1Projective {
self + (-rhs)
}
}
impl<T> Sum<T> for G1Projective
where
T: Borrow<G1Projective>,
{
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = T>,
{
iter.fold(Self::identity(), |acc, item| acc + item.borrow())
}
}
impl_binops_additive!(G1Projective, G1Affine);
impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective);
const B: Fp = Fp::from_raw_unchecked([
0xaa270000000cfff3,
0x53cc0032fc34000a,
0x478fe97a6b0a807f,
0xb1d37ebee6ba24d7,
0x8ec9733bbf78ab2f,
0x9d645513d83de7e,
]);
impl G1Affine {
pub fn identity() -> G1Affine {
G1Affine {
x: Fp::zero(),
y: Fp::one(),
infinity: Choice::from(1u8),
}
}
pub fn generator() -> G1Affine {
G1Affine {
x: Fp::from_raw_unchecked([
0x5cb38790fd530c16,
0x7817fc679976fff5,
0x154f95c7143ba1c1,
0xf0ae6acdf3d0e747,
0xedce6ecc21dbf440,
0x120177419e0bfb75,
]),
y: Fp::from_raw_unchecked([
0xbaac93d50ce72271,
0x8c22631a7918fd8e,
0xdd595f13570725ce,
0x51ac582950405194,
0xe1c8c3fad0059c0,
0xbbc3efc5008a26a,
]),
infinity: Choice::from(0u8),
}
}
pub fn to_compressed(&self) -> [u8; 48] {
let mut res = Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes();
res[0] |= 1u8 << 7;
res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity);
res[0] |= u8::conditional_select(
&0u8,
&(1u8 << 5),
(!self.infinity) & self.y.lexicographically_largest(),
);
res
}
pub fn to_uncompressed(&self) -> [u8; 96] {
let mut res = [0; 96];
res[0..48].copy_from_slice(
&Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes()[..],
);
res[48..96].copy_from_slice(
&Fp::conditional_select(&self.y, &Fp::zero(), self.infinity).to_bytes()[..],
);
res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity);
res
}
pub fn from_uncompressed(bytes: &[u8; 96]) -> CtOption<Self> {
Self::from_uncompressed_unchecked(bytes)
.and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free()))
}
pub fn from_uncompressed_unchecked(bytes: &[u8; 96]) -> CtOption<Self> {
let compression_flag_set = Choice::from((bytes[0] >> 7) & 1);
let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1);
let sort_flag_set = Choice::from((bytes[0] >> 5) & 1);
let x = {
let mut tmp = [0; 48];
tmp.copy_from_slice(&bytes[0..48]);
tmp[0] &= 0b0001_1111;
Fp::from_bytes(&tmp)
};
let y = {
let mut tmp = [0; 48];
tmp.copy_from_slice(&bytes[48..96]);
Fp::from_bytes(&tmp)
};
x.and_then(|x| {
y.and_then(|y| {
let p = G1Affine::conditional_select(
&G1Affine {
x,
y,
infinity: infinity_flag_set,
},
&G1Affine::identity(),
infinity_flag_set,
);
CtOption::new(
p,
((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) &
(!compression_flag_set) &
(!sort_flag_set),
)
})
})
}
pub fn from_compressed(bytes: &[u8; 48]) -> CtOption<Self> {
Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free()))
}
pub fn from_compressed_unchecked(bytes: &[u8; 48]) -> CtOption<Self> {
let compression_flag_set = Choice::from((bytes[0] >> 7) & 1);
let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1);
let sort_flag_set = Choice::from((bytes[0] >> 5) & 1);
let x = {
let mut tmp = [0; 48];
tmp.copy_from_slice(&bytes[0..48]);
tmp[0] &= 0b0001_1111;
Fp::from_bytes(&tmp)
};
x.and_then(|x| {
CtOption::new(
G1Affine::identity(),
infinity_flag_set &
compression_flag_set &
(!sort_flag_set) &
x.is_zero(),
)
.or_else(|| {
((x.square() * x) + B).sqrt().and_then(|y| {
let y = Fp::conditional_select(
&y,
&-y,
y.lexicographically_largest() ^ sort_flag_set,
);
CtOption::new(
G1Affine {
x,
y,
infinity: infinity_flag_set,
},
(!infinity_flag_set) &
compression_flag_set,
)
})
})
})
}
#[inline]
pub fn is_identity(&self) -> Choice {
self.infinity
}
pub fn is_torsion_free(&self) -> Choice {
const FQ_MODULUS_BYTES: [u8; 32] = [
1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8,
216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115,
];
G1Projective::from(*self)
.multiply(&FQ_MODULUS_BYTES)
.is_identity()
}
pub fn is_on_curve(&self) -> Choice {
(self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity
}
}
#[derive(Copy, Clone, Debug)]
pub struct G1Projective {
x: Fp,
y: Fp,
z: Fp,
}
impl<'a> From<&'a G1Affine> for G1Projective {
fn from(p: &'a G1Affine) -> G1Projective {
G1Projective {
x: p.x,
y: p.y,
z: Fp::conditional_select(&Fp::one(), &Fp::zero(), p.infinity),
}
}
}
impl From<G1Affine> for G1Projective {
fn from(p: G1Affine) -> G1Projective {
G1Projective::from(&p)
}
}
impl ConstantTimeEq for G1Projective {
fn ct_eq(&self, other: &Self) -> Choice {
let z = other.z.square();
let x1 = self.x * z;
let z = z * other.z;
let y1 = self.y * z;
let z = self.z.square();
let x2 = other.x * z;
let z = z * self.z;
let y2 = other.y * z;
let self_is_zero = self.z.is_zero();
let other_is_zero = other.z.is_zero();
(self_is_zero & other_is_zero)
| ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2))
}
}
impl ConditionallySelectable for G1Projective {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
G1Projective {
x: Fp::conditional_select(&a.x, &b.x, choice),
y: Fp::conditional_select(&a.y, &b.y, choice),
z: Fp::conditional_select(&a.z, &b.z, choice),
}
}
}
impl Eq for G1Projective {}
impl PartialEq for G1Projective {
#[inline]
fn eq(&self, other: &Self) -> bool {
bool::from(self.ct_eq(other))
}
}
impl<'a> Neg for &'a G1Projective {
type Output = G1Projective;
#[inline]
fn neg(self) -> G1Projective {
G1Projective {
x: self.x,
y: -self.y,
z: self.z,
}
}
}
impl Neg for G1Projective {
type Output = G1Projective;
#[inline]
fn neg(self) -> G1Projective {
-&self
}
}
impl<'a, 'b> Add<&'b G1Projective> for &'a G1Projective {
type Output = G1Projective;
#[inline]
fn add(self, rhs: &'b G1Projective) -> G1Projective {
self.add(rhs)
}
}
impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Projective {
type Output = G1Projective;
#[inline]
fn sub(self, rhs: &'b G1Projective) -> G1Projective {
self + (-rhs)
}
}
impl<'a, 'b> Mul<&'b Scalar> for &'a G1Projective {
type Output = G1Projective;
fn mul(self, other: &'b Scalar) -> Self::Output {
self.multiply(&other.to_bytes())
}
}
impl<'a, 'b> Mul<&'b Scalar> for &'a G1Affine {
type Output = G1Projective;
fn mul(self, other: &'b Scalar) -> Self::Output {
G1Projective::from(self).multiply(&other.to_bytes())
}
}
impl_binops_additive!(G1Projective, G1Projective);
impl_binops_multiplicative!(G1Projective, Scalar);
impl_binops_multiplicative_mixed!(G1Affine, Scalar, G1Projective);
impl G1Projective {
pub fn identity() -> G1Projective {
G1Projective {
x: Fp::zero(),
y: Fp::one(),
z: Fp::zero(),
}
}
pub fn generator() -> G1Projective {
G1Projective {
x: Fp::from_raw_unchecked([
0x5cb38790fd530c16,
0x7817fc679976fff5,
0x154f95c7143ba1c1,
0xf0ae6acdf3d0e747,
0xedce6ecc21dbf440,
0x120177419e0bfb75,
]),
y: Fp::from_raw_unchecked([
0xbaac93d50ce72271,
0x8c22631a7918fd8e,
0xdd595f13570725ce,
0x51ac582950405194,
0xe1c8c3fad0059c0,
0xbbc3efc5008a26a,
]),
z: Fp::one(),
}
}
pub fn double(&self) -> G1Projective {
let a = self.x.square();
let b = self.y.square();
let c = b.square();
let d = self.x + b;
let d = d.square();
let d = d - a - c;
let d = d + d;
let e = a + a + a;
let f = e.square();
let z3 = self.z * self.y;
let z3 = z3 + z3;
let x3 = f - (d + d);
let c = c + c;
let c = c + c;
let c = c + c;
let y3 = e * (d - x3) - c;
let tmp = G1Projective {
x: x3,
y: y3,
z: z3,
};
G1Projective::conditional_select(&tmp, &G1Projective::identity(), self.is_identity())
}
pub fn add(&self, rhs: &G1Projective) -> G1Projective {
let f1 = self.is_identity();
let res = G1Projective::conditional_select(self, rhs, f1);
let f2 = rhs.is_identity();
let z = rhs.z.square();
let u1 = self.x * z;
let z = z * rhs.z;
let s1 = self.y * z;
let z = self.z.square();
let u2 = rhs.x * z;
let z = z * self.z;
let s2 = rhs.y * z;
let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2));
let res =
G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3);
let t = u1 + u2;
let m = s1 + s2;
let rr = t.square();
let m_alt = -u2;
let tt = u1 * m_alt;
let rr = rr + tt;
let degenerate = m.is_zero() & rr.is_zero();
let rr_alt = s1 + s1;
let m_alt = m_alt + u1;
let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate);
let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate);
let n = m_alt.square();
let q = n * t;
let n = n.square();
let n = Fp::conditional_select(&n, &m, degenerate);
let t = rr_alt.square();
let z3 = m_alt * self.z * rhs.z;
let z3 = z3 + z3;
let q = -q;
let t = t + q;
let x3 = t;
let t = t + t;
let t = t + q;
let t = t * rr_alt;
let t = t + n;
let y3 = -t;
let x3 = x3 + x3;
let x3 = x3 + x3;
let y3 = y3 + y3;
let y3 = y3 + y3;
let tmp = G1Projective {
x: x3,
y: y3,
z: z3,
};
G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3))
}
pub fn add_mixed(&self, rhs: &G1Affine) -> G1Projective {
let f1 = self.is_identity();
let res = G1Projective::conditional_select(self, &G1Projective::from(rhs), f1);
let f2 = rhs.is_identity();
let u1 = self.x;
let s1 = self.y;
let z = self.z.square();
let u2 = rhs.x * z;
let z = z * self.z;
let s2 = rhs.y * z;
let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2));
let res =
G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3);
let t = u1 + u2;
let m = s1 + s2;
let rr = t.square();
let m_alt = -u2;
let tt = u1 * m_alt;
let rr = rr + tt;
let degenerate = m.is_zero() & rr.is_zero();
let rr_alt = s1 + s1;
let m_alt = m_alt + u1;
let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate);
let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate);
let n = m_alt.square();
let q = n * t;
let n = n.square();
let n = Fp::conditional_select(&n, &m, degenerate);
let t = rr_alt.square();
let z3 = m_alt * self.z;
let z3 = z3 + z3;
let q = -q;
let t = t + q;
let x3 = t;
let t = t + t;
let t = t + q;
let t = t * rr_alt;
let t = t + n;
let y3 = -t;
let x3 = x3 + x3;
let x3 = x3 + x3;
let y3 = y3 + y3;
let y3 = y3 + y3;
let tmp = G1Projective {
x: x3,
y: y3,
z: z3,
};
G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3))
}
fn multiply(&self, by: &[u8; 32]) -> G1Projective {
let mut acc = G1Projective::identity();
for bit in by
.iter()
.rev()
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
.skip(1)
{
acc = acc.double();
acc = G1Projective::conditional_select(&acc, &(acc + self), bit);
}
acc
}
fn mul_by_x(&self) -> G1Projective {
let mut xself = G1Projective::identity();
let mut x = crate::BLS_X >> 1;
let mut tmp = *self;
while x != 0 {
tmp = tmp.double();
if x % 2 == 1 {
xself += tmp;
}
x >>= 1;
}
if crate::BLS_X_IS_NEGATIVE {
xself = -xself;
}
xself
}
pub fn clear_cofactor(&self) -> G1Projective {
self - self.mul_by_x()
}
pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) {
assert_eq!(p.len(), q.len());
let mut acc = Fp::one();
for (p, q) in p.iter().zip(q.iter_mut()) {
q.x = acc;
acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity());
}
acc = acc.invert().unwrap();
for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) {
let skip = p.is_identity();
let tmp = q.x * acc;
acc = Fp::conditional_select(&(acc * p.z), &acc, skip);
let tmp2 = tmp.square();
let tmp3 = tmp2 * tmp;
q.x = p.x * tmp2;
q.y = p.y * tmp3;
q.infinity = Choice::from(0u8);
*q = G1Affine::conditional_select(&q, &G1Affine::identity(), skip);
}
}
#[inline]
pub fn is_identity(&self) -> Choice {
self.z.is_zero()
}
pub fn is_on_curve(&self) -> Choice {
(self.y.square() - (self.x.square() * self.x))
.ct_eq(&((self.z.square() * self.z).square() * B))
| self.z.is_zero()
}
}
#[test]
fn test_is_on_curve() {
assert!(bool::from(G1Affine::identity().is_on_curve()));
assert!(bool::from(G1Affine::generator().is_on_curve()));
assert!(bool::from(G1Projective::identity().is_on_curve()));
assert!(bool::from(G1Projective::generator().is_on_curve()));
let z = Fp::from_raw_unchecked([
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
]);
let gen = G1Affine::generator();
let mut test = G1Projective {
x: gen.x * (z.square()),
y: gen.y * (z.square() * z),
z,
};
assert!(bool::from(test.is_on_curve()));
test.x = z;
assert!(!bool::from(test.is_on_curve()));
}
#[test]
fn test_affine_point_equality() {
let a = G1Affine::generator();
let b = G1Affine::identity();
assert!(a == a);
assert!(b == b);
assert!(a != b);
assert!(b != a);
}
#[test]
fn test_projective_point_equality() {
let a = G1Projective::generator();
let b = G1Projective::identity();
assert!(a == a);
assert!(b == b);
assert!(a != b);
assert!(b != a);
let z = Fp::from_raw_unchecked([
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
]);
let mut c = G1Projective {
x: a.x * (z.square()),
y: a.y * (z.square() * z),
z,
};
assert!(bool::from(c.is_on_curve()));
assert!(a == c);
assert!(b != c);
assert!(c == a);
assert!(c != b);
c.y = -c.y;
assert!(bool::from(c.is_on_curve()));
assert!(a != c);
assert!(b != c);
assert!(c != a);
assert!(c != b);
c.y = -c.y;
c.x = z;
assert!(!bool::from(c.is_on_curve()));
assert!(a != b);
assert!(a != c);
assert!(b != c);
}
#[test]
fn test_conditionally_select_affine() {
let a = G1Affine::generator();
let b = G1Affine::identity();
assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(0u8)), a);
assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(1u8)), b);
}
#[test]
fn test_conditionally_select_projective() {
let a = G1Projective::generator();
let b = G1Projective::identity();
assert_eq!(
G1Projective::conditional_select(&a, &b, Choice::from(0u8)),
a
);
assert_eq!(
G1Projective::conditional_select(&a, &b, Choice::from(1u8)),
b
);
}
#[test]
fn test_projective_to_affine() {
let a = G1Projective::generator();
let b = G1Projective::identity();
assert!(bool::from(G1Affine::from(a).is_on_curve()));
assert!(!bool::from(G1Affine::from(a).is_identity()));
assert!(bool::from(G1Affine::from(b).is_on_curve()));
assert!(bool::from(G1Affine::from(b).is_identity()));
let z = Fp::from_raw_unchecked([
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
]);
let c = G1Projective {
x: a.x * (z.square()),
y: a.y * (z.square() * z),
z,
};
assert_eq!(G1Affine::from(c), G1Affine::generator());
}
#[test]
fn test_affine_to_projective() {
let a = G1Affine::generator();
let b = G1Affine::identity();
assert!(bool::from(G1Projective::from(a).is_on_curve()));
assert!(!bool::from(G1Projective::from(a).is_identity()));
assert!(bool::from(G1Projective::from(b).is_on_curve()));
assert!(bool::from(G1Projective::from(b).is_identity()));
}
#[test]
fn test_doubling() {
{
let tmp = G1Projective::identity().double();
assert!(bool::from(tmp.is_identity()));
assert!(bool::from(tmp.is_on_curve()));
}
{
let tmp = G1Projective::generator().double();
assert!(!bool::from(tmp.is_identity()));
assert!(bool::from(tmp.is_on_curve()));
assert_eq!(
G1Affine::from(tmp),
G1Affine {
x: Fp::from_raw_unchecked([
0x53e978ce58a9ba3c,
0x3ea0583c4f3d65f9,
0x4d20bb47f0012960,
0xa54c664ae5b2b5d9,
0x26b552a39d7eb21f,
0x8895d26e68785
]),
y: Fp::from_raw_unchecked([
0x70110b3298293940,
0xda33c5393f1f6afc,
0xb86edfd16a5aa785,
0xaec6d1c9e7b1c895,
0x25cfc2b522d11720,
0x6361c83f8d09b15
]),
infinity: Choice::from(0u8)
}
);
}
}
#[test]
fn test_projective_addition() {
{
let a = G1Projective::identity();
let b = G1Projective::identity();
let c = a + b;
assert!(bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
}
{
let a = G1Projective::identity();
let mut b = G1Projective::generator();
{
let z = Fp::from_raw_unchecked([
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
]);
b = G1Projective {
x: b.x * (z.square()),
y: b.y * (z.square() * z),
z,
};
}
let c = a + b;
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
assert!(c == G1Projective::generator());
}
{
let a = G1Projective::identity();
let mut b = G1Projective::generator();
{
let z = Fp::from_raw_unchecked([
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
]);
b = G1Projective {
x: b.x * (z.square()),
y: b.y * (z.square() * z),
z,
};
}
let c = b + a;
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
assert!(c == G1Projective::generator());
}
{
let a = G1Projective::generator().double().double();
let b = G1Projective::generator().double();
let c = a + b;
let mut d = G1Projective::generator();
for _ in 0..5 {
d = d + G1Projective::generator();
}
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
assert!(!bool::from(d.is_identity()));
assert!(bool::from(d.is_on_curve()));
assert_eq!(c, d);
}
{
let beta = Fp::from_raw_unchecked([
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x3f97d6e83d050d2,
0x18f0206554638741,
]);
let beta = beta.square();
let a = G1Projective::generator().double().double();
let b = G1Projective {
x: a.x * beta,
y: -a.y,
z: a.z,
};
assert!(bool::from(a.is_on_curve()));
assert!(bool::from(b.is_on_curve()));
let c = a + b;
assert_eq!(
G1Affine::from(c),
G1Affine::from(G1Projective {
x: Fp::from_raw_unchecked([
0x29e1e987ef68f2d0,
0xc5f3ec531db03233,
0xacd6c4b6ca19730f,
0x18ad9e827bc2bab7,
0x46e3b2c5785cc7a9,
0x7e571d42d22ddd6
]),
y: Fp::from_raw_unchecked([
0x94d117a7e5a539e7,
0x8e17ef673d4b5d22,
0x9d746aaf508a33ea,
0x8c6d883d2516c9a2,
0xbc3b8d5fb0447f7,
0x7bfa4c7210f4f44
]),
z: Fp::one()
})
);
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
}
}
#[test]
fn test_mixed_addition() {
{
let a = G1Affine::identity();
let b = G1Projective::identity();
let c = a + b;
assert!(bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
}
{
let a = G1Affine::identity();
let mut b = G1Projective::generator();
{
let z = Fp::from_raw_unchecked([
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
]);
b = G1Projective {
x: b.x * (z.square()),
y: b.y * (z.square() * z),
z,
};
}
let c = a + b;
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
assert!(c == G1Projective::generator());
}
{
let a = G1Affine::identity();
let mut b = G1Projective::generator();
{
let z = Fp::from_raw_unchecked([
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
]);
b = G1Projective {
x: b.x * (z.square()),
y: b.y * (z.square() * z),
z,
};
}
let c = b + a;
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
assert!(c == G1Projective::generator());
}
{
let a = G1Projective::generator().double().double();
let b = G1Projective::generator().double();
let c = a + b;
let mut d = G1Projective::generator();
for _ in 0..5 {
d = d + G1Affine::generator();
}
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
assert!(!bool::from(d.is_identity()));
assert!(bool::from(d.is_on_curve()));
assert_eq!(c, d);
}
{
let beta = Fp::from_raw_unchecked([
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x3f97d6e83d050d2,
0x18f0206554638741,
]);
let beta = beta.square();
let a = G1Projective::generator().double().double();
let b = G1Projective {
x: a.x * beta,
y: -a.y,
z: a.z,
};
let a = G1Affine::from(a);
assert!(bool::from(a.is_on_curve()));
assert!(bool::from(b.is_on_curve()));
let c = a + b;
assert_eq!(
G1Affine::from(c),
G1Affine::from(G1Projective {
x: Fp::from_raw_unchecked([
0x29e1e987ef68f2d0,
0xc5f3ec531db03233,
0xacd6c4b6ca19730f,
0x18ad9e827bc2bab7,
0x46e3b2c5785cc7a9,
0x7e571d42d22ddd6
]),
y: Fp::from_raw_unchecked([
0x94d117a7e5a539e7,
0x8e17ef673d4b5d22,
0x9d746aaf508a33ea,
0x8c6d883d2516c9a2,
0xbc3b8d5fb0447f7,
0x7bfa4c7210f4f44
]),
z: Fp::one()
})
);
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
}
}
#[test]
fn test_projective_negation_and_subtraction() {
let a = G1Projective::generator().double();
assert_eq!(a + (-a), G1Projective::identity());
assert_eq!(a + (-a), a - a);
}
#[test]
fn test_affine_negation_and_subtraction() {
let a = G1Affine::generator();
assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity());
assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a);
}
#[test]
fn test_projective_scalar_multiplication() {
let g = G1Projective::generator();
let a = Scalar::from_raw([
0x2b568297a56da71c,
0xd8c39ecb0ef375d1,
0x435c38da67bfbf96,
0x8088a05026b659b2,
]);
let b = Scalar::from_raw([
0x785fdd9b26ef8b85,
0xc997f25837695c18,
0x4c8dbc39e7b756c1,
0x70d9b6cc6d87df20,
]);
let c = a * b;
assert_eq!((g * a) * b, g * c);
}
#[test]
fn test_affine_scalar_multiplication() {
let g = G1Affine::generator();
let a = Scalar::from_raw([
0x2b568297a56da71c,
0xd8c39ecb0ef375d1,
0x435c38da67bfbf96,
0x8088a05026b659b2,
]);
let b = Scalar::from_raw([
0x785fdd9b26ef8b85,
0xc997f25837695c18,
0x4c8dbc39e7b756c1,
0x70d9b6cc6d87df20,
]);
let c = a * b;
assert_eq!(G1Affine::from(g * a) * b, g * c);
}
#[test]
fn test_is_torsion_free() {
let a = G1Affine {
x: Fp::from_raw_unchecked([
0xabaf895b97e43c8,
0xba4c6432eb9b61b0,
0x12506f52adfe307f,
0x75028c3439336b72,
0x84744f05b8e9bd71,
0x113d554fb09554f7,
]),
y: Fp::from_raw_unchecked([
0x73e90e88f5cf01c0,
0x37007b65dd3197e2,
0x5cf9a1992f0d7c78,
0x4f83c10b9eb3330d,
0xf6a63f6f07f60961,
0xc53b5b97e634df3,
]),
infinity: Choice::from(0u8),
};
assert!(!bool::from(a.is_torsion_free()));
assert!(bool::from(G1Affine::identity().is_torsion_free()));
assert!(bool::from(G1Affine::generator().is_torsion_free()));
}
#[test]
fn test_mul_by_x() {
let generator = G1Projective::generator();
let x = if crate::BLS_X_IS_NEGATIVE {
-Scalar::from(crate::BLS_X)
} else {
Scalar::from(crate::BLS_X)
};
assert_eq!(generator.mul_by_x(), generator * x);
let point = G1Projective::generator() * Scalar::from(42);
assert_eq!(point.mul_by_x(), point * x);
}
#[test]
fn test_clear_cofactor() {
let generator = G1Projective::generator();
assert!(bool::from(generator.clear_cofactor().is_on_curve()));
let id = G1Projective::identity();
assert!(bool::from(id.clear_cofactor().is_on_curve()));
let point = G1Projective {
x: Fp::from_raw_unchecked([
0x48af5ff540c817f0,
0xd73893acaf379d5a,
0xe6c43584e18e023c,
0x1eda39c30f188b3e,
0xf618c6d3ccc0f8d8,
0x0073542cd671e16c,
]),
y: Fp::from_raw_unchecked([
0x57bf8be79461d0ba,
0xfc61459cee3547c3,
0x0d23567df1ef147b,
0x0ee187bcce1d9b64,
0xb0c8cfbe9dc8fdc1,
0x1328661767ef368b,
]),
z: Fp::from_raw_unchecked([
0x3d2d1c670671394e,
0x0ee3a800a2f7c1ca,
0x270f4f21da2e5050,
0xe02840a53f1be768,
0x55debeb597512690,
0x08bd25353dc8f791,
]),
};
assert!(bool::from(point.is_on_curve()));
assert!(!bool::from(G1Affine::from(point).is_torsion_free()));
let cleared_point = point.clear_cofactor();
assert!(bool::from(cleared_point.is_on_curve()));
assert!(bool::from(G1Affine::from(cleared_point).is_torsion_free()));
let h_eff = Scalar::from(1) + Scalar::from(crate::BLS_X);
assert_eq!(point.clear_cofactor(), point * h_eff);
}
#[test]
fn test_batch_normalize() {
let a = G1Projective::generator().double();
let b = a.double();
let c = b.double();
for a_identity in (0..1).map(|n| n == 1) {
for b_identity in (0..1).map(|n| n == 1) {
for c_identity in (0..1).map(|n| n == 1) {
let mut v = [a, b, c];
if a_identity {
v[0] = G1Projective::identity()
}
if b_identity {
v[1] = G1Projective::identity()
}
if c_identity {
v[2] = G1Projective::identity()
}
let mut t = [
G1Affine::identity(),
G1Affine::identity(),
G1Affine::identity(),
];
let expected = [
G1Affine::from(v[0]),
G1Affine::from(v[1]),
G1Affine::from(v[2]),
];
G1Projective::batch_normalize(&v[..], &mut t[..]);
assert_eq!(&t[..], &expected[..]);
}
}
}
}
#[test]
fn g1_affine_serde_roundtrip() {
use bincode;
let gen = G1Affine::generator();
let ser = bincode::serialize(&gen).unwrap();
let deser: G1Affine = bincode::deserialize(&ser).unwrap();
assert_eq!(gen, deser);
}