diff --git a/Cargo.toml b/Cargo.toml index e6bde0af..efd4fcbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ bincode = "1.3.3" serde_json = "1.0.105" hex = "0.4" rand_chacha = "0.3.1" +sha3 = "0.10.8" [dependencies] subtle = "2.4" @@ -36,6 +37,8 @@ serde_arrays = { version = "0.1.0", optional = true } hex = { version = "0.4", optional = true, default-features = false, features = ["alloc", "serde"] } blake2b_simd = "1" maybe-rayon = { version = "0.1.0", default-features = false } +digest = "0.10.7" +sha2 = "0.10.8" [features] default = ["bits", "multicore", "bn256-table", "derive_serde"] diff --git a/src/bls12_381/endo.rs b/src/bls12_381/endo.rs new file mode 100644 index 00000000..a3aa8d85 --- /dev/null +++ b/src/bls12_381/endo.rs @@ -0,0 +1,85 @@ +//! Source: https://github.com/privacy-scaling-explorations/halo2curves/blob/support_bls12-381/src/bls12_381/mod.rs + +use crate::arithmetic::mul_512; +use crate::arithmetic::sbb; +use crate::{ + arithmetic::{CurveEndo, EndoParameters}, + endo, +}; +use ff::PrimeField; +use ff::WithSmallOrderMulGroup; +use std::convert::TryInto; + +use super::{G1Projective, Scalar}; + +// Obtained from https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go +// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md +// to have more details about the endomorphism. +const ENDO_PARAMS_BLS: EndoParameters = EndoParameters { + // round(b2/n) + gamma2: [0x63f6e522f6cfee30u64, 0x7c6becf1e01faadd, 0x01, 0x0], + // round(-b1/n) + gamma1: [0x02u64, 0x0, 0x0, 0x0], + b1: [0x01u64, 0x0, 0x0, 0x0], + b2: [0x0000000100000000, 0xac45a4010001a402, 0x0, 0x0], +}; + +endo!(G1Projective, Scalar, ENDO_PARAMS_BLS); + +#[test] +fn test_endo() { + use ff::Field; + use rand_core::OsRng; + + for _ in 0..100000 { + let k = Scalar::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = G1Projective::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!( + k, + -Scalar::from_u128(k1) + Scalar::ZETA * Scalar::from_u128(k2) + ) + } else if k1_neg { + assert_eq!( + k, + -Scalar::from_u128(k1) - Scalar::ZETA * Scalar::from_u128(k2) + ) + } else if k2_neg { + assert_eq!( + k, + Scalar::from_u128(k1) + Scalar::ZETA * Scalar::from_u128(k2) + ) + } else { + assert_eq!( + k, + Scalar::from_u128(k1) - Scalar::ZETA * Scalar::from_u128(k2) + ) + } + } + + for _ in 0..100000 { + let k = Scalar::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = G1Projective::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!( + k, + -Scalar::from_u128(k1) + Scalar::ZETA * Scalar::from_u128(k2) + ) + } else if k1_neg { + assert_eq!( + k, + -Scalar::from_u128(k1) - Scalar::ZETA * Scalar::from_u128(k2) + ) + } else if k2_neg { + assert_eq!( + k, + Scalar::from_u128(k1) + Scalar::ZETA * Scalar::from_u128(k2) + ) + } else { + assert_eq!( + k, + Scalar::from_u128(k1) - Scalar::ZETA * Scalar::from_u128(k2) + ) + } + } +} diff --git a/src/bls12_381/fp.rs b/src/bls12_381/fp.rs new file mode 100644 index 00000000..de9ff766 --- /dev/null +++ b/src/bls12_381/fp.rs @@ -0,0 +1,1344 @@ +//! This module provides an implementation of the BLS12-381 base field `GF(p)` +//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +#![allow(clippy::needless_borrow)] +use core::cmp::Ordering; +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; +use ff::{Field, PrimeField, WithSmallOrderMulGroup}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::arithmetic::{adc, mac, sbb}; +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; +/// Represents an element of the base field $\mathbb{F}_p$ of the BLS12-381 elliptic +/// curve construction. +/// The internal representation of this type is six 64-bit unsigned +/// integers in little-endian order. `Fp` values are always in +/// Montgomery form; i.e., Scalar(a) = aR mod p, with R = 2^384. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Fp(pub(crate) [u64; 6]); + +impl fmt::Debug for Fp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let tmp = self.to_bytes(); + write!(f, "0x")?; + for &b in tmp.iter().rev() { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl Default for Fp { + fn default() -> Self { + Fp::zero() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for Fp {} + +impl ConstantTimeEq for Fp { + fn ct_eq(&self, other: &Self) -> Choice { + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) + & self.0[4].ct_eq(&other.0[4]) + & self.0[5].ct_eq(&other.0[5]) + } +} + +// impl Eq for Fp {} +// impl PartialEq for Fp { +// #[inline] +// fn eq(&self, other: &Self) -> bool { +// bool::from(self.ct_eq(other)) +// } +// } + +impl Ord for Fp { + fn cmp(&self, other: &Self) -> Ordering { + let left = self.to_repr().0; + let right = other.to_repr().0; + left.iter() + .zip(right.iter()) + .rev() + .find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) { + Ordering::Equal => None, + res => Some(res), + }) + .unwrap_or(Ordering::Equal) + } +} + +impl PartialOrd for Fp { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl ConditionallySelectable for Fp { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + u64::conditional_select(&a.0[4], &b.0[4], choice), + u64::conditional_select(&a.0[5], &b.0[5], choice), + ]) + } +} + +impl From for Fp { + fn from(value: u64) -> Self { + Self([value, 0, 0, 0, 0, 0]) * R2 + } +} + +/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +const MODULUS: [u64; 6] = [ + 0xb9fe_ffff_ffff_aaab, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, +]; + +/// INV = -(p^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0x89f3_fffc_fffc_fffd; + +/// R = 2^384 mod p +const R: Fp = Fp([ + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, +]); + +/// R2 = 2^(384*2) mod p +const R2: Fp = Fp([ + 0xf4df_1f34_1c34_1746, + 0x0a76_e6a6_09d1_04f1, + 0x8de5_476c_4c95_b6d5, + 0x67eb_88a9_939d_83c0, + 0x9a79_3e85_b519_952d, + 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, +]); + +/// Fp(1/2) +/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +/// sage: hex(((1 / 2) * (2^384)) % modulus) +/// '0x17fbb8571a006596d3916126f2d14ca26e22d1ec31ebb502633cb57c253c276f855000053ab000011804000000015554' +const TWO_INV: Fp = Fp([ + 0x1804000000015554, + 0x855000053ab00001, + 0x633cb57c253c276f, + 0x6e22d1ec31ebb502, + 0xd3916126f2d14ca2, + 0x17fbb8571a006596, +]); + +/// Generator of the field. +/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +/// sage: hex((2 * (2^384)) % modulus) +/// '0x11ebab9dbb81e28c6cf28d7901622c038b256521ed1f9bcb57605e0db0ddbb51b93c0018d6c40005321300000006554f' +const GENERATOR: Fp = Fp([ + 0x321300000006554f, + 0xb93c0018d6c40005, + 0x57605e0db0ddbb51, + 0x8b256521ed1f9bcb, + 0x6cf28d7901622c03, + 0x11ebab9dbb81e28c, +]); + +/// Primitive root of unity +/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +/// sage: hex((GF(modulus)(2) ^ ((modulus - 1) >> 1)) * (2^384)) +/// '0x40ab3263eff0206ef148d1ea0f4c069eca8f3318332bb7a07e83a49a2e99d6932b7fff2ed47fffd43f5fffffffcaaae' +const ROOT_OF_UNITY: Fp = Fp([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x07e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, +]); + +/// Inverse of the primitive root of unity. +/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +/// sage: hex((1 / (GF(modulus)(2) ^ ((modulus - 1) >> 1))) * (2^384)) +/// '0x40ab3263eff0206ef148d1ea0f4c069eca8f3318332bb7a07e83a49a2e99d6932b7fff2ed47fffd43f5fffffffcaaae' +const ROOT_OF_UNITY_INV: Fp = Fp([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x07e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, +]); + +/// DELTA +/// DELTA +/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +/// sage: hex((GF(modulus)(2) ^ (2 ^ 1)) * (2^384)) +/// '0x9d645513d83de7e8ec9733bbf78ab2fb1d37ebee6ba24d7478fe97a6b0a807f53cc0032fc34000aaa270000000cfff3' +const DELTA: Fp = Fp([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, +]); + +/// ZETA +/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +/// sage: hex((1 / (GF(modulus)(2) ^ ((modulus - 1) // 3))) * (2^384)) +/// '0x18f020655463874103f97d6e83d050d28eb60ebe01bacb9e587042afd3851b955dab22461fcda5d2cd03c9e48671f071' +const ZETA: Fp = Fp([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741, +]); + +impl<'a> Neg for &'a Fp { + type Output = Fp; + + #[inline] + fn neg(self) -> Fp { + self.neg() + } +} + +impl Neg for Fp { + type Output = Fp; + + #[inline] + fn neg(self) -> Fp { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn sub(self, rhs: &'b Fp) -> Fp { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn add(self, rhs: &'b Fp) -> Fp { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn mul(self, rhs: &'b Fp) -> Fp { + self.mul(rhs) + } +} + +impl_binops_additive!(Fp, Fp); +impl_binops_multiplicative!(Fp, Fp); + +impl core::iter::Sum for Fp +where + T: core::borrow::Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::zero(), |acc, item| acc + item.borrow()) + } +} + +impl core::iter::Product for Fp +where + T: core::borrow::Borrow, +{ + fn product(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::one(), |acc, item| acc * item.borrow()) + } +} + +impl Field for Fp { + const ZERO: Self = Fp::zero(); + const ONE: Self = Fp::one(); + + fn random(rng: impl RngCore) -> Self { + Self::random(rng) + } + + fn square(&self) -> Self { + self.square() + } + + fn double(&self) -> Self { + self.add(self) + } + + fn invert(&self) -> CtOption { + self.invert() + } + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + ff::helpers::sqrt_ratio_generic(num, div) + } + + fn sqrt(&self) -> CtOption { + self.sqrt() + } +} + +/// This struct represents the [`Fp`] struct serialized as an array of 48 bytes +/// in Little Endian. +#[derive(Debug, Clone, Copy)] +pub struct ReprFp(pub(crate) [u8; 48]); + +impl AsMut<[u8]> for ReprFp { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +impl AsRef<[u8]> for ReprFp { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsRef<[u8; 48]> for ReprFp { + fn as_ref(&self) -> &[u8; 48] { + &self.0 + } +} + +impl From<[u8; 48]> for ReprFp { + fn from(value: [u8; 48]) -> Self { + // This uses little endian and so, assumes the array passed in is + // in that format. + Self(value) + } +} + +impl Default for ReprFp { + fn default() -> Self { + Self([0u8; 48]) + } +} + +impl PrimeField for Fp { + type Repr = ReprFp; + + fn from_repr(r: Self::Repr) -> CtOption { + // This uses little endian and so, assumes the array passed in is + // in that format. + Self::from_bytes(&r.0) + } + + fn to_repr(&self) -> Self::Repr { + ReprFp(self.to_bytes()) + } + + fn is_odd(&self) -> Choice { + Choice::from(self.to_bytes()[0] & 1) + } + + const MODULUS: &'static str = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"; + const NUM_BITS: u32 = 381; + const CAPACITY: u32 = Self::NUM_BITS - 1; + const TWO_INV: Self = TWO_INV; + const MULTIPLICATIVE_GENERATOR: Self = GENERATOR; + const S: u32 = 1; + const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; + const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; + const DELTA: Self = DELTA; +} + +impl WithSmallOrderMulGroup<3> for Fp { + const ZETA: Self = ZETA; +} + +impl Fp { + /// Returns zero, the additive identity. + #[inline] + pub const fn zero() -> Fp { + Fp([0, 0, 0, 0, 0, 0]) + } + + /// Returns one, the multiplicative identity. + #[inline] + pub const fn one() -> Fp { + R + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into an `Fp`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { + let mut tmp = Fp([0, 0, 0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); + tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); + tmp.0[4] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()); + tmp.0[5] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow); + let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow); + let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + /// Attempts to convert a big-endian byte representation of + /// a scalar into an `Fp`, failing if the input is not canonical. + pub fn from_bytes_be(bytes: &[u8; 48]) -> CtOption { + let mut tmp = Fp([0, 0, 0, 0, 0, 0]); + + tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); + tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); + tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()); + tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow); + let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow); + let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + /// Converts an element of `Fp` into a byte representation in + /// big-endian byte order. + pub fn to_bytes_be(self) -> [u8; 48] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fp::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, + ); + + let mut res = [0; 48]; + res[0..8].copy_from_slice(&tmp.0[5].to_be_bytes()); + res[8..16].copy_from_slice(&tmp.0[4].to_be_bytes()); + res[16..24].copy_from_slice(&tmp.0[3].to_be_bytes()); + res[24..32].copy_from_slice(&tmp.0[2].to_be_bytes()); + res[32..40].copy_from_slice(&tmp.0[1].to_be_bytes()); + res[40..48].copy_from_slice(&tmp.0[0].to_be_bytes()); + + res + } + + /// Converts an element of `Fp` into a byte representation in + /// little-endian byte order. + pub fn to_bytes(self) -> [u8; 48] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fp::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, + ); + + let mut res = [0; 48]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + res[32..40].copy_from_slice(&tmp.0[4].to_le_bytes()); + res[40..48].copy_from_slice(&tmp.0[5].to_le_bytes()); + + res + } + + /// Generates a new uniformly-distributed random element using the given randomness. + pub fn random(mut rng: impl RngCore) -> 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 { + // This can be determined by checking to see if the element is + // larger than (p - 1) // 2. If we subtract by ((p - 1) // 2) + 1 + // and there is no underflow, then the element must be larger than + // (p - 1) // 2. + + // First, because self is in Montgomery form we need to reduce it + let tmp = Fp::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, + ); + + let (_, borrow) = sbb(tmp.0[0], 0xdcff_7fff_ffff_d556, 0); + let (_, borrow) = sbb(tmp.0[1], 0x0f55_ffff_58a9_ffff, borrow); + let (_, borrow) = sbb(tmp.0[2], 0xb398_6950_7b58_7b12, borrow); + let (_, borrow) = sbb(tmp.0[3], 0xb23b_a5c2_79c2_895f, borrow); + let (_, borrow) = sbb(tmp.0[4], 0x258d_d3db_21a5_d66b, borrow); + let (_, borrow) = sbb(tmp.0[5], 0x0d00_88f5_1cbf_f34d, borrow); + + // If the element was smaller, the subtraction will underflow + // producing a borrow value of 0xffff...ffff, otherwise it will + // be zero. We create a Choice representing true if there was + // overflow (and so this element is not lexicographically larger + // than its negation) and then negate it. + + !Choice::from((borrow as u8) & 1) + } + + /// Constructs an element of `Fp` without checking that it is + /// canonical. + pub const fn from_raw_unchecked(v: [u64; 6]) -> Fp { + Fp(v) + } + + /// Although this is labeled "vartime", it is only + /// variable time with respect to the exponent. It + /// is also not exposed in the public API. + pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } + + #[inline] + /// Performs the square root of an element in constant time. + /// + /// NOTE: + /// We use Shank's method, as p = 3 (mod 4). This means + /// we only need to exponentiate by (p+1)/4. This only + /// works for elements that are actually quadratic residue, + /// so we check that we got the correct result at the end. + pub fn sqrt(&self) -> CtOption { + let sqrt = self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaab, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]); + + CtOption::new(sqrt, sqrt.square().ct_eq(self)) + } + + #[inline] + /// Computes the multiplicative inverse of this field + /// element, returning None in the case that this element + /// is zero. + pub fn invert(&self) -> CtOption { + // Exponentiate by p - 2 + let t = self.pow_vartime(&[ + 0xb9fe_ffff_ffff_aaa9, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, + ]); + + CtOption::new(t, !self.is_zero()) + } + + #[inline] + const fn subtract_p(&self) -> Fp { + let (r0, borrow) = sbb(self.0[0], MODULUS[0], 0); + let (r1, borrow) = sbb(self.0[1], MODULUS[1], borrow); + let (r2, borrow) = sbb(self.0[2], MODULUS[2], borrow); + let (r3, borrow) = sbb(self.0[3], MODULUS[3], borrow); + let (r4, borrow) = sbb(self.0[4], MODULUS[4], borrow); + let (r5, borrow) = sbb(self.0[5], MODULUS[5], borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask! + let r0 = (self.0[0] & borrow) | (r0 & !borrow); + let r1 = (self.0[1] & borrow) | (r1 & !borrow); + let r2 = (self.0[2] & borrow) | (r2 & !borrow); + let r3 = (self.0[3] & borrow) | (r3 & !borrow); + let r4 = (self.0[4] & borrow) | (r4 & !borrow); + let r5 = (self.0[5] & borrow) | (r5 & !borrow); + + Fp([r0, r1, r2, r3, r4, r5]) + } + + #[inline] + /// Performs constant time addition of two elements. + pub const fn add(&self, rhs: &Fp) -> Fp { + let (d0, carry) = adc(self.0[0], rhs.0[0], 0); + let (d1, carry) = adc(self.0[1], rhs.0[1], carry); + let (d2, carry) = adc(self.0[2], rhs.0[2], carry); + let (d3, carry) = adc(self.0[3], rhs.0[3], carry); + let (d4, carry) = adc(self.0[4], rhs.0[4], carry); + let (d5, _) = adc(self.0[5], rhs.0[5], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (Fp([d0, d1, d2, d3, d4, d5])).subtract_p() + } + + #[inline] + /// Performs constant time negation of an element. + pub const fn neg(&self) -> Fp { + let (d0, borrow) = sbb(MODULUS[0], self.0[0], 0); + let (d1, borrow) = sbb(MODULUS[1], self.0[1], borrow); + let (d2, borrow) = sbb(MODULUS[2], self.0[2], borrow); + let (d3, borrow) = sbb(MODULUS[3], self.0[3], borrow); + let (d4, borrow) = sbb(MODULUS[4], self.0[4], borrow); + let (d5, _) = sbb(MODULUS[5], self.0[5], borrow); + + // Let's use a mask if `self` was zero, which would mean + // the result of the subtraction is p. + let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4] | self.0[5]) == 0) + as u64) + .wrapping_sub(1); + + Fp([ + d0 & mask, + d1 & mask, + d2 & mask, + d3 & mask, + d4 & mask, + d5 & mask, + ]) + } + + #[inline] + /// Performs constant time subtraction of two elements. + pub const fn sub(&self, rhs: &Fp) -> Fp { + (&rhs.neg()).add(self) + } + + /// Returns `c = a.zip(b).fold(0, |acc, (a_i, b_i)| acc + a_i * b_i)`. + /// + /// Implements Algorithm 2 from Patrick Longa's + /// [ePrint 2022-367](https://eprint.iacr.org/2022/367) ยง3. + #[inline] + pub(crate) fn sum_of_products(a: [Fp; T], b: [Fp; T]) -> Fp { + // For a single `a x b` multiplication, operand scanning (schoolbook) takes each + // limb of `a` in turn, and multiplies it by all of the limbs of `b` to compute + // the result as a double-width intermediate representation, which is then fully + // reduced at the end. Here however we have pairs of multiplications (a_i, b_i), + // the results of which are summed. + // + // The intuition for this algorithm is two-fold: + // - We can interleave the operand scanning for each pair, by processing the jth + // limb of each `a_i` together. As these have the same offset within the overall + // operand scanning flow, their results can be summed directly. + // - We can interleave the multiplication and reduction steps, resulting in a + // single bitshift by the limb size after each iteration. This means we only + // need to store a single extra limb overall, instead of keeping around all the + // intermediate results and eventually having twice as many limbs. + + // Algorithm 2, line 2 + let (u0, u1, u2, u3, u4, u5) = + (0..6).fold((0, 0, 0, 0, 0, 0), |(u0, u1, u2, u3, u4, u5), j| { + // Algorithm 2, line 3 + // For each pair in the overall sum of products: + let (t0, t1, t2, t3, t4, t5, t6) = (0..T).fold( + (u0, u1, u2, u3, u4, u5, 0), + |(t0, t1, t2, t3, t4, t5, t6), i| { + // Compute digit_j x row and accumulate into `u`. + let (t0, carry) = mac(t0, a[i].0[j], b[i].0[0], 0); + let (t1, carry) = mac(t1, a[i].0[j], b[i].0[1], carry); + let (t2, carry) = mac(t2, a[i].0[j], b[i].0[2], carry); + let (t3, carry) = mac(t3, a[i].0[j], b[i].0[3], carry); + let (t4, carry) = mac(t4, a[i].0[j], b[i].0[4], carry); + let (t5, carry) = mac(t5, a[i].0[j], b[i].0[5], carry); + let (t6, _) = adc(t6, 0, carry); + + (t0, t1, t2, t3, t4, t5, t6) + }, + ); + + // Algorithm 2, lines 4-5 + // This is a single step of the usual Montgomery reduction process. + let k = t0.wrapping_mul(INV); + let (_, carry) = mac(t0, k, MODULUS[0], 0); + let (r1, carry) = mac(t1, k, MODULUS[1], carry); + let (r2, carry) = mac(t2, k, MODULUS[2], carry); + let (r3, carry) = mac(t3, k, MODULUS[3], carry); + let (r4, carry) = mac(t4, k, MODULUS[4], carry); + let (r5, carry) = mac(t5, k, MODULUS[5], carry); + let (r6, _) = adc(t6, 0, carry); + + (r1, r2, r3, r4, r5, r6) + }); + + // Because we represent F_p elements in non-redundant form, we need a final + // conditional subtraction to ensure the output is in range. + (&Fp([u0, u1, u2, u3, u4, u5])).subtract_p() + } + + #[inline(always)] + pub(crate) const fn montgomery_reduce( + t0: u64, + t1: u64, + t2: u64, + t3: u64, + t4: u64, + t5: u64, + t6: u64, + t7: u64, + t8: u64, + t9: u64, + t10: u64, + t11: u64, + ) -> Self { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = t0.wrapping_mul(INV); + let (_, carry) = mac(t0, k, MODULUS[0], 0); + let (r1, carry) = mac(t1, k, MODULUS[1], carry); + let (r2, carry) = mac(t2, k, MODULUS[2], carry); + let (r3, carry) = mac(t3, k, MODULUS[3], carry); + let (r4, carry) = mac(t4, k, MODULUS[4], carry); + let (r5, carry) = mac(t5, k, MODULUS[5], carry); + let (r6, r7) = adc(t6, 0, carry); + + let k = r1.wrapping_mul(INV); + let (_, carry) = mac(r1, k, MODULUS[0], 0); + let (r2, carry) = mac(r2, k, MODULUS[1], carry); + let (r3, carry) = mac(r3, k, MODULUS[2], carry); + let (r4, carry) = mac(r4, k, MODULUS[3], carry); + let (r5, carry) = mac(r5, k, MODULUS[4], carry); + let (r6, carry) = mac(r6, k, MODULUS[5], carry); + let (r7, r8) = adc(t7, r7, carry); + + let k = r2.wrapping_mul(INV); + let (_, carry) = mac(r2, k, MODULUS[0], 0); + let (r3, carry) = mac(r3, k, MODULUS[1], carry); + let (r4, carry) = mac(r4, k, MODULUS[2], carry); + let (r5, carry) = mac(r5, k, MODULUS[3], carry); + let (r6, carry) = mac(r6, k, MODULUS[4], carry); + let (r7, carry) = mac(r7, k, MODULUS[5], carry); + let (r8, r9) = adc(t8, r8, carry); + + let k = r3.wrapping_mul(INV); + let (_, carry) = mac(r3, k, MODULUS[0], 0); + let (r4, carry) = mac(r4, k, MODULUS[1], carry); + let (r5, carry) = mac(r5, k, MODULUS[2], carry); + let (r6, carry) = mac(r6, k, MODULUS[3], carry); + let (r7, carry) = mac(r7, k, MODULUS[4], carry); + let (r8, carry) = mac(r8, k, MODULUS[5], carry); + let (r9, r10) = adc(t9, r9, carry); + + let k = r4.wrapping_mul(INV); + let (_, carry) = mac(r4, k, MODULUS[0], 0); + let (r5, carry) = mac(r5, k, MODULUS[1], carry); + let (r6, carry) = mac(r6, k, MODULUS[2], carry); + let (r7, carry) = mac(r7, k, MODULUS[3], carry); + let (r8, carry) = mac(r8, k, MODULUS[4], carry); + let (r9, carry) = mac(r9, k, MODULUS[5], carry); + let (r10, r11) = adc(t10, r10, carry); + + let k = r5.wrapping_mul(INV); + let (_, carry) = mac(r5, k, MODULUS[0], 0); + let (r6, carry) = mac(r6, k, MODULUS[1], carry); + let (r7, carry) = mac(r7, k, MODULUS[2], carry); + let (r8, carry) = mac(r8, k, MODULUS[3], carry); + let (r9, carry) = mac(r9, k, MODULUS[4], carry); + let (r10, carry) = mac(r10, k, MODULUS[5], carry); + let (r11, _) = adc(t11, r11, carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Fp([r6, r7, r8, r9, r10, r11])).subtract_p() + } + + #[inline] + /// Performs constant time multiplication of two elements. + pub const fn mul(&self, rhs: &Fp) -> Fp { + let (t0, carry) = mac(0, self.0[0], rhs.0[0], 0); + let (t1, carry) = mac(0, self.0[0], rhs.0[1], carry); + let (t2, carry) = mac(0, self.0[0], rhs.0[2], carry); + let (t3, carry) = mac(0, self.0[0], rhs.0[3], carry); + let (t4, carry) = mac(0, self.0[0], rhs.0[4], carry); + let (t5, t6) = mac(0, self.0[0], rhs.0[5], carry); + + let (t1, carry) = mac(t1, self.0[1], rhs.0[0], 0); + let (t2, carry) = mac(t2, self.0[1], rhs.0[1], carry); + let (t3, carry) = mac(t3, self.0[1], rhs.0[2], carry); + let (t4, carry) = mac(t4, self.0[1], rhs.0[3], carry); + let (t5, carry) = mac(t5, self.0[1], rhs.0[4], carry); + let (t6, t7) = mac(t6, self.0[1], rhs.0[5], carry); + + let (t2, carry) = mac(t2, self.0[2], rhs.0[0], 0); + let (t3, carry) = mac(t3, self.0[2], rhs.0[1], carry); + let (t4, carry) = mac(t4, self.0[2], rhs.0[2], carry); + let (t5, carry) = mac(t5, self.0[2], rhs.0[3], carry); + let (t6, carry) = mac(t6, self.0[2], rhs.0[4], carry); + let (t7, t8) = mac(t7, self.0[2], rhs.0[5], carry); + + let (t3, carry) = mac(t3, self.0[3], rhs.0[0], 0); + let (t4, carry) = mac(t4, self.0[3], rhs.0[1], carry); + let (t5, carry) = mac(t5, self.0[3], rhs.0[2], carry); + let (t6, carry) = mac(t6, self.0[3], rhs.0[3], carry); + let (t7, carry) = mac(t7, self.0[3], rhs.0[4], carry); + let (t8, t9) = mac(t8, self.0[3], rhs.0[5], carry); + + let (t4, carry) = mac(t4, self.0[4], rhs.0[0], 0); + let (t5, carry) = mac(t5, self.0[4], rhs.0[1], carry); + let (t6, carry) = mac(t6, self.0[4], rhs.0[2], carry); + let (t7, carry) = mac(t7, self.0[4], rhs.0[3], carry); + let (t8, carry) = mac(t8, self.0[4], rhs.0[4], carry); + let (t9, t10) = mac(t9, self.0[4], rhs.0[5], carry); + + let (t5, carry) = mac(t5, self.0[5], rhs.0[0], 0); + let (t6, carry) = mac(t6, self.0[5], rhs.0[1], carry); + let (t7, carry) = mac(t7, self.0[5], rhs.0[2], carry); + let (t8, carry) = mac(t8, self.0[5], rhs.0[3], carry); + let (t9, carry) = mac(t9, self.0[5], rhs.0[4], carry); + let (t10, t11) = mac(t10, self.0[5], rhs.0[5], carry); + + Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + } + + /// Squares this element. + #[inline] + pub const fn square(&self) -> Self { + let (t1, carry) = mac(0, self.0[0], self.0[1], 0); + let (t2, carry) = mac(0, self.0[0], self.0[2], carry); + let (t3, carry) = mac(0, self.0[0], self.0[3], carry); + let (t4, carry) = mac(0, self.0[0], self.0[4], carry); + let (t5, t6) = mac(0, self.0[0], self.0[5], carry); + + let (t3, carry) = mac(t3, self.0[1], self.0[2], 0); + let (t4, carry) = mac(t4, self.0[1], self.0[3], carry); + let (t5, carry) = mac(t5, self.0[1], self.0[4], carry); + let (t6, t7) = mac(t6, self.0[1], self.0[5], carry); + + let (t5, carry) = mac(t5, self.0[2], self.0[3], 0); + let (t6, carry) = mac(t6, self.0[2], self.0[4], carry); + let (t7, t8) = mac(t7, self.0[2], self.0[5], carry); + + let (t7, carry) = mac(t7, self.0[3], self.0[4], 0); + let (t8, t9) = mac(t8, self.0[3], self.0[5], carry); + + let (t9, t10) = mac(t9, self.0[4], self.0[5], 0); + + let t11 = t10 >> 63; + let t10 = (t10 << 1) | (t9 >> 63); + let t9 = (t9 << 1) | (t8 >> 63); + let t8 = (t8 << 1) | (t7 >> 63); + let t7 = (t7 << 1) | (t6 >> 63); + let t6 = (t6 << 1) | (t5 >> 63); + let t5 = (t5 << 1) | (t4 >> 63); + let t4 = (t4 << 1) | (t3 >> 63); + let t3 = (t3 << 1) | (t2 >> 63); + let t2 = (t2 << 1) | (t1 >> 63); + let t1 = t1 << 1; + + let (t0, carry) = mac(0, self.0[0], self.0[0], 0); + let (t1, carry) = adc(t1, 0, carry); + let (t2, carry) = mac(t2, self.0[1], self.0[1], carry); + let (t3, carry) = adc(t3, 0, carry); + let (t4, carry) = mac(t4, self.0[2], self.0[2], carry); + let (t5, carry) = adc(t5, 0, carry); + let (t6, carry) = mac(t6, self.0[3], self.0[3], carry); + let (t7, carry) = adc(t7, 0, carry); + let (t8, carry) = mac(t8, self.0[4], self.0[4], carry); + let (t9, carry) = adc(t9, 0, carry); + let (t10, carry) = mac(t10, self.0[5], self.0[5], carry); + let (t11, _) = adc(t11, 0, carry); + + Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + } +} + +impl ff::FromUniformBytes<64> for Fp { + /// Converts a 512-bit little endian integer into + /// an `Fq` by reducing by the modulus. + fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { + Self::from_u512([ + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + u64::from_le_bytes(bytes[16..24].try_into().unwrap()), + u64::from_le_bytes(bytes[24..32].try_into().unwrap()), + u64::from_le_bytes(bytes[32..40].try_into().unwrap()), + u64::from_le_bytes(bytes[40..48].try_into().unwrap()), + u64::from_le_bytes(bytes[48..56].try_into().unwrap()), + u64::from_le_bytes(bytes[56..64].try_into().unwrap()), + ]) + } +} + +impl Fp { + fn from_u512(limbs: [u64; 8]) -> Fp { + let d0 = Fp([limbs[0], limbs[1], limbs[2], limbs[3], limbs[4], limbs[5]]); + let d1 = Fp([limbs[6], limbs[7], 0, 0, 0, 0]); + // Convert to Montgomery form + d0 * R2 + d1 * R3 + } +} + +impl From for Fp { + fn from(bit: bool) -> Fp { + if bit { + Fp::one() + } else { + Fp::zero() + } + } +} + +impl From<[u64; 6]> for Fp { + fn from(digits: [u64; 6]) -> Self { + Self::from_raw_unchecked(digits) + } +} + +#[test] +fn test_constants() { + assert_eq!(Fp::ONE.double(), GENERATOR); + assert_eq!(Fp::ONE, Fp::ONE.double() * TWO_INV); + assert_eq!(Fp::ONE, ROOT_OF_UNITY.pow([1 << Fp::S])); + assert_eq!(Fp::ONE, ROOT_OF_UNITY * ROOT_OF_UNITY_INV); + // sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab + // sage: hex((modulus - 1) >> 1) + // '0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd555' + let t = [ + 0xdcff7fffffffd555, + 0x0f55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d, + ]; + assert_eq!(Fp::ONE, DELTA.pow(t)); + assert_eq!(Fp::ONE, ZETA.pow([3])); +} + +#[test] +fn test_conditional_selection() { + let a = Fp([1, 2, 3, 4, 5, 6]); + let b = Fp([7, 8, 9, 10, 11, 12]); + + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_equality() { + fn is_equal(a: &Fp, b: &Fp) -> bool { + let eq = a == b; + let ct_eq = a.ct_eq(b); + + assert_eq!(eq, bool::from(ct_eq)); + + eq + } + + assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + + assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6]))); +} + +#[test] +fn test_squaring() { + let a = Fp([ + 0xd215_d276_8e83_191b, + 0x5085_d80f_8fb2_8261, + 0xce9a_032d_df39_3a56, + 0x3e9c_4fff_2ca0_c4bb, + 0x6436_b6f7_f4d9_5dfb, + 0x1060_6628_ad4a_4d90, + ]); + let b = Fp([ + 0x33d9_c42a_3cb3_e235, + 0xdad1_1a09_4c4c_d455, + 0xa2f1_44bd_729a_aeba, + 0xd415_0932_be9f_feac, + 0xe27b_c7c4_7d44_ee50, + 0x14b6_a78d_3ec7_a560, + ]); + + assert_eq!(a.square(), b); +} + +#[test] +fn test_multiplication() { + let a = Fp([ + 0x0397_a383_2017_0cd4, + 0x734c_1b2c_9e76_1d30, + 0x5ed2_55ad_9a48_beb5, + 0x095a_3c6b_22a7_fcfc, + 0x2294_ce75_d4e2_6a27, + 0x1333_8bd8_7001_1ebb, + ]); + let b = Fp([ + 0xb9c3_c7c5_b119_6af7, + 0x2580_e208_6ce3_35c1, + 0xf49a_ed3d_8a57_ef42, + 0x41f2_81e4_9846_e878, + 0xe076_2346_c384_52ce, + 0x0652_e893_26e5_7dc0, + ]); + let c = Fp([ + 0xf96e_f3d7_11ab_5355, + 0xe8d4_59ea_00f1_48dd, + 0x53f7_354a_5f00_fa78, + 0x9e34_a4f3_125c_5f83, + 0x3fbe_0c47_ca74_c19e, + 0x01b0_6a8b_bd4a_dfe4, + ]); + + assert_eq!(a * b, c); +} + +#[test] +fn test_addition() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091, + ]); + let c = Fp([ + 0x3934_42cc_b58b_b327, + 0x1092_685f_3bd5_47e3, + 0x3382_252c_ab6a_c4c9, + 0xf946_94cb_7688_7f55, + 0x4b21_5e90_93a5_e071, + 0x0d56_e30f_34f5_f853, + ]); + + assert_eq!(a + b, c); +} + +#[test] +fn test_subtraction() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091, + ]); + let c = Fp([ + 0x6d8d_33e6_3b43_4d3d, + 0xeb12_82fd_b766_dd39, + 0x8534_7bb6_f133_d6d5, + 0xa21d_aa5a_9892_f727, + 0x3b25_6cfb_3ad8_ae23, + 0x155d_7199_de7f_8464, + ]); + + assert_eq!(a - b, c); +} + +#[test] +fn test_negation() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x669e_44a6_8798_2a79, + 0xa0d9_8a50_37b5_ed71, + 0x0ad5_822f_2861_a854, + 0x96c5_2bf1_ebf7_5781, + 0x87f8_41f0_5c0c_658c, + 0x08a6_e795_afc5_283e, + ]); + + assert_eq!(-a, b); +} + +#[test] +fn test_from_bytes() { + let mut a = Fp([ + 0xdc90_6d9b_e3f9_5dc8, + 0x8755_caf7_4596_91a1, + 0xcff1_a7f4_e958_3ab3, + 0x9b43_821f_849e_2284, + 0xf575_54f3_a297_4f3f, + 0x085d_bea8_4ed4_7f79, + ]); + + for _ in 0..100 { + a = a.square(); + let tmp = a.to_bytes_be(); + let b = Fp::from_bytes_be(&tmp).unwrap(); + + assert_eq!(a, b); + } + + assert_eq!( + -Fp::one(), + Fp::from_bytes_be(&[ + 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + ]) + .unwrap() + ); + + assert!(bool::from( + Fp::from_bytes_be(&[ + 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + ]) + .is_none() + )); + + assert!(bool::from(Fp::from_bytes_be(&[0xff; 48]).is_none())); +} + +#[test] +fn test_sqrt() { + // a = 4 + let a = Fp::from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, + ]); + + assert_eq!( + // sqrt(4) = -2 + -a.sqrt().unwrap(), + // 2 + Fp::from_raw_unchecked([ + 0x3213_0000_0006_554f, + 0xb93c_0018_d6c4_0005, + 0x5760_5e0d_b0dd_bb51, + 0x8b25_6521_ed1f_9bcb, + 0x6cf2_8d79_0162_2c03, + 0x11eb_ab9d_bb81_e28c, + ]) + ); +} + +#[test] +fn test_inversion() { + let a = Fp([ + 0x43b4_3a50_78ac_2076, + 0x1ce0_7630_46f8_962b, + 0x724a_5276_486d_735c, + 0x6f05_c2a6_282d_48fd, + 0x2095_bd5b_b4ca_9331, + 0x03b3_5b38_94b0_f7da, + ]); + let b = Fp([ + 0x69ec_d704_0952_148f, + 0x985c_cc20_2219_0f55, + 0xe19b_ba36_a9ad_2f41, + 0x19bb_16c9_5219_dbd8, + 0x14dc_acfd_fb47_8693, + 0x115f_f58a_fff9_a8e1, + ]); + + assert_eq!(a.invert().unwrap(), b); + assert!(bool::from(Fp::zero().invert().is_none())); +} + +#[test] +fn test_lexicographic_largest() { + assert!(!bool::from(Fp::zero().lexicographically_largest())); + assert!(!bool::from(Fp::one().lexicographically_largest())); + assert!(!bool::from( + Fp::from_raw_unchecked([ + 0xa1fa_ffff_fffe_5557, + 0x995b_fff9_76a3_fffe, + 0x03f4_1d24_d174_ceb4, + 0xf654_7998_c199_5dbd, + 0x778a_468f_507a_6034, + 0x0205_5993_1f7f_8103 + ]) + .lexicographically_largest() + )); + assert!(bool::from( + Fp::from_raw_unchecked([ + 0x1804_0000_0001_5554, + 0x8550_0005_3ab0_0001, + 0x633c_b57c_253c_276f, + 0x6e22_d1ec_31eb_b502, + 0xd391_6126_f2d1_4ca2, + 0x17fb_b857_1a00_6596, + ]) + .lexicographically_largest() + )); + assert!(bool::from( + Fp::from_raw_unchecked([ + 0x43f5_ffff_fffc_aaae, + 0x32b7_fff2_ed47_fffd, + 0x07e8_3a49_a2e9_9d69, + 0xeca8_f331_8332_bb7a, + 0xef14_8d1e_a0f4_c069, + 0x040a_b326_3eff_0206, + ]) + .lexicographically_largest() + )); +} + +#[cfg(feature = "zeroize")] +#[test] +fn test_zeroize() { + use zeroize::Zeroize; + + let mut a = Fp::one(); + a.zeroize(); + assert!(bool::from(a.is_zero())); +} + +#[test] +fn test_from_bytes_debug() { + // let mut bytes = vec![ + // 220u8, 214, 26, 93, 113, 152, 62, 165, 185, 230, 195, 24, 61, 41, 116, 148, 248, 59, 10, + // 81, 185, 242, 151, 83, 182, 242, 42, 201, 239, 33, 70, 221, 221, 179, 240, 87, 84, 220, + // 123, 92, 72, 180, 248, 99, 100, 200, 6, 9 + // ]; + // // bytes.reserve(0); + assert!(bool::from( + Fp::from_bytes(&[ + 220u8, 214, 26, 93, 113, 152, 62, 165, 185, 230, 195, 24, 61, 41, 116, 148, 248, 59, + 10, 81, 185, 242, 151, 83, 182, 242, 42, 201, 239, 33, 70, 221, 221, 179, 240, 87, 84, + 220, 123, 92, 72, 180, 248, 99, 100, 200, 6, 9 + ]) + .is_some() + )); +} diff --git a/src/bls12_381/fp12.rs b/src/bls12_381/fp12.rs new file mode 100644 index 00000000..83fd6874 --- /dev/null +++ b/src/bls12_381/fp12.rs @@ -0,0 +1,900 @@ +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; + +use super::fp::*; +use super::fp2::*; +use super::fp6::*; + +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use rand_core::RngCore; + +/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6}[w] / (w^2 - v)$. +pub struct Fp12 { + pub c0: Fp6, + pub c1: Fp6, +} + +impl From for Fp12 { + fn from(f: Fp) -> Fp12 { + Fp12 { + c0: Fp6::from(f), + c1: Fp6::zero(), + } + } +} + +impl From for Fp12 { + fn from(f: Fp2) -> Fp12 { + Fp12 { + c0: Fp6::from(f), + c1: Fp6::zero(), + } + } +} + +impl From for Fp12 { + fn from(f: Fp6) -> Fp12 { + Fp12 { + c0: f, + c1: Fp6::zero(), + } + } +} + +impl Eq for Fp12 {} +impl PartialEq for Fp12 { + fn eq(&self, other: &Fp12) -> bool { + self.ct_eq(other).into() + } +} + +impl Copy for Fp12 {} +impl Clone for Fp12 { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Default for Fp12 { + fn default() -> Self { + Fp12::zero() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for Fp12 {} + +impl fmt::Debug for Fp12 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + ({:?})*w", self.c0, self.c1) + } +} + +impl ConditionallySelectable for Fp12 { + #[inline(always)] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp12 { + c0: Fp6::conditional_select(&a.c0, &b.c0, choice), + c1: Fp6::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl ConstantTimeEq for Fp12 { + #[inline(always)] + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) + } +} + +impl Fp12 { + #[inline] + pub const fn zero() -> Self { + Fp12 { + c0: Fp6::zero(), + c1: Fp6::zero(), + } + } + + #[inline] + pub const fn one() -> Self { + Fp12 { + c0: Fp6::one(), + c1: Fp6::zero(), + } + } + + pub(crate) fn random(mut rng: impl RngCore) -> Self { + Fp12 { + c0: Fp6::random(&mut rng), + c1: Fp6::random(&mut rng), + } + } + + pub fn double(&self) -> Self { + Self { + c0: self.c0.double(), + c1: self.c1.double(), + } + } + + pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 { + let aa = self.c0.mul_by_01(c0, c1); + let bb = self.c1.mul_by_1(c4); + let o = c1 + c4; + let c1 = self.c1 + self.c0; + let c1 = c1.mul_by_01(c0, &o); + let c1 = c1 - aa - bb; + let c0 = bb; + let c0 = c0.mul_by_nonresidue(); + let c0 = c0 + aa; + + Fp12 { c0, c1 } + } + + #[inline(always)] + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + #[inline(always)] + pub fn conjugate(&self) -> Self { + Fp12 { + c0: self.c0, + c1: -self.c1, + } + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + let c0 = self.c0.frobenius_map(); + let c1 = self.c1.frobenius_map(); + + // c1 = c1 * (u + 1)^((p - 1) / 6) + let c1 = c1 + * Fp6::from(Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0708_9552_b319_d465, + 0xc669_5f92_b50a_8313, + 0x97e8_3ccc_d117_228f, + 0xa35b_aeca_b2dc_29ee, + 0x1ce3_93ea_5daa_ce4d, + 0x08f2_220f_b0fb_66eb, + ]), + c1: Fp::from_raw_unchecked([ + 0xb2f6_6aad_4ce5_d646, + 0x5842_a06b_fc49_7cec, + 0xcf48_95d4_2599_d394, + 0xc11b_9cba_40a8_e8d0, + 0x2e38_13cb_e5a0_de89, + 0x110e_efda_8884_7faf, + ]), + }); + + Fp12 { c0, c1 } + } + + #[inline] + pub fn square(&self) -> Self { + let ab = self.c0 * self.c1; + let c0c1 = self.c0 + self.c1; + let c0 = self.c1.mul_by_nonresidue(); + let c0 = c0 + self.c0; + let c0 = c0 * c0c1; + let c0 = c0 - ab; + let c1 = ab + ab; + let c0 = c0 - ab.mul_by_nonresidue(); + + Fp12 { c0, c1 } + } + + pub fn invert(&self) -> CtOption { + (self.c0.square() - self.c1.square().mul_by_nonresidue()) + .invert() + .map(|t| Fp12 { + c0: self.c0 * t, + c1: self.c1 * -t, + }) + } +} + +impl ff::Field for Fp12 { + const ZERO: Self = Self::zero(); + + const ONE: Self = Self::one(); + + fn random(mut rng: impl RngCore) -> Self { + Fp12 { + c0: Fp6::random(&mut rng), + c1: Fp6::random(&mut rng), + } + } + + fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + fn square(&self) -> Self { + self.square() + } + + fn double(&self) -> Self { + self.double() + } + + fn sqrt(&self) -> CtOption { + unimplemented!() + } + + fn invert(&self) -> CtOption { + self.invert() + } + + fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) { + unimplemented!() + } +} + +impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn mul(self, other: &'b Fp12) -> Self::Output { + let aa = self.c0 * other.c0; + let bb = self.c1 * other.c1; + let o = other.c0 + other.c1; + let c1 = self.c1 + self.c0; + let c1 = c1 * o; + let c1 = c1 - aa; + let c1 = c1 - bb; + let c0 = bb.mul_by_nonresidue(); + let c0 = c0 + aa; + + Fp12 { c0, c1 } + } +} + +impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn add(self, rhs: &'b Fp12) -> Self::Output { + Fp12 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + } + } +} + +impl<'a> Neg for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn neg(self) -> Self::Output { + Fp12 { + c0: -self.c0, + c1: -self.c1, + } + } +} + +impl Neg for Fp12 { + type Output = Fp12; + + #[inline] + fn neg(self) -> Self::Output { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn sub(self, rhs: &'b Fp12) -> Self::Output { + Fp12 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + } + } +} + +impl_binops_additive!(Fp12, Fp12); +impl_binops_multiplicative!(Fp12, Fp12); +crate::impl_sum_prod!(Fp12); + +// non_residue^((modulus^i-1)/6) for i=0,...,11 +pub const FROBENIUS_COEFF_FQ12_C1: [Fp2; 12] = [ + // Fp2(u + 1)**(((q^0) - 1) / 6) + Fp2 { + c0: Fp([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ]), + c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + }, + // Fp2(u + 1)**(((q^1) - 1) / 6) + Fp2 { + c0: Fp([ + 0x7089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x8f2220fb0fb66eb, + ]), + c1: Fp([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ]), + }, + // Fp2(u + 1)**(((q^2) - 1) / 6) + Fp2 { + c0: Fp([ + 0xecfb361b798dba3a, + 0xc100ddb891865a2c, + 0xec08ff1232bda8e, + 0xd5c13cc6f1ca4721, + 0x47222a47bf7b5c04, + 0x110f184e51c5f59, + ]), + c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + }, + // Fp2(u + 1)**(((q^3) - 1) / 6) + Fp2 { + c0: Fp([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0xbd592fc7d825ec8, + ]), + c1: Fp([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0xe2b7eedbbfd87d2, + ]), + }, + // Fp2(u + 1)**(((q^4) - 1) / 6) + Fp2 { + c0: Fp([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ]), + c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + }, + // Fp2(u + 1)**(((q^5) - 1) / 6) + Fp2 { + c0: Fp([ + 0x3726c30af242c66c, + 0x7c2ac1aad1b6fe70, + 0xa04007fbba4b14a2, + 0xef517c3266341429, + 0x95ba654ed2226b, + 0x2e370eccc86f7dd, + ]), + c1: Fp([ + 0x82d83cf50dbce43f, + 0xa2813e53df9d018f, + 0xc6f0caa53c65e181, + 0x7525cf528d50fe95, + 0x4a85ed50f4798a6b, + 0x171da0fd6cf8eebd, + ]), + }, + // Fp2(u + 1)**(((q^6) - 1) / 6) + Fp2 { + c0: Fp([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, + ]), + c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + }, + // Fp2(u + 1)**(((q^7) - 1) / 6) + Fp2 { + c0: Fp([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ]), + c1: Fp([ + 0x7089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x8f2220fb0fb66eb, + ]), + }, + // Fp2(u + 1)**(((q^8) - 1) / 6) + Fp2 { + c0: Fp([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + }, + // Fp2(u + 1)**(((q^9) - 1) / 6) + Fp2 { + c0: Fp([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0xe2b7eedbbfd87d2, + ]), + c1: Fp([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0xbd592fc7d825ec8, + ]), + }, + // Fp2(u + 1)**(((q^10) - 1) / 6) + Fp2 { + c0: Fp([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ]), + c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + }, + // Fp2(u + 1)**(((q^11) - 1) / 6) + Fp2 { + c0: Fp([ + 0x82d83cf50dbce43f, + 0xa2813e53df9d018f, + 0xc6f0caa53c65e181, + 0x7525cf528d50fe95, + 0x4a85ed50f4798a6b, + 0x171da0fd6cf8eebd, + ]), + c1: Fp([ + 0x3726c30af242c66c, + 0x7c2ac1aad1b6fe70, + 0xa04007fbba4b14a2, + 0xef517c3266341429, + 0x95ba654ed2226b, + 0x2e370eccc86f7dd, + ]), + }, +]; + +#[test] +fn test_arithmetic() { + use super::fp::*; + use super::fp2::*; + + let a = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + }; + + let b = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d272_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e348, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd2_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a117_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + }; + + let c = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_71b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0x7791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_133c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_40e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_1744_c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d3_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_1040, + ]), + }, + }, + }; + + // because a and b and c are similar to each other and + // I was lazy, this is just some arbitrary way to make + // them a little more different + let a = a.square().invert().unwrap().square() + c; + let b = b.square().invert().unwrap().square() + a; + let c = c.square().invert().unwrap().square() + b; + + assert_eq!(a.square(), a * a); + assert_eq!(b.square(), b * b); + assert_eq!(c.square(), c * c); + + assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b)); + + assert_eq!( + a.invert().unwrap() * b.invert().unwrap(), + (a * b).invert().unwrap() + ); + assert_eq!(a.invert().unwrap() * a, Fp12::one()); + + assert!(a != a.frobenius_map()); + assert_eq!( + a, + a.frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + ); +} + +#[cfg(feature = "zeroize")] +#[test] +fn test_zeroize() { + use zeroize::Zeroize; + + let mut a = Fp12::one(); + a.zeroize(); + assert!(bool::from(a.is_zero())); +} diff --git a/src/bls12_381/fp2.rs b/src/bls12_381/fp2.rs new file mode 100644 index 00000000..6d2c1108 --- /dev/null +++ b/src/bls12_381/fp2.rs @@ -0,0 +1,1275 @@ +//! This module implements arithmetic over the quadratic extension field Fp2. +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +#![allow(clippy::needless_borrow)] +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; +use ff::{Field, WithSmallOrderMulGroup}; +use rand_core::RngCore; +use std::cmp::Ordering; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; + +use super::fp::Fp; + +#[derive(Copy, Clone)] +pub struct Fp2 { + pub c0: Fp, + pub c1: Fp, +} + +impl fmt::Debug for Fp2 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + {:?}*u", self.c0, self.c1) + } +} + +impl Default for Fp2 { + fn default() -> Self { + Fp2::zero() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for Fp2 {} + +impl From for Fp2 { + fn from(f: Fp) -> Fp2 { + Fp2 { + c0: f, + c1: Fp::zero(), + } + } +} + +impl ConstantTimeEq for Fp2 { + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) + } +} + +impl Eq for Fp2 {} +impl PartialEq for Fp2 { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl Ord for Fp2 { + #[inline(always)] + fn cmp(&self, other: &Fp2) -> Ordering { + match self.c1.cmp(&other.c1) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => self.c0.cmp(&other.c0), + } + } +} + +impl PartialOrd for Fp2 { + #[inline(always)] + fn partial_cmp(&self, other: &Fp2) -> Option { + Some(self.cmp(other)) + } +} + +impl ConditionallySelectable for Fp2 { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp2 { + c0: Fp::conditional_select(&a.c0, &b.c0, choice), + c1: Fp::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl<'a> Neg for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn neg(self) -> Fp2 { + self.neg() + } +} + +impl Neg for Fp2 { + type Output = Fp2; + + #[inline] + fn neg(self) -> Fp2 { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn sub(self, rhs: &'b Fp2) -> Fp2 { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn add(self, rhs: &'b Fp2) -> Fp2 { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn mul(self, rhs: &'b Fp2) -> Fp2 { + self.mul(rhs) + } +} + +impl_binops_additive!(Fp2, Fp2); +impl_binops_multiplicative!(Fp2, Fp2); + +impl Fp2 { + #[inline] + pub const fn zero() -> Fp2 { + Fp2 { + c0: Fp::zero(), + c1: Fp::zero(), + } + } + + #[inline] + pub const fn one() -> Fp2 { + Fp2 { + c0: Fp::one(), + c1: Fp::zero(), + } + } + + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + pub(crate) fn random(mut rng: impl RngCore) -> Fp2 { + Fp2 { + c0: Fp::random(&mut rng), + c1: Fp::random(&mut rng), + } + } + + pub fn double(&self) -> Self { + Self { + c0: self.c0.double(), + c1: self.c1.double(), + } + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + // This is always just a conjugation. If you're curious why, here's + // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ + self.conjugate() + } + + #[inline(always)] + pub fn conjugate(&self) -> Self { + Fp2 { + c0: self.c0, + c1: -self.c1, + } + } + + #[inline(always)] + pub fn mul_by_nonresidue(&self) -> Fp2 { + // Multiply a + bu by u + 1, getting + // au + a + bu^2 + bu + // and because u^2 = -1, we get + // (a - b) + (a + b)u + + Fp2 { + c0: self.c0 - self.c1, + c1: self.c0 + self.c1, + } + } + + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + #[inline] + pub fn lexicographically_largest(&self) -> Choice { + // If this element's c1 coefficient is lexicographically largest + // then it is lexicographically largest. Otherwise, in the event + // the c1 coefficient is zero and the c0 coefficient is + // lexicographically largest, then this element is lexicographically + // largest. + + self.c1.lexicographically_largest() + | (self.c1.is_zero() & self.c0.lexicographically_largest()) + } + + pub const fn square(&self) -> Fp2 { + // Complex squaring: + // + // v0 = c0 * c1 + // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 + // c1' = 2 * v0 + // + // In BLS12-381's F_{p^2}, our \beta is -1 so we + // can modify this formula: + // + // c0' = (c0 + c1) * (c0 - c1) + // c1' = 2 * c0 * c1 + + let a = (&self.c0).add(&self.c1); + let b = (&self.c0).sub(&self.c1); + let c = (&self.c0).add(&self.c0); + + Fp2 { + c0: (&a).mul(&b), + c1: (&c).mul(&self.c1), + } + } + + pub fn mul(&self, rhs: &Fp2) -> Fp2 { + // F_{p^2} x F_{p^2} multiplication implemented with operand scanning (schoolbook) + // computes the result as: + // + // aยทb = (a_0 b_0 + a_1 b_1 ฮฒ) + (a_0 b_1 + a_1 b_0)i + // + // In BLS12-381's F_{p^2}, our ฮฒ is -1, so the resulting F_{p^2} element is: + // + // c_0 = a_0 b_0 - a_1 b_1 + // c_1 = a_0 b_1 + a_1 b_0 + // + // Each of these is a "sum of products", which we can compute efficiently. + + Fp2 { + c0: Fp::sum_of_products([self.c0, -self.c1], [rhs.c0, rhs.c1]), + c1: Fp::sum_of_products([self.c0, self.c1], [rhs.c1, rhs.c0]), + } + } + + pub const fn add(&self, rhs: &Fp2) -> Fp2 { + Fp2 { + c0: (&self.c0).add(&rhs.c0), + c1: (&self.c1).add(&rhs.c1), + } + } + + pub const fn sub(&self, rhs: &Fp2) -> Fp2 { + Fp2 { + c0: (&self.c0).sub(&rhs.c0), + c1: (&self.c1).sub(&rhs.c1), + } + } + + pub const fn neg(&self) -> Fp2 { + Fp2 { + c0: (&self.c0).neg(), + c1: (&self.c1).neg(), + } + } + + pub fn sqrt(&self) -> CtOption { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + // with constant time modifications. + + CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { + // a1 = self^((p - 3) / 4) + let a1 = self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]); + + // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) + let alpha = a1.square() * self; + + // x0 = self^((p + 1) / 4) + let x0 = a1 * self; + + // In the event that alpha = -1, the element is order p - 1 and so + // we're just trying to get the square of an element of the subfield + // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element + // x0 = a + bu has b = 0, the solution is therefore au. + CtOption::new( + Fp2 { + c0: -x0.c1, + c1: x0.c0, + }, + alpha.ct_eq(&(&Fp2::one()).neg()), + ) + // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 + .or_else(|| { + CtOption::new( + (alpha + Fp2::one()).pow_vartime(&[ + 0xdcff_7fff_ffff_d555, + 0x0f55_ffff_58a9_ffff, + 0xb398_6950_7b58_7b12, + 0xb23b_a5c2_79c2_895f, + 0x258d_d3db_21a5_d66b, + 0x0d00_88f5_1cbf_f34d, + ]) * x0, + Choice::from(1), + ) + }) + // Only return the result if it's really the square root (and so + // self is actually quadratic nonresidue) + .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self))) + }) + } + + /// Computes the multiplicative inverse of this field + /// element, returning None in the case that this element + /// is zero. + pub fn invert(&self) -> CtOption { + // We wish to find the multiplicative inverse of a nonzero + // element a + bu in Fp2. We leverage an identity + // + // (a + bu)(a - bu) = a^2 + b^2 + // + // which holds because u^2 = -1. This can be rewritten as + // + // (a + bu)(a - bu)/(a^2 + b^2) = 1 + // + // because a^2 + b^2 = 0 has no nonzero solutions for (a, b). + // This gives that (a - bu)/(a^2 + b^2) is the inverse + // of (a + bu). Importantly, this can be computing using + // only a single inversion in Fp. + + (self.c0.square() + self.c1.square()).invert().map(|t| Fp2 { + c0: self.c0 * t, + c1: self.c1 * -t, + }) + } + + /// Although this is labeled "vartime", it is only + /// variable time with respect to the exponent. It + /// is also not exposed in the public API. + pub fn pow_vartime(&self, by: &[u64]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } + + /// Vartime exponentiation for larger exponents, only + /// used in testing and not exposed through the public API. + pub fn pow_vartime_extended(&self, by: &[u64]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into a `Fp2`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; 96]) -> CtOption { + let c0 = Fp::from_bytes(bytes[0..48].try_into().unwrap()); + let c1 = Fp::from_bytes(bytes[48..96].try_into().unwrap()); + CtOption::new( + Fp2 { + c0: c0.unwrap(), + c1: c1.unwrap(), + }, + c0.is_some() & c1.is_some(), + ) + } + + pub fn to_bytes(&self) -> [u8; 96] { + let mut res = [0u8; 96]; + let c0_bytes = self.c0.to_bytes(); + let c1_bytes = self.c1.to_bytes(); + res[0..48].copy_from_slice(&c0_bytes[..]); + res[48..96].copy_from_slice(&c1_bytes[..]); + res + } +} + +crate::impl_sum_prod!(Fp2); + +impl ff::Field for Fp2 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + + fn random(mut rng: impl RngCore) -> Self { + Fp2 { + c0: Fp::random(&mut rng), + c1: Fp::random(&mut rng), + } + } + + fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + fn square(&self) -> Self { + self.square() + } + + fn double(&self) -> Self { + self.double() + } + + fn sqrt(&self) -> CtOption { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + // with constant time modifications. + + CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { + // a1 = self^((p - 3) / 4) + let a1 = self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]); + + // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) + let alpha = a1.square() * self; + + // x0 = self^((p + 1) / 4) + let x0 = a1 * self; + + // In the event that alpha = -1, the element is order p - 1 and so + // we're just trying to get the square of an element of the subfield + // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element + // x0 = a + bu has b = 0, the solution is therefore au. + CtOption::new( + Fp2 { + c0: -x0.c1, + c1: x0.c0, + }, + alpha.ct_eq(&(Fp2::one()).neg()), + ) + // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 + .or_else(|| { + CtOption::new( + (alpha + Fp2::one()).pow_vartime(&[ + 0xdcff_7fff_ffff_d555, + 0x0f55_ffff_58a9_ffff, + 0xb398_6950_7b58_7b12, + 0xb23b_a5c2_79c2_895f, + 0x258d_d3db_21a5_d66b, + 0x0d00_88f5_1cbf_f34d, + ]) * x0, + Choice::from(1), + ) + }) + // Only return the result if it's really the square root (and so + // self is actually quadratic nonresidue) + .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self))) + }) + } + + fn invert(&self) -> CtOption { + self.invert() + } + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + // 1. c1, the largest integer such that 2^c1 divides q - 1. + const C1: usize = 3; + // 2. c2 = (q - 1) / (2^c1) + // const C2: &[u64] = &[ + // 7265783942635991495, + // 12654461171626608085, + // 7117242603539670943, + // 3231317604283856616, + // 7288461048012747785, + // 2570724377909988239, + // 16043555967369944544, + // 726422729336273229, + // 10150185336703275386, + // 4349230516827719894, + // 16823846345382421693, + // 23792283108797432, + // ]; + // 3. c3 = (c2 - 1) / 2 + const C3: &[u64] = &[ + 12856264008172771555, + 15550602622668079850, + 3558621301769835471, + 10839030838996704116, + 12867602560861149700, + 1285362188954994119, + 17245150020539748080, + 363211364668136614, + 5075092668351637693, + 11397987295268635755, + 8411923172691210846, + 11896141554398716, + ]; + // 4. c4 = 2^c1 - 1 + const C4: &[u64] = &[7]; + // 5. c5 = 2^(c1 - 1) + const C5: &[u64] = &[4]; + // 6. c6 = Z^c2 + const C6: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 8921533702591418330, + 15859389534032789116, + 3389114680249073393, + 15116930867080254631, + 3288288975085550621, + 1021049300055853010, + ]), + c1: Fp::from_raw_unchecked([ + 8921533702591418330, + 15859389534032789116, + 3389114680249073393, + 15116930867080254631, + 3288288975085550621, + 1021049300055853010, + ]), + }; + // 7. c7 = Z^((c2 + 1) / 2) + const C7: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 1921729236329761493, + 9193968980645934504, + 9862280504246317678, + 6861748847800817560, + 10375788487011937166, + 4460107375738415, + ]), + c1: Fp::from_raw_unchecked([ + 16821121318233475459, + 10183025025229892778, + 1779012082459463630, + 3442292649700377418, + 1061500799026501234, + 1352426537312017168, + ]), + }; + let mut tv1 = C6; // 1. tv1 = c6 + let tv2 = div.pow_vartime(C4); // 2. tv2 = v^c4 + let tv3 = tv2.square(); // 3. tv3 = tv2^2 + let tv3 = tv3 * div; // 4. tv3 = tv3 * v + let tv5 = num * tv3; // 5. tv5 = u * tv3 + let tv5 = tv5.pow_vartime(C3); // 6. tv5 = tv5^c3 + let tv5 = tv5 * tv2; // 7. tv5 = tv5 * tv2 + let tv2 = tv5 * div; // 8. tv2 = tv5 * v + let mut tv3 = tv5 * num; // 9. tv3 = tv5 * u + let mut tv4 = tv3 * tv2; // 10. tv4 = tv3 * tv2 + let tv5 = tv4.pow_vartime(C5); // 11. tv5 = tv4^c5 + let is_square = tv5.ct_eq(&Fp2::one()); // 12. isQR = tv5 == 1 + let tv2 = tv3 * C7; // 13. tv2 = tv3 * c7 + let tv5 = tv4 * tv1; // 14. tv5 = tv4 * tv1 + tv3.conditional_assign(&tv2, !is_square); // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.conditional_assign(&tv5, !is_square); // 16. tv4 = CMOV(tv5, tv4, isQR) + // 17. for i in (c1, c1 - 1, ..., 2): + for i in (2..=C1).rev() { + let tv5 = i as u32 - 2; // 18. tv5 = i - 2 + let tv5 = num_bigint::BigUint::from(2u64).pow(tv5); // 19. tv5 = 2^tv5 + let tv5 = tv4.pow_vartime(&tv5.to_u64_digits()); // 20. tv5 = tv4^tv5 + let e1 = tv5.ct_eq(&Fp2::one()); // 21. e1 = tv5 == 1 + let tv2 = tv3 * tv1; // 22. tv2 = tv3 * tv1 + tv1 = tv1 * tv1; // 23. tv1 = tv1 * tv1 + let tv5 = tv4 * tv1; // 24. tv5 = tv4 * tv1 + tv3.conditional_assign(&tv2, !e1); // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.conditional_assign(&tv5, !e1); // 26. tv4 = CMOV(tv5, tv4, e1) + } + (is_square, tv3) + } +} + +#[derive(Clone, Copy, Debug)] +pub struct Fp2Bytes { + pub slice: [u8; 96], +} + +impl Default for Fp2Bytes { + fn default() -> Self { + Self { slice: [0u8; 96] } + } +} + +impl AsMut<[u8]> for Fp2Bytes { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.slice + } +} + +impl AsRef<[u8]> for Fp2Bytes { + fn as_ref(&self) -> &[u8] { + &self.slice + } +} + +// While Fp2 is not a prime field we must implement as consequence of `CurveExt::Base` requiring `WithSmallOrderMulGroup`. +impl ff::PrimeField for Fp2 { + type Repr = Fp2Bytes; + + const NUM_BITS: u32 = 381; + const CAPACITY: u32 = 381 - 1; + const MODULUS: &'static str = + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"; + const MULTIPLICATIVE_GENERATOR: Self = unimplemented!(); + const ROOT_OF_UNITY: Self = Self { + c0: Fp::from_raw_unchecked([ + 0x7bcf_a7a2_5aa3_0fda, + 0xdc17_dec1_2a92_7e7c, + 0x2f08_8dd8_6b4e_bef1, + 0xd1ca_2087_da74_d4a7, + 0x2da2_5966_96ce_bc1d, + 0x0e2b_7eed_bbfd_87d2, + ]), + c1: Fp::from_raw_unchecked([ + 0x7bcf_a7a2_5aa3_0fda, + 0xdc17_dec1_2a92_7e7c, + 0x2f08_8dd8_6b4e_bef1, + 0xd1ca_2087_da74_d4a7, + 0x2da2_5966_96ce_bc1d, + 0x0e2b_7eed_bbfd_87d2, + ]), + }; + const ROOT_OF_UNITY_INV: Self = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3e2f_585d_a55c_9ad1, + 0x4294_213d_86c1_8183, + 0x3828_44c8_8b62_3732, + 0x92ad_2afd_1910_3e18, + 0x1d79_4e4f_ac7c_f0b9, + 0x0bd5_92fc_7d82_5ec8, + ]), + c1: Fp::from_raw_unchecked([ + 0x7bcf_a7a2_5aa3_0fda, + 0xdc17_dec1_2a92_7e7c, + 0x2f08_8dd8_6b4e_bef1, + 0xd1ca_2087_da74_d4a7, + 0x2da2_5966_96ce_bc1d, + 0x0e2b_7eed_bbfd_87d2, + ]), + }; + const DELTA: Self = Fp2 { + c0: Fp::zero(), + c1: Fp::zero(), + }; + const TWO_INV: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1804_0000_0001_5554, + 0x8550_0005_3ab0_0001, + 0x633c_b57c_253c_276f, + 0x6e22_d1ec_31eb_b502, + 0xd391_6126_f2d1_4ca2, + 0x17fb_b857_1a00_6596, + ]), + c1: Fp::zero(), + }; + const S: u32 = 0; + + fn from_repr(r: Self::Repr) -> CtOption { + // This uses little endian and so, assumes the array passed in is + // in that format. + Self::from_bytes(&r.slice) + } + + fn to_repr(&self) -> Self::Repr { + Fp2Bytes { + slice: self.to_bytes(), + } + } + + fn is_odd(&self) -> Choice { + Choice::from(self.to_bytes()[0] & 1) + } +} + +impl From for Fp2 { + fn from(val: u64) -> Self { + Fp2 { + c0: Fp::from(val), + c1: Fp::zero(), + } + } +} + +// The only reason we implement this trait for Fp2 is so it can be used as base field of `CurveExt`` trait for `G2Affine`. +// See: https://github.com/zcash/pasta_curves/blob/main/src/arithmetic/curves.rs#L32 +impl WithSmallOrderMulGroup<3> for Fp2 { + // Fq::ZETA ^2 + const ZETA: Self = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x30f1_361b_798a_64e8, + 0xf3b8_ddab_7ece_5a2a, + 0x16a8_ca3a_c615_77f7, + 0xc26a_2ff8_74fd_029b, + 0x3636_b766_6070_1c6e, + 0x051b_a4ab_241b_6160, + ]), + c1: Fp::zero(), + }; +} + +#[test] +fn test_conditional_selection() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]), + c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]), + }; + + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_equality() { + fn is_equal(a: &Fp2, b: &Fp2) -> bool { + let eq = a == b; + let ct_eq = a.ct_eq(b); + + assert_eq!(eq, bool::from(ct_eq)); + + eq + } + + assert!(is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); + + assert!(!is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); + + assert!(!is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); +} + +#[test] +fn test_squaring() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + + assert_eq!(a.square(), b); +} + +#[test] +fn test_multiplication() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf597_483e_27b4_e0f7, + 0x610f_badf_811d_ae5f, + 0x8432_af91_7714_327a, + 0x6a9a_9603_cf88_f09e, + 0xf05a_7bf8_bad0_eb01, + 0x0954_9131_c003_ffae, + ]), + c1: Fp::from_raw_unchecked([ + 0x963b_02d0_f93d_37cd, + 0xc95c_e1cd_b30a_73d4, + 0x3087_25fa_3126_f9b8, + 0x56da_3c16_7fab_0d50, + 0x6b50_86b5_f4b6_d6af, + 0x09c3_9f06_2f18_e9f2, + ]), + }; + + assert_eq!(a * b, c); +} + +#[test] +fn test_addition() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6b82_a9a7_08c1_32d2, + 0x476b_1da3_39ba_5ba4, + 0x848c_0e62_4b91_cd87, + 0x11f9_5955_295a_99ec, + 0xf337_6fce_2255_9f06, + 0x0c3f_e3fa_ce8c_8f43, + ]), + c1: Fp::from_raw_unchecked([ + 0x6f99_2c12_73ab_5bc5, + 0x3355_1366_17a1_df33, + 0x8b0e_f74c_0aed_aff9, + 0x062f_9246_8ad2_ca12, + 0xe146_9770_738f_d584, + 0x12c3_c3dd_84bc_a26d, + ]), + }; + + assert_eq!(a + b, c); +} + +#[test] +fn test_subtraction() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe1c0_86bb_bf1b_5981, + 0x4faf_c3a9_aa70_5d7e, + 0x2734_b5c1_0bb7_e726, + 0xb2bd_7776_af03_7a3e, + 0x1b89_5fb3_98a8_4164, + 0x1730_4aef_6f11_3cec, + ]), + c1: Fp::from_raw_unchecked([ + 0x74c3_1c79_9519_1204, + 0x3271_aa54_79fd_ad2b, + 0xc9b4_7157_4915_a30f, + 0x65e4_0313_ec44_b8be, + 0x7487_b238_5b70_67cb, + 0x0952_3b26_d0ad_19a4, + ]), + }; + + assert_eq!(a - b, c); +} + +#[test] +fn test_negation() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf05c_e7ce_9c11_39d7, + 0x6274_8f57_97e8_a36d, + 0xc4e8_d9df_c664_96df, + 0xb457_88e1_8118_9209, + 0x6949_13d0_8772_930d, + 0x1549_836a_3770_f3cf, + ]), + c1: Fp::from_raw_unchecked([ + 0x24d0_5bb9_fb9d_491c, + 0xfb1e_a120_c12e_39d0, + 0x7067_879f_c807_c7b1, + 0x60a9_269a_31bb_dab6, + 0x45c2_56bc_fd71_649b, + 0x18f6_9b5d_2b8a_fbde, + ]), + }; + + assert_eq!(-a, b); +} + +#[test] +fn test_sqrt() { + // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x2bee_d146_27d7_f9e9, + 0xb661_4e06_660e_5dce, + 0x06c4_cc7c_2f91_d42c, + 0x996d_7847_4b7a_63cc, + 0xebae_bc4c_820d_574e, + 0x1886_5e12_d93f_d845, + ]), + c1: Fp::from_raw_unchecked([ + 0x7d82_8664_baf4_f566, + 0xd17e_6639_96ec_7339, + 0x679e_ad55_cb40_78d0, + 0xfe3b_2260_e001_ec28, + 0x3059_93d0_43d9_1b68, + 0x0626_f03c_0489_b72d, + ]), + }; + + assert_eq!(a.sqrt().unwrap().square(), a); + + // b = 5, which is a generator of the p - 1 order + // multiplicative subgroup + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6631_0000_0010_5545, + 0x2114_0040_0eec_000d, + 0x3fa7_af30_c820_e316, + 0xc52a_8b8d_6387_695d, + 0x9fb4_e61d_1e83_eac5, + 0x005c_b922_afe8_4dc7, + ]), + c1: Fp::zero(), + }; + + assert_eq!(b.sqrt().unwrap().square(), b); + + // c = 25, which is a generator of the (p - 1) / 2 order + // multiplicative subgroup + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x44f6_0000_0051_ffae, + 0x86b8_0141_9948_0043, + 0xd715_9952_f1f3_794a, + 0x755d_6e3d_fe1f_fc12, + 0xd36c_d6db_5547_e905, + 0x02f8_c8ec_bf18_67bb, + ]), + c1: Fp::zero(), + }; + + assert_eq!(c.sqrt().unwrap().square(), c); + + // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 + // is nonsquare. + assert!(bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc5fa_1bc8_fd00_d7f6, + 0x3830_ca45_4606_003b, + 0x2b28_7f11_04b1_02da, + 0xa7fb_30f2_8230_f23e, + 0x339c_db9e_e953_dbf0, + 0x0d78_ec51_d989_fc57, + ]), + c1: Fp::from_raw_unchecked([ + 0x27ec_4898_cf87_f613, + 0x9de1_394e_1abb_05a5, + 0x0947_f85d_c170_fc14, + 0x586f_bc69_6b61_14b7, + 0x2b34_75a4_077d_7169, + 0x13e1_c895_cc4b_6c22, + ]) + } + .sqrt() + .is_none() + )); +} + +#[test] +fn test_inversion() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::from_raw_unchecked([ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + ]), + }; + + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0581_a133_3d4f_48a6, + 0x5824_2f6e_f074_8500, + 0x0292_c955_349e_6da5, + 0xba37_721d_dd95_fcd0, + 0x70d1_6790_3aa5_dfc5, + 0x1189_5e11_8b58_a9d5, + ]), + c1: Fp::from_raw_unchecked([ + 0x0eda_09d2_d7a8_5d17, + 0x8808_e137_a7d1_a2cf, + 0x43ae_2625_c1ff_21db, + 0xf85a_c9fd_f7a7_4c64, + 0x8fcc_dda5_b8da_9738, + 0x08e8_4f0c_b32c_d17d, + ]), + }; + + assert_eq!(a.invert().unwrap(), b); + + assert!(bool::from(Fp2::zero().invert().is_none())); +} + +#[test] +fn test_lexicographic_largest() { + assert!(!bool::from(Fp2::zero().lexicographically_largest())); + assert!(!bool::from(Fp2::one().lexicographically_largest())); + assert!(bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::from_raw_unchecked([ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + ]), + } + .lexicographically_largest() + )); + assert!(!bool::from( + Fp2 { + c0: -Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: -Fp::from_raw_unchecked([ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + ]), + } + .lexicographically_largest() + )); + assert!(!bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::zero(), + } + .lexicographically_largest() + )); + assert!(bool::from( + Fp2 { + c0: -Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::zero(), + } + .lexicographically_largest() + )); +} + +#[cfg(feature = "zeroize")] +#[test] +fn test_zeroize() { + use zeroize::Zeroize; + + let mut a = Fp2::one(); + a.zeroize(); + assert!(bool::from(a.is_zero())); +} + +#[test] +fn test_root_of_unity_inv() { + use ff::PrimeField; + assert_eq!(Fp2::ROOT_OF_UNITY * Fp2::ROOT_OF_UNITY_INV, Fp2::ONE) +} diff --git a/src/bls12_381/fp6.rs b/src/bls12_381/fp6.rs new file mode 100644 index 00000000..e78606a5 --- /dev/null +++ b/src/bls12_381/fp6.rs @@ -0,0 +1,585 @@ +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use super::fp::*; +use super::fp2::*; + +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use rand_core::RngCore; + +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; + +/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2}[v] / (v^3 - u - 1)$. +pub struct Fp6 { + pub c0: Fp2, + pub c1: Fp2, + pub c2: Fp2, +} + +impl From for Fp6 { + fn from(f: Fp) -> Fp6 { + Fp6 { + c0: Fp2::from(f), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } +} + +impl From for Fp6 { + fn from(f: Fp2) -> Fp6 { + Fp6 { + c0: f, + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } +} + +impl Eq for Fp6 {} +impl PartialEq for Fp6 { + fn eq(&self, other: &Fp6) -> bool { + self.ct_eq(other).into() + } +} + +impl Copy for Fp6 {} +impl Clone for Fp6 { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Default for Fp6 { + fn default() -> Self { + Fp6::zero() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for Fp6 {} + +impl fmt::Debug for Fp6 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2) + } +} + +impl ConditionallySelectable for Fp6 { + #[inline(always)] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp6 { + c0: Fp2::conditional_select(&a.c0, &b.c0, choice), + c1: Fp2::conditional_select(&a.c1, &b.c1, choice), + c2: Fp2::conditional_select(&a.c2, &b.c2, choice), + } + } +} + +impl ConstantTimeEq for Fp6 { + #[inline(always)] + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2) + } +} + +impl Fp6 { + #[inline] + pub const fn zero() -> Self { + Fp6 { + c0: Fp2::zero(), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } + + #[inline] + pub const fn one() -> Self { + Fp6 { + c0: Fp2::one(), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } + + pub(crate) fn random(mut rng: impl RngCore) -> Self { + Fp6 { + c0: Fp2::random(&mut rng), + c1: Fp2::random(&mut rng), + c2: Fp2::random(&mut rng), + } + } + + pub fn double(&self) -> Self { + Self { + c0: self.c0.double(), + c1: self.c1.double(), + c2: self.c2.double(), + } + } + + pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 { + Fp6 { + c0: (self.c2 * c1).mul_by_nonresidue(), + c1: self.c0 * c1, + c2: self.c1 * c1, + } + } + + pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 { + let a_a = self.c0 * c0; + let b_b = self.c1 * c1; + + let t1 = (self.c2 * c1).mul_by_nonresidue() + a_a; + + let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b; + + let t3 = self.c2 * c0 + b_b; + + Fp6 { + c0: t1, + c1: t2, + c2: t3, + } + } + + /// Multiply by quadratic nonresidue v. + pub fn mul_by_nonresidue(&self) -> Self { + // Given a + bv + cv^2, this produces + // av + bv^2 + cv^3 + // but because v^3 = u + 1, we have + // c(u + 1) + av + v^2 + + Fp6 { + c0: self.c2.mul_by_nonresidue(), + c1: self.c0, + c2: self.c1, + } + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + let c0 = self.c0.frobenius_map(); + let c1 = self.c1.frobenius_map(); + let c2 = self.c2.frobenius_map(); + + // c1 = c1 * (u + 1)^((p - 1) / 3) + let c1 = c1 + * Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + }; + + // c2 = c2 * (u + 1)^((2p - 2) / 3) + let c2 = c2 + * Fp2 { + c0: Fp::from_raw_unchecked([ + 0x890d_c9e4_8675_45c3, + 0x2af3_2253_3285_a5d5, + 0x5088_0866_309b_7e2c, + 0xa20d_1b8c_7e88_1024, + 0x14e4_f04f_e2db_9068, + 0x14e5_6d3f_1564_853a, + ]), + c1: Fp::zero(), + }; + + Fp6 { c0, c1, c2 } + } + + #[inline(always)] + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero() + } + + /// Returns `c = self * b`. + /// + /// Implements the full-tower interleaving strategy from + /// [ePrint 2022-376](https://eprint.iacr.org/2022/367). + #[inline] + fn mul_interleaved(&self, b: &Self) -> Self { + // The intuition for this algorithm is that we can look at F_p^6 as a direct + // extension of F_p^2, and express the overall operations down to the base field + // F_p instead of only over F_p^2. This enables us to interleave multiplications + // and reductions, ensuring that we don't require double-width intermediate + // representations (with around twice as many limbs as F_p elements). + + // We want to express the multiplication c = a x b, where a = (a_0, a_1, a_2) is + // an element of F_p^6, and a_i = (a_i,0, a_i,1) is an element of F_p^2. The fully + // expanded multiplication is given by (2022-376 ยง5): + // + // c_0,0 = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1 + // - a_1,0 b_2,1 - a_1,1 b_2,0 - a_2,0 b_1,1 - a_2,1 b_1,0. + // = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 (b_2,0 - b_2,1) - a_1,1 (b_2,0 + b_2,1) + // + a_2,0 (b_1,0 - b_1,1) - a_2,1 (b_1,0 + b_1,1). + // + // c_0,1 = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0 b_2,1 + a_1,1 b_2,0 + a_2,0 b_1,1 + a_2,1 b_1,0 + // + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1. + // = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0(b_2,0 + b_2,1) + a_1,1(b_2,0 - b_2,1) + // + a_2,0(b_1,0 + b_1,1) + a_2,1(b_1,0 - b_1,1). + // + // c_1,0 = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0 b_2,0 - a_2,1 b_2,1 + // - a_2,0 b_2,1 - a_2,1 b_2,0. + // = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0(b_2,0 - b_2,1) + // - a_2,1(b_2,0 + b_2,1). + // + // c_1,1 = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0 b_2,1 + a_2,1 b_2,0 + // + a_2,0 b_2,0 - a_2,1 b_2,1 + // = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0(b_2,0 + b_2,1) + // + a_2,1(b_2,0 - b_2,1). + // + // c_2,0 = a_0,0 b_2,0 - a_0,1 b_2,1 + a_1,0 b_1,0 - a_1,1 b_1,1 + a_2,0 b_0,0 - a_2,1 b_0,1. + // c_2,1 = a_0,0 b_2,1 + a_0,1 b_2,0 + a_1,0 b_1,1 + a_1,1 b_1,0 + a_2,0 b_0,1 + a_2,1 b_0,0. + // + // Each of these is a "sum of products", which we can compute efficiently. + + let a = self; + let b10_p_b11 = b.c1.c0 + b.c1.c1; + let b10_m_b11 = b.c1.c0 - b.c1.c1; + let b20_p_b21 = b.c2.c0 + b.c2.c1; + let b20_m_b21 = b.c2.c0 - b.c2.c1; + + Fp6 { + c0: Fp2 { + c0: Fp::sum_of_products( + [a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1], + [b.c0.c0, b.c0.c1, b20_m_b21, b20_p_b21, b10_m_b11, b10_p_b11], + ), + c1: Fp::sum_of_products( + [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1], + [b.c0.c1, b.c0.c0, b20_p_b21, b20_m_b21, b10_p_b11, b10_m_b11], + ), + }, + c1: Fp2 { + c0: Fp::sum_of_products( + [a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1], + [b.c1.c0, b.c1.c1, b.c0.c0, b.c0.c1, b20_m_b21, b20_p_b21], + ), + c1: Fp::sum_of_products( + [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1], + [b.c1.c1, b.c1.c0, b.c0.c1, b.c0.c0, b20_p_b21, b20_m_b21], + ), + }, + c2: Fp2 { + c0: Fp::sum_of_products( + [a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1], + [b.c2.c0, b.c2.c1, b.c1.c0, b.c1.c1, b.c0.c0, b.c0.c1], + ), + c1: Fp::sum_of_products( + [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1], + [b.c2.c1, b.c2.c0, b.c1.c1, b.c1.c0, b.c0.c1, b.c0.c0], + ), + }, + } + } + + #[inline] + pub fn square(&self) -> Self { + let s0 = self.c0.square(); + let ab = self.c0 * self.c1; + let s1 = ab + ab; + let s2 = (self.c0 - self.c1 + self.c2).square(); + let bc = self.c1 * self.c2; + let s3 = bc + bc; + let s4 = self.c2.square(); + + Fp6 { + c0: s3.mul_by_nonresidue() + s0, + c1: s4.mul_by_nonresidue() + s1, + c2: s1 + s2 + s3 - s0 - s4, + } + } + + #[inline] + pub fn invert(&self) -> CtOption { + let c0 = (self.c1 * self.c2).mul_by_nonresidue(); + let c0 = self.c0.square() - c0; + + let c1 = self.c2.square().mul_by_nonresidue(); + let c1 = c1 - (self.c0 * self.c1); + + let c2 = self.c1.square(); + let c2 = c2 - (self.c0 * self.c2); + + let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue(); + let tmp = tmp + (self.c0 * c0); + + tmp.invert().map(|t| Fp6 { + c0: t * c0, + c1: t * c1, + c2: t * c2, + }) + } +} + +impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn mul(self, other: &'b Fp6) -> Self::Output { + self.mul_interleaved(other) + } +} + +impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn add(self, rhs: &'b Fp6) -> Self::Output { + Fp6 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + c2: self.c2 + rhs.c2, + } + } +} + +impl<'a> Neg for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn neg(self) -> Self::Output { + Fp6 { + c0: -self.c0, + c1: -self.c1, + c2: -self.c2, + } + } +} + +impl Neg for Fp6 { + type Output = Fp6; + + #[inline] + fn neg(self) -> Self::Output { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn sub(self, rhs: &'b Fp6) -> Self::Output { + Fp6 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + c2: self.c2 - rhs.c2, + } + } +} + +impl_binops_additive!(Fp6, Fp6); +impl_binops_multiplicative!(Fp6, Fp6); + +#[test] +fn test_arithmetic() { + use super::fp::*; + + let a = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }; + + let b = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf120_cb98_b16f_d84b, + 0x5fb5_10cf_f3de_1d61, + 0x0f21_a5d0_69d8_c251, + 0xaa1f_d62f_34f2_839a, + 0x5a13_3515_7f89_913f, + 0x14a3_fe32_9643_c247, + ]), + c1: Fp::from_raw_unchecked([ + 0x3516_cb98_b16c_82f9, + 0x926d_10c2_e126_1d5f, + 0x1709_e01a_0cc2_5fba, + 0x96c8_c960_b825_3f14, + 0x4927_c234_207e_51a9, + 0x18ae_b158_d542_c44e, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xbf0d_cb98_b169_82fc, + 0xa679_10b7_1d1a_1d5c, + 0xb7c1_47c2_b8fb_06ff, + 0x1efa_710d_47d2_e7ce, + 0xed20_a79c_7e27_653c, + 0x02b8_5294_dac1_dfba, + ]), + c1: Fp::from_raw_unchecked([ + 0x9d52_cb98_b180_82e5, + 0x621d_1111_5176_1d6f, + 0xe798_8260_3b48_af43, + 0x0ad3_1637_a4f4_da37, + 0xaeac_737c_5ac1_cf2e, + 0x006e_7e73_5b48_b824, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe148_cb98_b17d_2d93, + 0x94d5_1104_3ebe_1d6c, + 0xef80_bca9_de32_4cac, + 0xf77c_0969_2827_95b1, + 0x9dc1_009a_fbb6_8f97, + 0x0479_3199_9a47_ba2b, + ]), + c1: Fp::from_raw_unchecked([ + 0x253e_cb98_b179_d841, + 0xc78d_10f7_2c06_1d6a, + 0xf768_f6f3_811b_ea15, + 0xe424_fc9a_ab5a_512b, + 0x8cd5_8db9_9cab_5001, + 0x0883_e4bf_d946_bc32, + ]), + }, + }; + + let c = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6934_cb98_b176_82ef, + 0xfa45_10ea_194e_1d67, + 0xff51_313d_2405_877e, + 0xd0cd_efcc_2e8d_0ca5, + 0x7bea_1ad8_3da0_106b, + 0x0c8e_97e6_1845_be39, + ]), + c1: Fp::from_raw_unchecked([ + 0x4779_cb98_b18d_82d8, + 0xb5e9_1144_4daa_1d7a, + 0x2f28_6bda_a653_2fc2, + 0xbca6_94f6_8bae_ff0f, + 0x3d75_e6b8_1a3a_7a5d, + 0x0a44_c3c4_98cc_96a3, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8b6f_cb98_b18a_2d86, + 0xe8a1_1137_3af2_1d77, + 0x3710_a624_493c_cd2b, + 0xa94f_8828_0ee1_ba89, + 0x2c8a_73d6_bb2f_3ac7, + 0x0e4f_76ea_d7cb_98aa, + ]), + c1: Fp::from_raw_unchecked([ + 0xcf65_cb98_b186_d834, + 0x1b59_112a_283a_1d74, + 0x3ef8_e06d_ec26_6a95, + 0x95f8_7b59_9214_7603, + 0x1b9f_00f5_5c23_fb31, + 0x125a_2a11_16ca_9ab1, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x135b_cb98_b183_82e2, + 0x4e11_111d_1582_1d72, + 0x46e1_1ab7_8f10_07fe, + 0x82a1_6e8b_1547_317d, + 0x0ab3_8e13_fd18_bb9b, + 0x1664_dd37_55c9_9cb8, + ]), + c1: Fp::from_raw_unchecked([ + 0xce65_cb98_b131_8334, + 0xc759_0fdb_7c3a_1d2e, + 0x6fcb_8164_9d1c_8eb3, + 0x0d44_004d_1727_356a, + 0x3746_b738_a7d0_d296, + 0x136c_144a_96b1_34fc, + ]), + }, + }; + + assert_eq!(a.square(), a * a); + assert_eq!(b.square(), b * b); + assert_eq!(c.square(), c * c); + + assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b)); + + assert_eq!( + a.invert().unwrap() * b.invert().unwrap(), + (a * b).invert().unwrap() + ); + assert_eq!(a.invert().unwrap() * a, Fp6::one()); +} + +#[cfg(feature = "zeroize")] +#[test] +fn test_zeroize() { + use zeroize::Zeroize; + + let mut a = Fp6::one(); + a.zeroize(); + assert!(bool::from(a.is_zero())); +} diff --git a/src/bls12_381/g1.rs b/src/bls12_381/g1.rs new file mode 100644 index 00000000..5c23c439 --- /dev/null +++ b/src/bls12_381/g1.rs @@ -0,0 +1,2024 @@ +//! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use core::borrow::Borrow; +use core::fmt; +use core::iter::Sum; +use core::ops::{Add, Mul, Neg, Sub}; +use group::{ + ff::Field, + prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, + Curve, Group, GroupEncoding, UncompressedEncoding, +}; +use pasta_curves::arithmetic::{Coordinates, CurveAffine, CurveExt}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use super::fp::Fp; +use super::hash_to_curve::{ExpandMsgXmd, HashToCurve}; +use super::Scalar; +use crate::CurveAffineExt; +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; + +/// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space. +/// It is ideal to keep elements in this representation to reduce memory usage and +/// improve performance through the use of mixed curve model arithmetic. +/// +/// Values of `G1Affine` are guaranteed to be in the $q$-order subgroup unless an +/// "unchecked" API was misused. +#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] +#[derive(Copy, Clone, Debug)] +pub struct G1Affine { + pub x: Fp, + pub y: Fp, + infinity: Choice, +} + +impl Default for G1Affine { + fn default() -> G1Affine { + G1Affine::identity() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes 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()); + let x = p.x * zinv; + let y = p.y * zinv; + + let tmp = G1Affine { + x, + y, + infinity: Choice::from(0u8), + }; + + G1Affine::conditional_select(&tmp, &G1Affine::identity(), zinv.is_zero()) + } +} + +impl From for G1Affine { + fn from(p: G1Projective) -> G1Affine { + G1Affine::from(&p) + } +} + +impl ConstantTimeEq for G1Affine { + fn ct_eq(&self, other: &Self) -> Choice { + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal + + (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 Add for G1Affine { + type Output = G1Projective; + fn add(self, rhs: G1Affine) -> Self::Output { + self.to_curve() + rhs.to_curve() + } +} + +impl Sub for G1Affine { + type Output = G1Projective; + fn sub(self, rhs: G1Affine) -> Self::Output { + 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 Sum for G1Projective +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + 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([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, +]); + +impl G1Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Affine { + G1Affine { + x: Fp::zero(), + y: Fp::one(), + infinity: Choice::from(1u8), + } + } + + pub fn random(mut rng: impl RngCore) -> Self { + loop { + let x = Fp::random(&mut rng); + let ysign = (rng.next_u32() % 2) as u8; + + let x3 = x.square() * x; + let y = (x3 + B).sqrt(); + if let Some(y) = Option::::from(y) { + let sign = y.to_bytes()[0] & 1; + let y = if ysign ^ sign == 0 { y } else { -y }; + + let p = G1Affine { + x, + y, + infinity: 0.into(), + }; + + let p = p.to_curve(); + return p.clear_cofactor().to_affine(); + } + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/design/index.html) + /// for how this generator is chosen. + pub fn generator() -> G1Affine { + G1Affine { + x: Fp::from_raw_unchecked([ + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, + ]), + y: Fp::from_raw_unchecked([ + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, + ]), + infinity: Choice::from(0u8), + } + } + + /// Serializes this element into compressed form. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + pub fn to_compressed_be(&self) -> [u8; 48] { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + let mut res = Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes_be(); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select( + &0u8, + &(1u8 << 5), + (!self.infinity) & self.y.lexicographically_largest(), + ); + + res + } + + /// Serializes this element into compressed form in little-endian. + pub fn to_compressed_le(&self) -> [u8; 48] { + let mut bytes = self.to_compressed_be(); + bytes.reverse(); + bytes + } + + /// Serializes this element into uncompressed form. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + /// + /// NOTE: this function used in [`UncompressedEncoding::to_uncompressed`]. + pub fn to_uncompressed_be(&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_be()[..], + ); + res[48..96].copy_from_slice( + &Fp::conditional_select(&self.y, &Fp::zero(), self.infinity).to_bytes_be()[..], + ); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + res + } + + /// Serializes this element into uncompressed form in little-endian. + pub fn to_uncompressed_le(&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()[..], + ); + + // Is this point at infinity? If so, set the second-most significant bit. + res[47] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + res + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + pub fn from_uncompressed_be(bytes: &[u8; 96]) -> CtOption { + Self::from_uncompressed_unchecked_be(bytes) + .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is on the curve and not checking if it is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_uncompressed_unchecked_be(bytes: &[u8; 96]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + 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); + + // Attempt to obtain the x-coordinate + let x = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes_be(&tmp) + }; + + // Attempt to obtain the y-coordinate + let y = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes_be(&tmp) + }; + + x.and_then(|x| { + y.and_then(|y| { + // Create a point representing this value + let p = G1Affine::conditional_select( + &G1Affine { + x, + y, + infinity: infinity_flag_set, + }, + &G1Affine::identity(), + infinity_flag_set, + ); + + CtOption::new( + p, + // If the infinity flag is set, the x and y coordinates should have been zero. + ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & + // The compression flag should not have been set, as this is an uncompressed element + (!compression_flag_set) & + // The sort flag should not have been set, as this is an uncompressed element + (!sort_flag_set), + ) + }) + }) + } + + /// Attempts to deserialize a compressed element from big-endian bytes. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + /// + /// NOTE: this function used in [`CompressedEncoding::from_compressed`]. + pub fn from_compressed_be(bytes: &[u8; 48]) -> CtOption { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). + + Self::from_compressed_unchecked_be(bytes) + .and_then(|p| CtOption::new(p, p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element from big-endian bytes, not checking if the + /// element is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_compressed()` instead. + pub fn from_compressed_unchecked_be(bytes: &[u8; 48]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + 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); + + // Attempt to obtain the x-coordinate + let x = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes_be(&tmp) + }; + + x.and_then(|x| { + // If the infinity flag is set, return the value assuming + // the x-coordinate is zero and the sort bit is not set. + // + // Otherwise, return a recovered point (assuming the correct + // y-coordinate can be found) so long as the infinity flag + // was not set. + CtOption::new( + G1Affine::identity(), + infinity_flag_set & // Infinity flag should be set + compression_flag_set & // Compression flag should be set + (!sort_flag_set) & // Sort flag should not be set + x.is_zero(), // The x-coordinate should be zero + ) + .or_else(|| { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + ((x.square() * x) + B).sqrt().and_then(|y| { + // Switch to the correct y-coordinate if necessary. + 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) & // Infinity flag should not be set + compression_flag_set, // Compression flag should be set + ) + }) + }) + }) + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + pub fn from_uncompressed_le(bytes: &[u8; 96]) -> CtOption { + Self::from_uncompressed_unchecked_le(bytes) + .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is on the curve and not checking if it is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_uncompressed_unchecked_le(bytes: &[u8; 96]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let infinity_flag_set = Choice::from((bytes[47] >> 6) & 1); + + // Attempt to obtain the x-coordinate + let x = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + Fp::from_bytes(&tmp) + }; + + // Attempt to obtain the y-coordinate + 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| { + // Create a point representing this value + let p = G1Affine::conditional_select( + &G1Affine { + x, + y, + infinity: infinity_flag_set, + }, + &G1Affine::identity(), + infinity_flag_set, + ); + + CtOption::new( + p, + // If the infinity flag is set, the x and y coordinates should have been zero. + (!infinity_flag_set) | (x.is_zero() & y.is_zero()), + ) + }) + }) + } + + /// Attempts to deserialize an uncompressed element from little-endian bytes, not checking if the + /// element is in the correct subgroup. + pub fn from_compressed_unchecked_le(bytes: &[u8; 48]) -> CtOption { + let mut bytes = *bytes; + bytes.reverse(); + Self::from_compressed_unchecked_be(&bytes) + } + + /// Attempts to deserialize a compressed element from little-endian bytes. + pub fn from_compressed_le(bytes: &[u8; 48]) -> CtOption { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). + + Self::from_compressed_unchecked_le(bytes) + .and_then(|p| CtOption::new(p, p.is_torsion_free())) + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.infinity + } + + /// Returns true if this point is free of an $h$-torsion component, and so it + /// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true + /// unless an "unchecked" API was used. + pub fn is_torsion_free(&self) -> Choice { + // Algorithm from Section 6 of https://eprint.iacr.org/2021/1130 + // Updated proof of correctness in https://eprint.iacr.org/2022/352 + // + // Check that endomorphism_p(P) == -[x^2] P + + let minus_x_squared_times_p = G1Projective::from(self).mul_by_x().mul_by_x().neg(); + let endomorphism_p = endomorphism(self); + minus_x_squared_times_p.ct_eq(&G1Projective::from(endomorphism_p)) + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // y^2 - x^3 ?= 4 + (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity + } +} + +/// A nontrivial third root of unity in Fp +pub const BETA: Fp = Fp::from_raw_unchecked([ + 0x30f1_361b_798a_64e8, + 0xf3b8_ddab_7ece_5a2a, + 0x16a8_ca3a_c615_77f7, + 0xc26a_2ff8_74fd_029b, + 0x3636_b766_6070_1c6e, + 0x051b_a4ab_241b_6160, +]); + +fn endomorphism(p: &G1Affine) -> G1Affine { + // Endomorphism of the points on the curve. + // endomorphism_p(x,y) = (BETA * x, y) + // where BETA is a non-trivial cubic root of unity in Fq. + let mut res = *p; + res.x *= BETA; + res +} + +impl CurveAffine for G1Affine { + /// The scalar field of this elliptic curve. + type ScalarExt = super::Scalar; + /// The base field over which this elliptic curve is constructed. + type Base = super::fp::Fp; + /// The projective form of the curve + type CurveExt = G1Projective; + + /// Gets the coordinates of this point. + /// + /// Returns None if this is the identity. + fn coordinates(&self) -> CtOption> { + Coordinates::from_xy(self.x, self.y) + } + + /// Obtains a point given $(x, y)$, failing if it is not on the + /// curve. + fn from_xy(x: Self::Base, y: Self::Base) -> CtOption { + let p: G1Affine = Self { + x, + y, + infinity: x.ct_eq(&Self::Base::ZERO) & y.ct_eq(&Self::Base::ZERO), + }; + CtOption::new(p, p.is_on_curve()) + } + + /// Returns whether or not this element is on the curve; should + /// always be true unless an "unchecked" API was used. + fn is_on_curve(&self) -> Choice { + self.is_on_curve() + } + + /// Returns the curve constant $a$. + fn a() -> Self::Base { + Self::Base::ZERO + } + + /// Returns the curve constant $b$. + fn b() -> Self::Base { + B + } +} + +impl CurveAffineExt for G1Affine { + fn into_coordinates(self) -> (Self::Base, Self::Base) { + (self.x, self.y) + } +} + +/// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space. +#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] +#[derive(Copy, Clone, Debug)] +pub struct G1Projective { + pub(crate) x: Fp, + pub(crate) y: Fp, + pub(crate) z: Fp, +} + +impl Default for G1Projective { + fn default() -> G1Projective { + G1Projective::identity() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for G1Projective {} + +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 { + x: p.x, + y: p.y, + z: Fp::conditional_select(&Fp::one(), &Fp::zero(), p.infinity), + } + } +} + +impl From for G1Projective { + fn from(p: G1Affine) -> G1Projective { + G1Projective::from(&p) + } +} + +impl ConstantTimeEq for G1Projective { + fn ct_eq(&self, other: &Self) -> Choice { + // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? + + let x1 = self.x * other.z; + let x2 = other.x * self.z; + + let y1 = self.y * other.z; + let y2 = other.y * self.z; + + let self_is_zero = self.z.is_zero(); + let other_is_zero = other.z.is_zero(); + + (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) + // Neither point at infinity, coordinates are the same + } +} + +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 G1Projective> for &'a Scalar { + type Output = G1Projective; + + #[inline] + fn mul(self, rhs: &'b G1Projective) -> Self::Output { + rhs * self + } +} + +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<'a, 'b> Mul<&'b G1Affine> for &'a Scalar { + type Output = G1Projective; + + #[inline] + fn mul(self, rhs: &'b G1Affine) -> Self::Output { + rhs * self + } +} + +impl CurveExt for G1Projective { + type ScalarExt = Scalar; + type Base = Fp; + type AffineExt = G1Affine; + + const CURVE_ID: &'static str = "Bls12-381"; + + fn endo(&self) -> Self { + endomorphism(&G1Affine::from(self)).into() + } + + fn jacobian_coordinates(&self) -> (Fp, Fp, Fp) { + // Homogenous to Jacobian + let x = self.x * self.z; + let y = self.y * self.z.square(); + (x, y, self.z) + } + + fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box Self + 'a> { + Box::new(|msg| { + >>::hash_to_curve( + msg, + domain_prefix.as_bytes(), + ) + }) + } + + fn is_on_curve(&self) -> Choice { + // Check (Y/Z)^2 = (X/Z)^3 + b + // <=> Z Y^2 - X^3 = Z^3 b + + (self.z * self.y.square() - self.x.square() * self.x) + .ct_eq(&(self.z.square() * self.z * G1Affine::b())) + | self.z.is_zero() + } + + fn b() -> Self::Base { + B + } + + fn a() -> Self::Base { + Self::Base::ZERO + } + + fn new_jacobian(x: Self::Base, y: Self::Base, z: Self::Base) -> CtOption { + // Jacobian to homogenous + let z_inv = z.invert().unwrap_or(Fp::zero()); + let p_x = x * z_inv; + let p_y = y * z_inv.square(); + let p = Self { + x: p_x, + y: Fp::conditional_select(&p_y, &Fp::one(), z.is_zero()), + z, + }; + CtOption::new(p, p.is_on_curve()) + } +} + +impl_binops_additive!(G1Projective, G1Projective); +impl_binops_multiplicative!(G1Projective, Scalar); +impl_binops_multiplicative_mixed!(G1Affine, Scalar, G1Projective); +impl_binops_multiplicative_mixed!(Scalar, G1Affine, G1Projective); +impl_binops_multiplicative_mixed!(Scalar, G1Projective, G1Projective); + +#[inline(always)] +fn mul_by_3b(a: Fp) -> Fp { + let a = a + a; // 2 + let a = a + a; // 4 + a + a + a // 12 +} + +impl G1Projective { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Projective { + G1Projective { + x: Fp::zero(), + y: Fp::one(), + z: Fp::zero(), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/design/index.html) + /// for how this generator is chosen. + pub fn generator() -> G1Projective { + G1Projective { + x: Fp::from_raw_unchecked([ + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, + ]), + y: Fp::from_raw_unchecked([ + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, + ]), + z: Fp::one(), + } + } + + /// Computes the doubling of this point. + pub fn double(&self) -> G1Projective { + // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.y.square(); + let z3 = t0 + t0; + let z3 = z3 + z3; + let z3 = z3 + z3; + let t1 = self.y * self.z; + let t2 = self.z.square(); + let t2 = mul_by_3b(t2); + let x3 = t2 * z3; + let y3 = t0 + t2; + let z3 = t1 * z3; + let t1 = t2 + t2; + let t2 = t1 + t2; + let t0 = t0 - t2; + let y3 = t0 * y3; + let y3 = x3 + y3; + let t1 = self.x * self.y; + let x3 = t0 * t1; + let x3 = x3 + x3; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + G1Projective::conditional_select(&tmp, &G1Projective::identity(), self.is_identity()) + } + + /// Adds this point to another point. + pub fn add(&self, rhs: &G1Projective) -> G1Projective { + // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t2 = self.z * rhs.z; + let t3 = self.x + self.y; + let t4 = rhs.x + rhs.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = self.y + self.z; + let x3 = rhs.y + rhs.z; + let t4 = t4 * x3; + let x3 = t1 + t2; + let t4 = t4 - x3; + let x3 = self.x + self.z; + let y3 = rhs.x + rhs.z; + let x3 = x3 * y3; + let y3 = t0 + t2; + let y3 = x3 - y3; + let x3 = t0 + t0; + let t0 = x3 + t0; + let t2 = mul_by_3b(t2); + let z3 = t1 + t2; + let t1 = t1 - t2; + let y3 = mul_by_3b(y3); + let x3 = t4 * y3; + let t2 = t3 * t1; + let x3 = t2 - x3; + let y3 = y3 * t0; + let t1 = t1 * z3; + let y3 = t1 + y3; + let t0 = t0 * t3; + let z3 = z3 * t4; + let z3 = z3 + t0; + + G1Projective { + x: x3, + y: y3, + z: z3, + } + } + + /// Adds this point to another point in the affine model. + pub fn add_mixed(&self, rhs: &G1Affine) -> G1Projective { + // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t3 = rhs.x + rhs.y; + let t4 = self.x + self.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = rhs.y * self.z; + let t4 = t4 + self.y; + let y3 = rhs.x * self.z; + let y3 = y3 + self.x; + let x3 = t0 + t0; + let t0 = x3 + t0; + let t2 = mul_by_3b(self.z); + let z3 = t1 + t2; + let t1 = t1 - t2; + let y3 = mul_by_3b(y3); + let x3 = t4 * y3; + let t2 = t3 * t1; + let x3 = t2 - x3; + let y3 = y3 * t0; + let t1 = t1 * z3; + let y3 = t1 + y3; + let t0 = t0 * t3; + let z3 = z3 * t4; + let z3 = z3 + t0; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + G1Projective::conditional_select(&tmp, self, rhs.is_identity()) + } + + fn multiply(&self, by: &[u8; 32]) -> G1Projective { + let mut acc = G1Projective::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + 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 + } + + /// Multiply `self` by `crate::BLS_X`, using double and add. + fn mul_by_x(&self) -> G1Projective { + let mut xself = G1Projective::identity(); + // NOTE: in BLS12-381 we can just skip the first bit. + let mut x = super::BLS_X >> 1; + let mut tmp = *self; + while x != 0 { + tmp = tmp.double(); + + if x % 2 == 1 { + xself += tmp; + } + x >>= 1; + } + // finally, flip the sign + if super::BLS_X_IS_NEGATIVE { + xself = -xself; + } + xself + } + + /// Multiplies by $(1 - z)$, where $z$ is the parameter of BLS12-381, which + /// [suffices to clear](https://ia.cr/2019/403) the cofactor and map + /// elliptic curve points to elements of $\mathbb{G}\_1$. + pub fn clear_cofactor(&self) -> G1Projective { + self - self.mul_by_x() + } + + /// Converts a batch of `G1Projective` elements into `G1Affine` elements. This + /// function will panic if `p.len() != q.len()`. + 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()) { + // We use the `x` field of `G1Affine` to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity()); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); + + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = Fp::conditional_select(&(acc * p.z), &acc, skip); + + // Set the coordinates to the correct value + q.x = p.x * tmp; + q.y = p.y * tmp; + q.infinity = Choice::from(0u8); + + *q = G1Affine::conditional_select(q, &G1Affine::identity(), skip); + } + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.z.is_zero() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // Y^2 Z = X^3 + b Z^3 + + (self.y.square() * self.z).ct_eq(&(self.x.square() * self.x + self.z.square() * self.z * B)) + | self.z.is_zero() + } +} + +#[derive(Clone, Copy)] +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]) + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for G1Compressed {} + +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 + } +} + +impl ConstantTimeEq for G1Compressed { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } +} + +impl Eq for G1Compressed {} +impl PartialEq for G1Compressed { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl TryFrom<&[u8]> for G1Compressed { + type Error = std::array::TryFromSliceError; + + fn try_from(value: &[u8]) -> Result { + Ok(G1Compressed(value.try_into()?)) + } +} + +#[derive(Clone, Copy)] +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]) + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for G1Uncompressed {} + +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 ConstantTimeEq for G1Uncompressed { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } +} + +impl Eq for G1Uncompressed {} +impl PartialEq for G1Uncompressed { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl TryFrom<&[u8]> for G1Uncompressed { + type Error = std::array::TryFromSliceError; + + fn try_from(value: &[u8]) -> Result { + Ok(G1Uncompressed(value.try_into()?)) + } +} + +impl Group for G1Projective { + type Scalar = Scalar; + + fn random(mut rng: impl RngCore) -> Self { + loop { + let x = Fp::random(&mut 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 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 { + G1Affine::from_bytes(bytes).map(Self::from) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + 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::from_compressed_le(&bytes.0) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + Self::from_compressed_unchecked_le(&bytes.0) + } + + fn to_bytes(&self) -> Self::Repr { + G1Compressed(self.to_compressed_le()) + } +} + +/// Allows to de/encode G1 points from uncompressed bytes in **little endian**. +impl UncompressedEncoding for G1Affine { + type Uncompressed = G1Uncompressed; + + fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed_le(&bytes.0) + } + + fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed_unchecked_le(&bytes.0) + } + + fn to_uncompressed(&self) -> Self::Uncompressed { + G1Uncompressed(self.to_uncompressed_le()) + } +} + +#[test] +fn test_beta() { + assert_eq!( + BETA, + Fp::from_bytes_be(&[ + 0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, 0xdf, 0x76, + 0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, 0xdd, 0xb3, 0xa9, 0x3b, + 0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, 0x62, 0x0a, 0x00, 0x02, 0x2e, 0x01, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe + ]) + .unwrap() + ); + assert_ne!(BETA, Fp::one()); + assert_ne!(BETA * BETA, Fp::one()); + assert_eq!(BETA * BETA * BETA, Fp::one()); +} +#[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([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let gen = G1Affine::generator(); + let mut test = G1Projective { + x: gen.x * z, + y: gen.y * z, + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} + +#[test] +#[allow(clippy::eq_op)] +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] +#[allow(clippy::eq_op)] +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([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let mut c = G1Projective { + x: a.x * z, + y: a.y * 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([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let c = G1Projective { + x: a.x * z, + y: a.y * 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([ + 0x53e9_78ce_58a9_ba3c, + 0x3ea0_583c_4f3d_65f9, + 0x4d20_bb47_f001_2960, + 0xa54c_664a_e5b2_b5d9, + 0x26b5_52a3_9d7e_b21f, + 0x0008_895d_26e6_8785, + ]), + y: Fp::from_raw_unchecked([ + 0x7011_0b32_9829_3940, + 0xda33_c539_3f1f_6afc, + 0xb86e_dfd1_6a5a_a785, + 0xaec6_d1c9_e7b1_c895, + 0x25cf_c2b5_22d1_1720, + 0x0636_1c83_f8d0_9b15, + ]), + 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([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * z, + y: b.y * 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([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * z, + y: b.y * 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(); // 4P + let b = G1Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G1Projective::generator(); + for _ in 0..5 { + 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); + } + + // Degenerate case + { + let beta = Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]); + 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([ + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6, + ]), + y: Fp::from_raw_unchecked([ + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + ]), + 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([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * z, + y: b.y * 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([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * z, + y: b.y * 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(); // 4P + let b = G1Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G1Projective::generator(); + for _ in 0..5 { + 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); + } + + // Degenerate case + { + let beta = Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]); + 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([ + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6, + ]), + y: Fp::from_raw_unchecked([ + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + ]), + z: Fp::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +#[allow(clippy::eq_op)] +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([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = Scalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + 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([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = Scalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + 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([ + 0x0aba_f895_b97e_43c8, + 0xba4c_6432_eb9b_61b0, + 0x1250_6f52_adfe_307f, + 0x7502_8c34_3933_6b72, + 0x8474_4f05_b8e9_bd71, + 0x113d_554f_b095_54f7, + ]), + y: Fp::from_raw_unchecked([ + 0x73e9_0e88_f5cf_01c0, + 0x3700_7b65_dd31_97e2, + 0x5cf9_a199_2f0d_7c78, + 0x4f83_c10b_9eb3_330d, + 0xf6a6_3f6f_07f6_0961, + 0x0c53_b5b9_7e63_4df3, + ]), + 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() { + // multiplying by `x` a point in G1 is the same as multiplying by + // the equivalent scalar. + let generator = G1Projective::generator(); + let x = if super::BLS_X_IS_NEGATIVE { + -Scalar::from(super::BLS_X) + } else { + Scalar::from(super::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() { + // the generator (and the identity) are always on the curve, + // even after clearing the 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 z = Fp::from_raw_unchecked([ + 0x3d2d1c670671394e, + 0x0ee3a800a2f7c1ca, + 0x270f4f21da2e5050, + 0xe02840a53f1be768, + 0x55debeb597512690, + 0x08bd25353dc8f791, + ]); + + let point = G1Projective { + x: Fp::from_raw_unchecked([ + 0x48af5ff540c817f0, + 0xd73893acaf379d5a, + 0xe6c43584e18e023c, + 0x1eda39c30f188b3e, + 0xf618c6d3ccc0f8d8, + 0x0073542cd671e16c, + ]) * z, + y: Fp::from_raw_unchecked([ + 0x57bf8be79461d0ba, + 0xfc61459cee3547c3, + 0x0d23567df1ef147b, + 0x0ee187bcce1d9b64, + 0xb0c8cfbe9dc8fdc1, + 0x1328661767ef368b, + ]), + z: z.square() * z, + }; + + 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())); + + // in BLS12-381 the cofactor in G1 can be + // cleared multiplying by (1-x) + let h_eff = Scalar::from(1) + Scalar::from(super::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[..]); + } + } + } +} + +#[cfg(feature = "zeroize")] +#[test] +fn test_zeroize() { + use zeroize::Zeroize; + + let mut a = G1Affine::generator(); + a.zeroize(); + assert!(bool::from(a.is_identity())); + + let mut a = G1Projective::generator(); + a.zeroize(); + assert!(bool::from(a.is_identity())); + + let mut a = GroupEncoding::to_bytes(&G1Affine::generator()); + a.zeroize(); + assert_eq!(&a, &G1Compressed::default()); + + let mut a = UncompressedEncoding::to_uncompressed(&G1Affine::generator()); + a.zeroize(); + assert_eq!(&a, &G1Uncompressed::default()); +} + +#[allow(clippy::op_ref)] +#[test] +fn test_commutative_scalar_subgroup_multiplication() { + let a = Scalar::from_raw([ + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562, + ]); + + let g1_a = G1Affine::generator(); + let g1_p = G1Projective::generator(); + + // By reference. + assert_eq!(&g1_a * &a, &a * &g1_a); + assert_eq!(&g1_p * &a, &a * &g1_p); + + // Mixed + assert_eq!(&g1_a * a, a * &g1_a); + assert_eq!(&g1_p * a, a * &g1_p); + assert_eq!(g1_a * &a, &a * g1_a); + assert_eq!(g1_p * &a, &a * g1_p); + + // By value. + assert_eq!(g1_p * a, a * g1_p); + assert_eq!(g1_a * a, a * g1_a); +} diff --git a/src/bls12_381/g2.rs b/src/bls12_381/g2.rs new file mode 100644 index 00000000..dd6b3551 --- /dev/null +++ b/src/bls12_381/g2.rs @@ -0,0 +1,2466 @@ +//! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use core::borrow::Borrow; +use core::fmt; +use core::iter::Sum; +use core::ops::{Add, Mul, Neg, Sub}; +use ff::Field; +use group::{ + prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, + Curve, Group, GroupEncoding, UncompressedEncoding, +}; +use pasta_curves::arithmetic::{Coordinates, CurveAffine, CurveExt}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::CurveAffineExt; +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; + +use super::fp::Fp; +use super::fp2::Fp2; +use super::hash_to_curve::{ExpandMsgXmd, HashToCurve}; +use super::Scalar; + +#[cfg(feature = "alloc")] +use group::WnafGroup; + +/// This is an element of $\mathbb{G}_2$ represented in the affine coordinate space. +/// It is ideal to keep elements in this representation to reduce memory usage and +/// improve performance through the use of mixed curve model arithmetic. +/// +/// Values of `G2Affine` are guaranteed to be in the $q$-order subgroup unless an +/// "unchecked" API was misused. +#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] +#[derive(Copy, Clone, Debug)] +pub struct G2Affine { + pub x: Fp2, + pub y: Fp2, + infinity: Choice, +} + +impl Default for G2Affine { + fn default() -> G2Affine { + G2Affine::identity() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes 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()); + let x = p.x * zinv; + let y = p.y * zinv; + + let tmp = G2Affine { + x, + y, + infinity: Choice::from(0u8), + }; + + G2Affine::conditional_select(&tmp, &G2Affine::identity(), zinv.is_zero()) + } +} + +impl From for G2Affine { + fn from(p: G2Projective) -> G2Affine { + G2Affine::from(&p) + } +} + +impl ConstantTimeEq for G2Affine { + fn ct_eq(&self, other: &Self) -> Choice { + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal + + (self.infinity & other.infinity) + | ((!self.infinity) + & (!other.infinity) + & self.x.ct_eq(&other.x) + & self.y.ct_eq(&other.y)) + } +} + +impl ConditionallySelectable for G2Affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G2Affine { + x: Fp2::conditional_select(&a.x, &b.x, choice), + y: Fp2::conditional_select(&a.y, &b.y, choice), + infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), + } + } +} + +impl Eq for G2Affine {} +impl PartialEq for G2Affine { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G2Affine { + type Output = G2Affine; + + #[inline] + fn neg(self) -> G2Affine { + G2Affine { + x: self.x, + y: Fp2::conditional_select(&-self.y, &Fp2::one(), self.infinity), + infinity: self.infinity, + } + } +} + +impl Neg for G2Affine { + type Output = G2Affine; + + #[inline] + fn neg(self) -> G2Affine { + -&self + } +} + +impl<'a, 'b> Add<&'b G2Projective> for &'a G2Affine { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Projective) -> G2Projective { + rhs.add_mixed(self) + } +} + +impl<'a, 'b> Add<&'b G2Affine> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Affine) -> G2Projective { + self.add_mixed(rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Affine { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Projective) -> G2Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Affine) -> G2Projective { + self + (-rhs) + } +} + +impl Add for G2Affine { + type Output = G2Projective; + fn add(self, rhs: G2Affine) -> Self::Output { + self.to_curve() + rhs.to_curve() + } +} + +impl Sub for G2Affine { + type Output = G2Projective; + fn sub(self, rhs: G2Affine) -> Self::Output { + self + -rhs + } +} + +impl Sum for G2Projective +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + +impl_binops_additive!(G2Projective, G2Affine); +impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); + +const B: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, + ]), + c1: Fp::from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, + ]), +}; + +const B3: Fp2 = Fp2::add(&Fp2::add(&B, &B), &B); + +impl G2Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G2Affine { + G2Affine { + x: Fp2::zero(), + y: Fp2::one(), + infinity: Choice::from(1u8), + } + } + + pub fn random(mut rng: impl RngCore) -> Self { + loop { + let x = Fp2::random(&mut rng); + let ysign = (rng.next_u32() % 2) as u8; + + let x3 = x.square() * x; + let y = (x3 + B).sqrt(); + if let Some(y) = Option::::from(y) { + let sign = y.to_bytes()[0] & 1; + let y = if ysign ^ sign == 0 { y } else { -y }; + + let p = G2Affine { + x, + y, + infinity: 0.into(), + }; + + let p = p.to_curve(); + return p.clear_cofactor().to_affine(); + } + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/design/index.html) + /// for how this generator is chosen. + pub fn generator() -> G2Affine { + G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7, + ]), + c1: Fp::from_raw_unchecked([ + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5, + ]), + c1: Fp::from_raw_unchecked([ + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2, + ]), + }, + infinity: Choice::from(0u8), + } + } + + /// Serializes this element into compressed form in big-endian. + /// See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) for details about how group elements are serialized. + pub fn to_compressed_be(&self) -> [u8; 96] { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); + + let mut res = [0; 96]; + + res[0..48].copy_from_slice(&x.c1.to_bytes_be()[..]); + res[48..96].copy_from_slice(&x.c0.to_bytes_be()[..]); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select( + &0u8, + &(1u8 << 5), + (!self.infinity) & self.y.lexicographically_largest(), + ); + + res + } + + /// Serializes this element into compressed form in little-endian. + pub fn to_compressed_le(&self) -> [u8; 96] { + let mut bytes = self.to_compressed_be(); + bytes.reverse(); + bytes + } + + /// Serializes this element into uncompressed form. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + pub fn to_uncompressed_be(&self) -> [u8; 192] { + let mut res = [0; 192]; + + let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); + let y = Fp2::conditional_select(&self.y, &Fp2::zero(), self.infinity); + + res[0..48].copy_from_slice(&x.c1.to_bytes_be()[..]); + res[48..96].copy_from_slice(&x.c0.to_bytes_be()[..]); + res[96..144].copy_from_slice(&y.c1.to_bytes_be()[..]); + res[144..192].copy_from_slice(&y.c0.to_bytes_be()[..]); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + res + } + + /// Serializes this element into uncompressed form in little-endian. + pub fn to_uncompressed_le(&self) -> [u8; 192] { + let mut res = [0; 192]; + + let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); + let y = Fp2::conditional_select(&self.y, &Fp2::zero(), self.infinity); + + res[0..48].copy_from_slice(&x.c1.to_bytes()[..]); + res[48..96].copy_from_slice(&x.c0.to_bytes()[..]); + res[96..144].copy_from_slice(&y.c1.to_bytes()[..]); + res[144..192].copy_from_slice(&y.c0.to_bytes()[..]); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + res + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + pub fn from_uncompressed_be(bytes: &[u8; 192]) -> CtOption { + Self::from_uncompressed_unchecked_be(bytes) + .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is on the curve and not checking if it is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_uncompressed_unchecked_be(bytes: &[u8; 192]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + 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); + + // Attempt to obtain the x-coordinate + let xc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes_be(&tmp) + }; + let xc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes_be(&tmp) + }; + + // Attempt to obtain the y-coordinate + let yc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[96..144]); + + Fp::from_bytes_be(&tmp) + }; + let yc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[144..192]); + + Fp::from_bytes_be(&tmp) + }; + + xc1.and_then(|xc1| { + xc0.and_then(|xc0| { + yc1.and_then(|yc1| { + yc0.and_then(|yc0| { + let x = Fp2 { + c0: xc0, + c1: xc1 + }; + let y = Fp2 { + c0: yc0, + c1: yc1 + }; + + // Create a point representing this value + let p = G2Affine::conditional_select( + &G2Affine { + x, + y, + infinity: infinity_flag_set, + }, + &G2Affine::identity(), + infinity_flag_set, + ); + + CtOption::new( + p, + // If the infinity flag is set, the x and y coordinates should have been zero. + ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & + // The compression flag should not have been set, as this is an uncompressed element + (!compression_flag_set) & + // The sort flag should not have been set, as this is an uncompressed element + (!sort_flag_set), + ) + }) + }) + }) + }) + } + + /// Attempts to deserialize a compressed element. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + pub fn from_compressed_be(bytes: &[u8; 96]) -> CtOption { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). + + Self::from_compressed_unchecked_be(bytes) + .and_then(|p| CtOption::new(p, p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_compressed()` instead. + pub fn from_compressed_unchecked_be(bytes: &[u8; 96]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + 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); + + // Attempt to obtain the x-coordinate + let xc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes_be(&tmp) + }; + let xc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes_be(&tmp) + }; + + xc1.and_then(|xc1| { + xc0.and_then(|xc0| { + let x = Fp2 { c0: xc0, c1: xc1 }; + + // If the infinity flag is set, return the value assuming + // the x-coordinate is zero and the sort bit is not set. + // + // Otherwise, return a recovered point (assuming the correct + // y-coordinate can be found) so long as the infinity flag + // was not set. + CtOption::new( + G2Affine::identity(), + infinity_flag_set & // Infinity flag should be set + compression_flag_set & // Compression flag should be set + (!sort_flag_set) & // Sort flag should not be set + x.is_zero(), // The x-coordinate should be zero + ) + .or_else(|| { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + ((x.square() * x) + B).sqrt().and_then(|y| { + // Switch to the correct y-coordinate if necessary. + let y = Fp2::conditional_select( + &y, + &-y, + y.lexicographically_largest() ^ sort_flag_set, + ); + + CtOption::new( + G2Affine { + x, + y, + infinity: infinity_flag_set, + }, + (!infinity_flag_set) & // Infinity flag should not be set + compression_flag_set, // Compression flag should be set + ) + }) + }) + }) + }) + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/serialization/index.html) + /// for details about how group elements are serialized. + pub fn from_uncompressed_le(bytes: &[u8; 192]) -> CtOption { + Self::from_uncompressed_unchecked_le(bytes) + .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is on the curve and not checking if it is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_uncompressed_unchecked_le(bytes: &[u8; 192]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + + // Attempt to obtain the x-coordinate + let xc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + Fp::from_bytes(&tmp) + }; + let xc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes(&tmp) + }; + + // Attempt to obtain the y-coordinate + let yc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[96..144]); + + Fp::from_bytes(&tmp) + }; + let yc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[144..192]); + + Fp::from_bytes(&tmp) + }; + + xc1.and_then(|xc1| { + xc0.and_then(|xc0| { + yc1.and_then(|yc1| { + yc0.and_then(|yc0| { + let x = Fp2 { c0: xc0, c1: xc1 }; + let y = Fp2 { c0: yc0, c1: yc1 }; + + // Create a point representing this value + let p = G2Affine::conditional_select( + &G2Affine { + x, + y, + infinity: infinity_flag_set, + }, + &G2Affine::identity(), + infinity_flag_set, + ); + + CtOption::new( + p, + // If the infinity flag is set, the x and y coordinates should have been zero. + (!infinity_flag_set) | (x.is_zero() & y.is_zero()), + ) + }) + }) + }) + }) + } + + /// Attempts to deserialize an uncompressed element from little-endian bytes, not checking if the + /// element is in the correct subgroup. + pub fn from_compressed_unchecked_le(bytes: &[u8; 96]) -> CtOption { + let mut bytes = *bytes; + bytes.reverse(); + Self::from_compressed_unchecked_be(&bytes) + } + + /// Attempts to deserialize a compressed element from little-endian bytes. + pub fn from_compressed_le(bytes: &[u8; 96]) -> CtOption { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). + + Self::from_compressed_unchecked_le(bytes) + .and_then(|p| CtOption::new(p, p.is_torsion_free())) + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.infinity + } + + /// Returns true if this point is free of an $h$-torsion component, and so it + /// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true + /// unless an "unchecked" API was used. + pub fn is_torsion_free(&self) -> Choice { + // Algorithm from Section 4 of https://eprint.iacr.org/2021/1130 + // Updated proof of correctness in https://eprint.iacr.org/2022/352 + // + // Check that psi(P) == [x] P + let p = G2Projective::from(self); + p.psi().ct_eq(&p.mul_by_x()) + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // y^2 - x^3 ?= 4(u + 1) + (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity + } +} + +/// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space. +#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] +#[derive(Copy, Clone, Debug)] +pub struct G2Projective { + pub(crate) x: Fp2, + pub(crate) y: Fp2, + pub(crate) z: Fp2, +} + +impl Default for G2Projective { + fn default() -> G2Projective { + G2Projective::identity() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for G2Projective {} + +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 { + x: p.x, + y: p.y, + z: Fp2::conditional_select(&Fp2::one(), &Fp2::zero(), p.infinity), + } + } +} + +impl From for G2Projective { + fn from(p: G2Affine) -> G2Projective { + G2Projective::from(&p) + } +} + +impl ConstantTimeEq for G2Projective { + fn ct_eq(&self, other: &Self) -> Choice { + // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? + + let x1 = self.x * other.z; + let x2 = other.x * self.z; + + let y1 = self.y * other.z; + let y2 = other.y * self.z; + + let self_is_zero = self.z.is_zero(); + let other_is_zero = other.z.is_zero(); + + (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) + // Neither point at infinity, coordinates are the same + } +} + +impl ConditionallySelectable for G2Projective { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G2Projective { + x: Fp2::conditional_select(&a.x, &b.x, choice), + y: Fp2::conditional_select(&a.y, &b.y, choice), + z: Fp2::conditional_select(&a.z, &b.z, choice), + } + } +} + +impl Eq for G2Projective {} +impl PartialEq for G2Projective { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn neg(self) -> G2Projective { + G2Projective { + x: self.x, + y: -self.y, + z: self.z, + } + } +} + +impl Neg for G2Projective { + type Output = G2Projective; + + #[inline] + fn neg(self) -> G2Projective { + -&self + } +} + +impl<'a, 'b> Add<&'b G2Projective> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Projective) -> G2Projective { + self.add(rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Projective) -> G2Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G2Projective { + type Output = G2Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + self.multiply(&other.to_bytes()) + } +} + +impl<'a, 'b> Mul<&'b G2Projective> for &'a Scalar { + type Output = G2Projective; + + #[inline] + fn mul(self, rhs: &'b G2Projective) -> Self::Output { + rhs * self + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G2Affine { + type Output = G2Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + G2Projective::from(self).multiply(&other.to_bytes()) + } +} + +impl<'a, 'b> Mul<&'b G2Affine> for &'a Scalar { + type Output = G2Projective; + + #[inline] + fn mul(self, rhs: &'b G2Affine) -> Self::Output { + rhs * self + } +} + +impl_binops_additive!(G2Projective, G2Projective); +impl_binops_multiplicative!(G2Projective, Scalar); +impl_binops_multiplicative_mixed!(G2Affine, Scalar, G2Projective); +impl_binops_multiplicative_mixed!(Scalar, G2Affine, G2Projective); +impl_binops_multiplicative_mixed!(Scalar, G2Projective, G2Projective); + +#[inline(always)] +fn mul_by_3b(x: Fp2) -> Fp2 { + x * B3 +} + +impl G2Projective { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G2Projective { + G2Projective { + x: Fp2::zero(), + y: Fp2::one(), + z: Fp2::zero(), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](https://docs.rs/bls12_381/0.8.0/bls12_381/notes/design/index.html) + /// for how this generator is chosen. + pub fn generator() -> G2Projective { + G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7, + ]), + c1: Fp::from_raw_unchecked([ + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5, + ]), + c1: Fp::from_raw_unchecked([ + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2, + ]), + }, + z: Fp2::one(), + } + } + + /// Computes the doubling of this point. + pub fn double(&self) -> G2Projective { + // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.y.square(); + let z3 = t0 + t0; + let z3 = z3 + z3; + let z3 = z3 + z3; + let t1 = self.y * self.z; + let t2 = self.z.square(); + let t2 = mul_by_3b(t2); + let x3 = t2 * z3; + let y3 = t0 + t2; + let z3 = t1 * z3; + let t1 = t2 + t2; + let t2 = t1 + t2; + let t0 = t0 - t2; + let y3 = t0 * y3; + let y3 = x3 + y3; + let t1 = self.x * self.y; + let x3 = t0 * t1; + let x3 = x3 + x3; + + let tmp = G2Projective { + x: x3, + y: y3, + z: z3, + }; + + G2Projective::conditional_select(&tmp, &G2Projective::identity(), self.is_identity()) + } + + /// Adds this point to another point. + pub fn add(&self, rhs: &G2Projective) -> G2Projective { + // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t2 = self.z * rhs.z; + let t3 = self.x + self.y; + let t4 = rhs.x + rhs.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = self.y + self.z; + let x3 = rhs.y + rhs.z; + let t4 = t4 * x3; + let x3 = t1 + t2; + let t4 = t4 - x3; + let x3 = self.x + self.z; + let y3 = rhs.x + rhs.z; + let x3 = x3 * y3; + let y3 = t0 + t2; + let y3 = x3 - y3; + let x3 = t0 + t0; + let t0 = x3 + t0; + let t2 = mul_by_3b(t2); + let z3 = t1 + t2; + let t1 = t1 - t2; + let y3 = mul_by_3b(y3); + let x3 = t4 * y3; + let t2 = t3 * t1; + let x3 = t2 - x3; + let y3 = y3 * t0; + let t1 = t1 * z3; + let y3 = t1 + y3; + let t0 = t0 * t3; + let z3 = z3 * t4; + let z3 = z3 + t0; + + G2Projective { + x: x3, + y: y3, + z: z3, + } + } + + /// Adds this point to another point in the affine model. + pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective { + // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t3 = rhs.x + rhs.y; + let t4 = self.x + self.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = rhs.y * self.z; + let t4 = t4 + self.y; + let y3 = rhs.x * self.z; + let y3 = y3 + self.x; + let x3 = t0 + t0; + let t0 = x3 + t0; + let t2 = mul_by_3b(self.z); + let z3 = t1 + t2; + let t1 = t1 - t2; + let y3 = mul_by_3b(y3); + let x3 = t4 * y3; + let t2 = t3 * t1; + let x3 = t2 - x3; + let y3 = y3 * t0; + let t1 = t1 * z3; + let y3 = t1 + y3; + let t0 = t0 * t3; + let z3 = z3 * t4; + let z3 = z3 + t0; + + let tmp = G2Projective { + x: x3, + y: y3, + z: z3, + }; + + G2Projective::conditional_select(&tmp, self, rhs.is_identity()) + } + + fn multiply(&self, by: &[u8]) -> G2Projective { + let mut acc = G2Projective::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + 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 = G2Projective::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + + fn psi(&self) -> G2Projective { + // 1 / ((u+1) ^ ((q-1)/3)) + let psi_coeff_x = Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ]), + }; + // 1 / ((u+1) ^ (p-1)/2) + let psi_coeff_y = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0x0bd592fc7d825ec8, + ]), + c1: Fp::from_raw_unchecked([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0x0e2b7eedbbfd87d2, + ]), + }; + + G2Projective { + // x = frobenius(x)/((u+1)^((p-1)/3)) + x: self.x.frobenius_map() * psi_coeff_x, + // y = frobenius(y)/(u+1)^((p-1)/2) + y: self.y.frobenius_map() * psi_coeff_y, + // z = frobenius(z) + z: self.z.frobenius_map(), + } + } + + fn psi2(&self) -> G2Projective { + // 1 / 2 ^ ((q-1)/3) + let psi2_coeff_x = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fp::zero(), + }; + + G2Projective { + // x = frobenius^2(x)/2^((p-1)/3); note that q^2 is the order of the field. + x: self.x * psi2_coeff_x, + // y = -frobenius^2(y); note that q^2 is the order of the field. + y: self.y.neg(), + // z = z + z: self.z, + } + } + + /// Multiply `self` by `super::BLS_X`, using double and add. + fn mul_by_x(&self) -> G2Projective { + let mut xself = G2Projective::identity(); + // NOTE: in BLS12-381 we can just skip the first bit. + let mut x = super::BLS_X >> 1; + let mut acc = *self; + while x != 0 { + acc = acc.double(); + if x % 2 == 1 { + xself += acc; + } + x >>= 1; + } + // finally, flip the sign + if super::BLS_X_IS_NEGATIVE { + xself = -xself; + } + xself + } + + /// Clears the cofactor, using [Budroni-Pintore](https://ia.cr/2017/419). + /// This is equivalent to multiplying by $h\_\textrm{eff} = 3(z^2 - 1) \cdot + /// h_2$, where $h_2$ is the cofactor of $\mathbb{G}\_2$ and $z$ is the + /// parameter of BLS12-381. + pub fn clear_cofactor(&self) -> G2Projective { + let t1 = self.mul_by_x(); // [x] P + let t2 = self.psi(); // psi(P) + + self.double().psi2() // psi^2(2P) + + (t1 + t2).mul_by_x() // psi^2(2P) + [x^2] P + [x] psi(P) + - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) + - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) + - self // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) + } + + /// Converts a batch of `G2Projective` elements into `G2Affine` elements. This + /// function will panic if `p.len() != q.len()`. + pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) { + assert_eq!(p.len(), q.len()); + + let mut acc = Fp2::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of `G2Affine` to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity()); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); + + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = Fp2::conditional_select(&(acc * p.z), &acc, skip); + + // Set the coordinates to the correct value + q.x = p.x * tmp; + q.y = p.y * tmp; + q.infinity = Choice::from(0u8); + + *q = G2Affine::conditional_select(q, &G2Affine::identity(), skip); + } + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.z.is_zero() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // Y^2 Z = X^3 + b Z^3 + + (self.y.square() * self.z).ct_eq(&(self.x.square() * self.x + self.z.square() * self.z * B)) + | self.z.is_zero() + } +} + +#[derive(Clone, Copy)] +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]) + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for G2Compressed {} + +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 + } +} + +impl ConstantTimeEq for G2Compressed { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } +} + +impl Eq for G2Compressed {} +impl PartialEq for G2Compressed { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl TryFrom<&[u8]> for G2Compressed { + type Error = std::array::TryFromSliceError; + + fn try_from(value: &[u8]) -> Result { + Ok(G2Compressed(value.try_into()?)) + } +} + +#[derive(Clone, Copy)] +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]) + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for G2Uncompressed {} + +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 ConstantTimeEq for G2Uncompressed { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } +} + +impl Eq for G2Uncompressed {} +impl PartialEq for G2Uncompressed { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl TryFrom<&[u8]> for G2Uncompressed { + type Error = std::array::TryFromSliceError; + + fn try_from(value: &[u8]) -> Result { + Ok(G2Uncompressed(value.try_into()?)) + } +} + +impl Group for G2Projective { + type Scalar = Scalar; + + fn random(mut rng: impl RngCore) -> Self { + loop { + let x = Fp2::random(&mut 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() + } +} + +#[cfg(feature = "alloc")] +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 { + G2Affine::from_bytes(bytes).map(Self::from) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + 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::from_compressed_le(&bytes.0) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + Self::from_compressed_unchecked_le(&bytes.0) + } + + fn to_bytes(&self) -> Self::Repr { + G2Compressed(self.to_compressed_le()) + } +} + +impl UncompressedEncoding for G2Affine { + type Uncompressed = G2Uncompressed; + + fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed_le(&bytes.0) + } + + fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed_unchecked_le(&bytes.0) + } + + fn to_uncompressed(&self) -> Self::Uncompressed { + G2Uncompressed(self.to_uncompressed_le()) + } +} + +impl CurveExt for G2Projective { + type ScalarExt = Scalar; + type Base = Fp2; + type AffineExt = G2Affine; + + const CURVE_ID: &'static str = "Bls12-381"; + + fn endo(&self) -> Self { + unimplemented!() + } + + fn jacobian_coordinates(&self) -> (Fp2, Fp2, Fp2) { + // Homogenous to Jacobian + let x = self.x * self.z; + let y = self.y * self.z.square(); + (x, y, self.z) + } + + fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box Self + 'a> { + Box::new(|msg| { + >>::hash_to_curve( + msg, + domain_prefix.as_bytes(), + ) + }) + } + + fn is_on_curve(&self) -> Choice { + // Check (Y/Z)^2 = (X/Z)^3 + b + // <=> Z Y^2 - X^3 = Z^3 b + + // (self.z * self.y.square() - self.x.square() * self.x) + // .ct_eq(&(self.z.square() * self.z * G2Affine::b())) + // | self.z.is_zero() + unimplemented!() + } + + fn b() -> Self::Base { + B + } + + fn a() -> Self::Base { + Self::Base::ZERO + } + + fn new_jacobian(x: Self::Base, y: Self::Base, z: Self::Base) -> CtOption { + // Jacobian to homogenous + let z_inv = z.invert().unwrap_or(Fp2::zero()); + let p_x = x * z_inv; + let p_y = y * z_inv.square(); + let p = Self { + x: p_x, + y: Fp2::conditional_select(&p_y, &Fp2::ONE, z.is_zero()), + z, + }; + CtOption::new(p, p.is_on_curve()) + } +} + +impl CurveAffine for G2Affine { + /// The scalar field of this elliptic curve. + type ScalarExt = super::Scalar; + /// The base field over which this elliptic curve is constructed. + type Base = super::fp2::Fp2; + /// The projective form of the curve + type CurveExt = G2Projective; + + /// Gets the coordinates of this point. + /// + /// Returns None if this is the identity. + fn coordinates(&self) -> CtOption> { + Coordinates::from_xy(self.x, self.y) + } + + /// Obtains a point given $(x, y)$, failing if it is not on the + /// curve. + fn from_xy(x: Self::Base, y: Self::Base) -> CtOption { + let p: G2Affine = Self { + x, + y, + infinity: x.ct_eq(&Self::Base::ZERO) & y.ct_eq(&Self::Base::ZERO), + }; + CtOption::new(p, p.is_on_curve()) + } + + /// Returns whether or not this element is on the curve; should + /// always be true unless an "unchecked" API was used. + fn is_on_curve(&self) -> Choice { + self.is_on_curve() + } + + /// Returns the curve constant $a$. + fn a() -> Self::Base { + Self::Base::ZERO + } + + /// Returns the curve constant $b$. + fn b() -> Self::Base { + B + } +} + +impl CurveAffineExt for G2Affine { + fn into_coordinates(self) -> (Self::Base, Self::Base) { + (self.x, self.y) + } +} + +#[test] +fn test_is_on_curve() { + assert!(bool::from(G2Affine::identity().is_on_curve())); + assert!(bool::from(G2Affine::generator().is_on_curve())); + assert!(bool::from(G2Projective::identity().is_on_curve())); + assert!(bool::from(G2Projective::generator().is_on_curve())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let gen = G2Affine::generator(); + let mut test = G2Projective { + x: gen.x * z, + y: gen.y * z, + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} + +#[test] +#[allow(clippy::eq_op)] +fn test_affine_point_equality() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); +} + +#[test] +#[allow(clippy::eq_op)] +fn test_projective_point_equality() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let mut c = G2Projective { + x: a.x * z, + y: a.y * 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 = G2Affine::generator(); + let b = G2Affine::identity(); + + assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(0u8)), a); + assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(1u8)), b); +} + +#[test] +fn test_conditionally_select_projective() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert_eq!( + G2Projective::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + G2Projective::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_projective_to_affine() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert!(bool::from(G2Affine::from(a).is_on_curve())); + assert!(!bool::from(G2Affine::from(a).is_identity())); + assert!(bool::from(G2Affine::from(b).is_on_curve())); + assert!(bool::from(G2Affine::from(b).is_identity())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let c = G2Projective { + x: a.x * z, + y: a.y * z, + z, + }; + + assert_eq!(G2Affine::from(c), G2Affine::generator()); +} + +#[test] +fn test_affine_to_projective() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); + + assert!(bool::from(G2Projective::from(a).is_on_curve())); + assert!(!bool::from(G2Projective::from(a).is_identity())); + assert!(bool::from(G2Projective::from(b).is_on_curve())); + assert!(bool::from(G2Projective::from(b).is_identity())); +} + +#[test] +fn test_doubling() { + { + let tmp = G2Projective::identity().double(); + assert!(bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + } + { + let tmp = G2Projective::generator().double(); + assert!(!bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + + assert_eq!( + G2Affine::from(tmp), + G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe9d9_e2da_9620_f98b, + 0x54f1_1993_46b9_7f36, + 0x3db3_b820_376b_ed27, + 0xcfdb_31c9_b0b6_4f4c, + 0x41d7_c127_8635_4493, + 0x0571_0794_c255_c064, + ]), + c1: Fp::from_raw_unchecked([ + 0xd6c1_d3ca_6ea0_d06e, + 0xda0c_bd90_5595_489f, + 0x4f53_52d4_3479_221d, + 0x8ade_5d73_6f8c_97e0, + 0x48cc_8433_925e_f70e, + 0x08d7_ea71_ea91_ef81, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x15ba_26eb_4b0d_186f, + 0x0d08_6d64_b7e9_e01e, + 0xc8b8_48dd_652f_4c78, + 0xeecf_46a6_123b_ae4f, + 0x255e_8dd8_b6dc_812a, + 0x1641_42af_21dc_f93f, + ]), + c1: Fp::from_raw_unchecked([ + 0xf9b4_a1a8_9598_4db4, + 0xd417_b114_cccf_f748, + 0x6856_301f_c89f_086e, + 0x41c7_7787_8931_e3da, + 0x3556_b155_066a_2105, + 0x00ac_f7d3_25cb_89cf, + ]), + }, + infinity: Choice::from(0u8) + } + ); + } +} + +#[test] +fn test_projective_addition() { + { + let a = G2Projective::identity(); + let b = G2Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G2Projective::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * z, + y: b.y * z, + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * z, + y: b.y * z, + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::generator().double().double(); // 4P + let b = G2Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G2Projective::generator(); + for _ in 0..5 { + d += G2Projective::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); + } + + // Degenerate case + { + let beta = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + c1: Fp::zero(), + }; + let beta = beta.square(); + let a = G2Projective::generator().double().double(); + let b = G2Projective { + 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!( + G2Affine::from(c), + G2Affine::from(G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2, + ]), + c1: Fp::from_raw_unchecked([ + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952, + ]) + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3, + ]), + c1: Fp::from_raw_unchecked([ + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe, + ]) + }, + z: Fp2::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +fn test_mixed_addition() { + { + let a = G2Affine::identity(); + let b = G2Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G2Affine::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * z, + y: b.y * z, + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Affine::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * z, + y: b.y * z, + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::generator().double().double(); // 4P + let b = G2Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G2Projective::generator(); + for _ in 0..5 { + d += G2Affine::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); + } + + // Degenerate case + { + let beta = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + c1: Fp::zero(), + }; + let beta = beta.square(); + let a = G2Projective::generator().double().double(); + let b = G2Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + let a = G2Affine::from(a); + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G2Affine::from(c), + G2Affine::from(G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2, + ]), + c1: Fp::from_raw_unchecked([ + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952, + ]) + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3, + ]), + c1: Fp::from_raw_unchecked([ + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe, + ]) + }, + z: Fp2::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +#[allow(clippy::eq_op)] +fn test_projective_negation_and_subtraction() { + let a = G2Projective::generator().double(); + assert_eq!(a + (-a), G2Projective::identity()); + assert_eq!(a + (-a), a - a); +} + +#[test] +fn test_affine_negation_and_subtraction() { + let a = G2Affine::generator(); + assert_eq!(G2Projective::from(a) + (-a), G2Projective::identity()); + assert_eq!(G2Projective::from(a) + (-a), G2Projective::from(a) - a); +} + +#[test] +fn test_projective_scalar_multiplication() { + let g = G2Projective::generator(); + let a = Scalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = Scalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!((g * a) * b, g * c); +} + +#[test] +fn test_affine_scalar_multiplication() { + let g = G2Affine::generator(); + let a = Scalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = Scalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!(G2Affine::from(g * a) * b, g * c); +} + +#[test] +fn test_is_torsion_free() { + let a = G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x89f5_50c8_13db_6431, + 0xa50b_e8c4_56cd_8a1a, + 0xa45b_3741_14ca_e851, + 0xbb61_90f5_bf7f_ff63, + 0x970c_a02c_3ba8_0bc7, + 0x02b8_5d24_e840_fbac, + ]), + c1: Fp::from_raw_unchecked([ + 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: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3cf1_ea0d_434b_0f40, + 0x1a0d_c610_e603_e333, + 0x7f89_9561_60c7_2fa0, + 0x25ee_03de_cf64_31c5, + 0xeee8_e206_ec0f_e137, + 0x0975_92b2_26df_ef28, + ]), + c1: Fp::from_raw_unchecked([ + 0x71e8_bb5f_2924_7367, + 0xa5fe_049e_2118_31ce, + 0x0ce6_b354_502a_3896, + 0x93b0_1200_0997_314e, + 0x6759_f3b6_aa5b_42ac, + 0x1569_44c4_dfe9_2bbb, + ]), + }, + infinity: Choice::from(0u8), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G2Affine::identity().is_torsion_free())); + assert!(bool::from(G2Affine::generator().is_torsion_free())); +} + +#[test] +fn test_mul_by_x() { + // multiplying by `x` a point in G2 is the same as multiplying by + // the equivalent scalar. + let generator = G2Projective::generator(); + let x = if super::BLS_X_IS_NEGATIVE { + -Scalar::from(super::BLS_X) + } else { + Scalar::from(super::BLS_X) + }; + assert_eq!(generator.mul_by_x(), generator * x); + + let point = G2Projective::generator() * Scalar::from(42); + assert_eq!(point.mul_by_x(), point * x); +} + +#[test] +fn test_psi() { + let generator = G2Projective::generator(); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52, + ]), + c1: Fp::from_raw_unchecked([ + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3, + ]), + }; + + // `point` is a random point in the curve + let point = G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc, + ]), + c1: Fp::from_raw_unchecked([ + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495, + ]), + } * z, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771, + ]), + c1: Fp::from_raw_unchecked([ + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d, + ]), + }, + z: z.square() * z, + }; + assert!(bool::from(point.is_on_curve())); + + // psi2(P) = psi(psi(P)) + assert_eq!(generator.psi2(), generator.psi().psi()); + assert_eq!(point.psi2(), point.psi().psi()); + // psi(P) is a morphism + assert_eq!(generator.double().psi(), generator.psi().double()); + assert_eq!(point.psi() + generator.psi(), (point + generator).psi()); + // psi(P) behaves in the same way on the same projective point + let mut normalized_point = [G2Affine::identity()]; + G2Projective::batch_normalize(&[point], &mut normalized_point); + let normalized_point = G2Projective::from(normalized_point[0]); + assert_eq!(point.psi(), normalized_point.psi()); + assert_eq!(point.psi2(), normalized_point.psi2()); +} + +#[test] +fn test_clear_cofactor() { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52, + ]), + c1: Fp::from_raw_unchecked([ + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3, + ]), + }; + + // `point` is a random point in the curve + let point = G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc, + ]), + c1: Fp::from_raw_unchecked([ + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495, + ]), + } * z, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771, + ]), + c1: Fp::from_raw_unchecked([ + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d, + ]), + }, + z: z.square() * z, + }; + + assert!(bool::from(point.is_on_curve())); + assert!(!bool::from(G2Affine::from(point).is_torsion_free())); + let cleared_point = point.clear_cofactor(); + + assert!(bool::from(cleared_point.is_on_curve())); + assert!(bool::from(G2Affine::from(cleared_point).is_torsion_free())); + + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + let generator = G2Projective::generator(); + assert!(bool::from(generator.clear_cofactor().is_on_curve())); + let id = G2Projective::identity(); + assert!(bool::from(id.clear_cofactor().is_on_curve())); + + // test the effect on q-torsion points multiplying by h_eff modulo |Scalar| + // h_eff % q = 0x2b116900400069009a40200040001ffff + let h_eff_modq: [u8; 32] = [ + 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, + 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + assert_eq!(generator.clear_cofactor(), generator.multiply(&h_eff_modq)); + assert_eq!( + cleared_point.clear_cofactor(), + cleared_point.multiply(&h_eff_modq) + ); +} + +#[test] +fn test_batch_normalize() { + let a = G2Projective::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] = G2Projective::identity() + } + if b_identity { + v[1] = G2Projective::identity() + } + if c_identity { + v[2] = G2Projective::identity() + } + + let mut t = [ + G2Affine::identity(), + G2Affine::identity(), + G2Affine::identity(), + ]; + let expected = [ + G2Affine::from(v[0]), + G2Affine::from(v[1]), + G2Affine::from(v[2]), + ]; + + G2Projective::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); + } + } + } +} + +#[cfg(feature = "zeroize")] +#[test] +fn test_zeroize() { + use zeroize::Zeroize; + + let mut a = G2Affine::generator(); + a.zeroize(); + assert!(bool::from(a.is_identity())); + + let mut a = G2Projective::generator(); + a.zeroize(); + assert!(bool::from(a.is_identity())); + + let mut a = GroupEncoding::to_bytes(&G2Affine::generator()); + a.zeroize(); + assert_eq!(&a, &G2Compressed::default()); + + let mut a = UncompressedEncoding::to_uncompressed(&G2Affine::generator()); + a.zeroize(); + assert_eq!(&a, &G2Uncompressed::default()); +} + +#[allow(clippy::op_ref)] +#[test] +fn test_commutative_scalar_subgroup_multiplication() { + let a = Scalar::from_raw([ + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562, + ]); + + let g2_a = G2Affine::generator(); + let g2_p = G2Projective::generator(); + + // By reference. + assert_eq!(&g2_a * &a, &a * &g2_a); + assert_eq!(&g2_p * &a, &a * &g2_p); + + // Mixed + assert_eq!(&g2_a * a, a * &g2_a); + assert_eq!(&g2_p * a, a * &g2_p); + assert_eq!(g2_a * &a, &a * g2_a); + assert_eq!(g2_p * &a, &a * g2_p); + + // By value. + assert_eq!(g2_p * a, a * g2_p); + assert_eq!(g2_a * a, a * g2_a); +} diff --git a/src/bls12_381/hash_to_curve/chain.rs b/src/bls12_381/hash_to_curve/chain.rs new file mode 100644 index 00000000..b75c85c5 --- /dev/null +++ b/src/bls12_381/hash_to_curve/chain.rs @@ -0,0 +1,922 @@ +//! Addition chains for computing square roots. +//! chain_pm3div4: input x, output x^((p-3)//4). +//! chain_p2m9div16: input x, output x^((p**2 - 9) // 16). +//! +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use core::ops::MulAssign; + +use crate::bls12_381::{fp::Fp, fp2::Fp2}; + +macro_rules! square { + ($var:expr, $n:expr) => { + for _ in 0..$n { + $var = $var.square(); + } + }; +} + +#[allow(clippy::cognitive_complexity)] +/// addchain for 1000602388805416848354447456433976039139220704984751971333014534031007912622709466110671907282253916009473568139946 +/// Bos-Coster (win=4) : 458 links, 16 variables */ +/// Addition chain implementing exponentiation by (p - 3) // 4. +pub fn chain_pm3div4(var0: &Fp) -> Fp { + let mut var1 = var0.square(); + //Self::sqr(var1, var0); /* 0 : 2 */ + let var9 = var1 * var0; + //Self::mul(&mut var9, var1, var0); /* 1 : 3 */ + let var5 = var1.square(); + //Self::sqr(&mut var5, var1); /* 2 : 4 */ + let var2 = var9 * var1; + //Self::mul(&mut var2, &var9, var1); /* 3 : 5 */ + let var7 = var5 * var9; + //Self::mul(&mut var7, &var5, &var9); /* 4 : 7 */ + let var10 = var2 * var5; + //Self::mul(&mut var10, &var2, &var5); /* 5 : 9 */ + let var13 = var7 * var5; + //Self::mul(&mut var13, &var7, &var5); /* 6 : 11 */ + let var4 = var10 * var5; + //Self::mul(&mut var4, &var10, &var5); /* 7 : 13 */ + let var8 = var13 * var5; + //Self::mul(&mut var8, &var13, &var5); /* 8 : 15 */ + let var15 = var4 * var5; + //Self::mul(&mut var15, &var4, &var5); /* 9 : 17 */ + let var11 = var8 * var5; + //Self::mul(&mut var11, &var8, &var5); /* 10 : 19 */ + let var3 = var15 * var5; + //Self::mul(&mut var3, &var15, &var5); /* 11 : 21 */ + let var12 = var11 * var5; + //Self::mul(&mut var12, &var11, &var5); /* 12 : 23 */ + var1 = var4.square(); + //Self::sqr(var1, &var4); /* 13 : 26 */ + let var14 = var12 * var5; + //Self::mul(&mut var14, &var12, &var5); /* 14 : 27 */ + let var6 = var1 * var9; + //Self::mul(&mut var6, var1, &var9); /* 15 : 29 */ + let var5 = var1 * var2; + //Self::mul(&mut var5, var1, &var2); /* 16 : 31 */ + // 17 : 106496 + square!(var1, 12); + // 29 : 106513 + var1.mul_assign(&var15); + // 30 : 13633664 + square!(var1, 7); + // 37 : 13633679 + var1.mul_assign(&var8); + // 38 : 218138864 + square!(var1, 4); + // 42 : 218138869 + var1.mul_assign(&var2); + // 43 : 13960887616 + square!(var1, 6); + // 49 : 13960887623 + var1.mul_assign(&var7); + // 50 : 1786993615744 + square!(var1, 7); + // 57 : 1786993615767 + var1.mul_assign(&var12); + // 58 : 57183795704544 + square!(var1, 5); + // 63 : 57183795704575 + var1.mul_assign(&var5); + // 64 : 228735182818300 + square!(var1, 2); + // 66 : 228735182818303 + var1.mul_assign(&var9); + // 67 : 14639051700371392 + square!(var1, 6); + // 73 : 14639051700371405 + var1.mul_assign(&var4); + // 74 : 936899308823769920 + square!(var1, 6); + // 80 : 936899308823769933 + var1.mul_assign(&var4); + // 81 : 59961555764721275712 + square!(var1, 6); + // 87 : 59961555764721275721 + var1.mul_assign(&var10); + // 88 : 479692446117770205768 + square!(var1, 3); + // 91 : 479692446117770205771 + var1.mul_assign(&var9); + // 92 : 61400633103074586338688 + square!(var1, 7); + // 99 : 61400633103074586338701 + var1.mul_assign(&var4); + // 100 : 982410129649193381419216 + square!(var1, 4); + // 104 : 982410129649193381419229 + var1.mul_assign(&var4); + // 105 : 62874248297548376410830656 + square!(var1, 6); + // 111 : 62874248297548376410830671 + var1.mul_assign(&var8); + // 112 : 4023951891043096090293162944 + square!(var1, 6); + // 118 : 4023951891043096090293162971 + var1.mul_assign(&var14); + // 119 : 32191615128344768722345303768 + square!(var1, 3); + // 122 : 32191615128344768722345303769 + var1.mul_assign(var0); + // 123 : 8241053472856260792920397764864 + square!(var1, 8); + // 131 : 8241053472856260792920397764877 + var1.mul_assign(&var4); + // 132 : 1054854844525601381493810913904256 + square!(var1, 7); + // 139 : 1054854844525601381493810913904279 + var1.mul_assign(&var12); + // 140 : 33755355024819244207801949244936928 + square!(var1, 5); + // 145 : 33755355024819244207801949244936939 + var1.mul_assign(&var13); + // 146 : 2160342721588431629299324751675964096 + square!(var1, 6); + // 152 : 2160342721588431629299324751675964109 + var1.mul_assign(&var4); + // 153 : 138261934181659624275156784107261702976 + square!(var1, 6); + // 159 : 138261934181659624275156784107261703005 + var1.mul_assign(&var6); + // 160 : 2212190946906553988402508545716187248080 + square!(var1, 4); + // 164 : 2212190946906553988402508545716187248089 + var1.mul_assign(&var10); + // 165 : 566320882408077821031042187703343935510784 + square!(var1, 8); + // 173 : 566320882408077821031042187703343935510813 + var1.mul_assign(&var6); + // 174 : 9061134118529245136496675003253502968173008 + square!(var1, 4); + // 178 : 9061134118529245136496675003253502968173021 + var1.mul_assign(&var4); + // 179 : 1159825167171743377471574400416448379926146688 + square!(var1, 7); + // 186 : 1159825167171743377471574400416448379926146711 + var1.mul_assign(&var12); + // 187 : 593830485591932609265446093013221570522187116032 + square!(var1, 9); + // 196 : 593830485591932609265446093013221570522187116051 + var1.mul_assign(&var11); + // 197 : 2375321942367730437061784372052886282088748464204 + square!(var1, 2); + // 199 : 2375321942367730437061784372052886282088748464207 + var1.mul_assign(&var9); + // 200 : 76010302155767373985977099905692361026839950854624 + square!(var1, 5); + // 205 : 76010302155767373985977099905692361026839950854631 + var1.mul_assign(&var7); + // 206 : 9729318675938223870205068787928622211435513709392768 + square!(var1, 7); + // 213 : 9729318675938223870205068787928622211435513709392773 + var1.mul_assign(&var2); + // 214 : 1245352790520092655386248804854863643063745754802274944 + square!(var1, 7); + // 221 : 1245352790520092655386248804854863643063745754802274953 + var1.mul_assign(&var10); + // 222 : 79702578593285929944719923510711273156079728307345596992 + square!(var1, 6); + // 228 : 79702578593285929944719923510711273156079728307345597015 + var1.mul_assign(&var12); + // 229 : 2550482514985149758231037552342760740994551305835059104480 + square!(var1, 5); + // 234 : 2550482514985149758231037552342760740994551305835059104509 + var1.mul_assign(&var6); + // 235 : 81615440479524792263393201674968343711825641786721891344288 + square!(var1, 5); + // 240 : 81615440479524792263393201674968343711825641786721891344307 + var1.mul_assign(&var11); + // 241 : 2611694095344793352428582453598986998778420537175100523017824 + square!(var1, 5); + // 246 : 2611694095344793352428582453598986998778420537175100523017843 + var1.mul_assign(&var11); + // 247 : 668593688408267098221717108121340671687275657516825733892567808 + square!(var1, 8); + // 255 : 668593688408267098221717108121340671687275657516825733892567821 + var1.mul_assign(&var4); + // 256 : 85579992116258188572379789839531605975971284162153693938248681088 + square!(var1, 7); + // 263 : 85579992116258188572379789839531605975971284162153693938248681109 + var1.mul_assign(&var3); + // 264 : 43816955963524192549058452397840182259697297491022691296383324727808 + square!(var1, 9); + // 273 : 43816955963524192549058452397840182259697297491022691296383324727823 + var1.mul_assign(&var8); + // 274 : 1402142590832774161569870476730885832310313519712726121484266391290336 + square!(var1, 5); + // 279 : 1402142590832774161569870476730885832310313519712726121484266391290349 + var1.mul_assign(&var4); + // 280 : 11217140726662193292558963813847086658482508157701808971874131130322792 + square!(var1, 3); + // 283 : 11217140726662193292558963813847086658482508157701808971874131130322795 + var1.mul_assign(&var9); + // 284 : 2871588026025521482895094736344854184571522088371663096799777569362635520 + square!(var1, 8); + // 292 : 2871588026025521482895094736344854184571522088371663096799777569362635535 + var1.mul_assign(&var8); + // 293 : 22972704208204171863160757890758833476572176706973304774398220554901084280 + square!(var1, 3); + // 296 : 22972704208204171863160757890758833476572176706973304774398220554901084283 + var1.mul_assign(&var9); + // 297 : 2940506138650133998484577010017130685001238618492583011122972231027338788224 + square!(var1, 7); + // 304 : 2940506138650133998484577010017130685001238618492583011122972231027338788233 + var1.mul_assign(&var10); + // 305 : 1505539142988868607224103429128770910720634172668202501694961782285997459575296 + square!(var1, 9); + // 314 : 1505539142988868607224103429128770910720634172668202501694961782285997459575311 + var1.mul_assign(&var8); + // 315 : 96354505151287590862342619464241338286120587050764960108477554066303837412819904 + square!(var1, 6); + // 321 : 96354505151287590862342619464241338286120587050764960108477554066303837412819925 + var1.mul_assign(&var3); + // 322 : 6166688329682405815189927645711445650311717571248957446942563460243445594420475200 + square!(var1, 6); + // 328 : 6166688329682405815189927645711445650311717571248957446942563460243445594420475231 + var1.mul_assign(&var5); + // 329 : 197334026549836986086077684662766260809974962279966638302162030727790259021455207392 + square!(var1, 5); + // 334 : 197334026549836986086077684662766260809974962279966638302162030727790259021455207423 + var1.mul_assign(&var5); + // 335 : 6314688849594783554754485909208520345919198792958932425669184983289288288686566637536 + square!(var1, 5); + // 340 : 6314688849594783554754485909208520345919198792958932425669184983289288288686566637567 + var1.mul_assign(&var5); + // 341 : 101035021593516536876071774547336325534707180687342918810706959732628612618985066201072 + square!(var1, 4); + // 345 : 101035021593516536876071774547336325534707180687342918810706959732628612618985066201085 + var1.mul_assign(&var4); + // 346 : 808280172748132295008574196378690604277657445498743350485655677861028900951880529608680 + square!(var1, 3); + // 349 : 808280172748132295008574196378690604277657445498743350485655677861028900951880529608683 + var1.mul_assign(&var9); + // 350 : 206919724223521867522194994272944794695080306047678297724327853532423398643681415579822848 + square!(var1, 8); + // 358 : 206919724223521867522194994272944794695080306047678297724327853532423398643681415579822869 + var1.mul_assign(&var3); + // 359 : 26485724700610799042840959266936933720970279174102822108713965252150195026391221194217327232 + square!(var1, 7); + // 366 : 26485724700610799042840959266936933720970279174102822108713965252150195026391221194217327263 + var1.mul_assign(&var5); + // 367 : 847543190419545569370910696541981879071048933571290307478846888068806240844519078214954472416 + square!(var1, 5); + // 372 : 847543190419545569370910696541981879071048933571290307478846888068806240844519078214954472447 + var1.mul_assign(&var5); + // 373 : 27121382093425458219869142289343420130273565874281289839323100418201799707024610502878543118304 + square!(var1, 5); + // 378 : 27121382093425458219869142289343420130273565874281289839323100418201799707024610502878543118335 + var1.mul_assign(&var5); + // 379 : 433942113494807331517906276629494722084377053988500637429169606691228795312393768046056689893360 + square!(var1, 4); + // 383 : 433942113494807331517906276629494722084377053988500637429169606691228795312393768046056689893375 + var1.mul_assign(&var8); + // 384 : 6943073815916917304286500426071915553350032863816010198866713707059660724998300288736907038294000 + square!(var1, 4); + // 388 : 6943073815916917304286500426071915553350032863816010198866713707059660724998300288736907038294007 + var1.mul_assign(&var7); + // 389 : 888713448437365414948672054537205190828804206568449305454939354503636572799782436958324100901632896 + square!(var1, 7); + // 396 : 888713448437365414948672054537205190828804206568449305454939354503636572799782436958324100901632927 + var1.mul_assign(&var5); + // 397 : 28438830349995693278357505745190566106521734610190377774558059344116370329593037982666371228852253664 + square!(var1, 5); + // 402 : 28438830349995693278357505745190566106521734610190377774558059344116370329593037982666371228852253693 + var1.mul_assign(&var6); + // 403 : 910042571199862184907440183846098115408695507526092088785857899011723850546977215445323879323272118176 + square!(var1, 5); + // 408 : 910042571199862184907440183846098115408695507526092088785857899011723850546977215445323879323272118207 + var1.mul_assign(&var5); + // 409 : 29121362278395589917038085883075139693078256240834946841147452768375163217503270894250364138344707782624 + square!(var1, 5); + // 414 : 29121362278395589917038085883075139693078256240834946841147452768375163217503270894250364138344707782655 + var1.mul_assign(&var5); + // 415 : 931883592908658877345218748258404470178504199706718298916718488588005222960104668616011652427030649044960 + square!(var1, 5); + // 420 : 931883592908658877345218748258404470178504199706718298916718488588005222960104668616011652427030649044991 + var1.mul_assign(&var5); + // 421 : 29820274973077084075046999944268943045712134390614985565334991634816167134723349395712372877664980769439712 + square!(var1, 5); + // 426 : 29820274973077084075046999944268943045712134390614985565334991634816167134723349395712372877664980769439743 + var1.mul_assign(&var5); + // 427 : 954248799138466690401503998216606177462788300499679538090719732314117348311147180662795932085279384622071776 + square!(var1, 5); + // 432 : 954248799138466690401503998216606177462788300499679538090719732314117348311147180662795932085279384622071807 + var1.mul_assign(&var5); + // 433 : 30535961572430934092848127942931397678809225615989745218903031434051755145956709781209469826728940307906297824 + square!(var1, 5); + // 438 : 30535961572430934092848127942931397678809225615989745218903031434051755145956709781209469826728940307906297855 + var1.mul_assign(&var5); + // 439 : 488575385158894945485570047086902362860947609855835923502448502944828082335307356499351517227663044926500765680 + square!(var1, 4); + // 443 : 488575385158894945485570047086902362860947609855835923502448502944828082335307356499351517227663044926500765693 + var1.mul_assign(&var4); + // 444 : 31268824650169276511076483013561751223100647030773499104156704188468997269459670815958497102570434875296049004352 + square!(var1, 6); + // 450 : 31268824650169276511076483013561751223100647030773499104156704188468997269459670815958497102570434875296049004373 + var1.mul_assign(&var3); + // 451 : 500301194402708424177223728216988019569610352492375985666507267015503956311354733055335953641126958004736784069968 + square!(var1, 4); + // 455 : 500301194402708424177223728216988019569610352492375985666507267015503956311354733055335953641126958004736784069973 + var1.mul_assign(&var2); + // 456 : 1000602388805416848354447456433976039139220704984751971333014534031007912622709466110671907282253916009473568139946 + var1.square() +} + +#[allow(clippy::cognitive_complexity)] +/// addchain for 1001205140483106588246484290269935788605945006208159541241399033561623546780709821462541004956387089373434649096260670658193992783731681621012512651314777238193313314641988297376025498093520728838658813979860931248214124593092835 +/// Bos-Coster (win=4) : 895 links, 17 variables +/// Addition chain implementing exponentiation by (p**2 - 9) // 16. +pub fn chain_p2m9div16(var0: &Fp2) -> Fp2 { + let mut var1 = var0.square(); + //Self::sqr(var1, var0); /* 0 : 2 */ + let var2 = var1 * var0; + //Self::mul(&mut var2, var1, var0); /* 1 : 3 */ + let var15 = var2 * var1; + //Self::mul(&mut var15, &var2, var1); /* 2 : 5 */ + let var3 = var15 * var1; + //Self::mul(&mut var3, &var15, var1); /* 3 : 7 */ + let var14 = var3 * var1; + //Self::mul(&mut var14, &var3, var1); /* 4 : 9 */ + let var13 = var14 * var1; + //Self::mul(&mut var13, &var14, var1); /* 5 : 11 */ + let var5 = var13 * var1; + //Self::mul(&mut var5, &var13, var1); /* 6 : 13 */ + let var10 = var5 * var1; + //Self::mul(&mut var10, &var5, var1); /* 7 : 15 */ + let var9 = var10 * var1; + //Self::mul(&mut var9, &var10, var1); /* 8 : 17 */ + let var16 = var9 * var1; + //Self::mul(&mut var16, &var9, var1); /* 9 : 19 */ + let var4 = var16 * var1; + //Self::mul(&mut var4, &var16, var1); /* 10 : 21 */ + let var7 = var4 * var1; + //Self::mul(&mut var7, &var4, var1); /* 11 : 23 */ + let var6 = var7 * var1; + //Self::mul(&mut var6, &var7, var1); /* 12 : 25 */ + let var12 = var6 * var1; + //Self::mul(&mut var12, &var6, var1); /* 13 : 27 */ + let var8 = var12 * var1; + //Self::mul(&mut var8, &var12, var1); /* 14 : 29 */ + let var11 = var8 * var1; + //Self::mul(&mut var11, &var8, var1); /* 15 : 31 */ + var1 = var4.square(); + //Self::sqr(var1, &var4); /* 16 : 42 */ + // 17 : 168 + square!(var1, 2); + // 19 : 169 + var1.mul_assign(var0); + // 20 : 86528 + square!(var1, 9); + // 29 : 86555 + var1.mul_assign(&var12); + // 30 : 1384880 + square!(var1, 4); + // 34 : 1384893 + var1.mul_assign(&var5); + // 35 : 88633152 + square!(var1, 6); + // 41 : 88633161 + var1.mul_assign(&var14); + // 42 : 1418130576 + square!(var1, 4); + // 46 : 1418130583 + var1.mul_assign(&var3); + // 47 : 45380178656 + square!(var1, 5); + // 52 : 45380178659 + var1.mul_assign(&var2); + // 53 : 11617325736704 + square!(var1, 8); + // 61 : 11617325736717 + var1.mul_assign(&var5); + // 62 : 185877211787472 + square!(var1, 4); + // 66 : 185877211787479 + var1.mul_assign(&var3); + // 67 : 2974035388599664 + square!(var1, 4); + // 71 : 2974035388599679 + var1.mul_assign(&var10); + // 72 : 761353059481517824 + square!(var1, 8); + // 80 : 761353059481517853 + var1.mul_assign(&var8); + // 81 : 48726595806817142592 + square!(var1, 6); + // 87 : 48726595806817142603 + var1.mul_assign(&var13); + // 88 : 779625532909074281648 + square!(var1, 4); + // 92 : 779625532909074281661 + var1.mul_assign(&var5); + // 93 : 6237004263272594253288 + square!(var1, 3); + // 96 : 6237004263272594253289 + var1.mul_assign(var0); + // 97 : 399168272849446032210496 + square!(var1, 6); + // 103 : 399168272849446032210511 + var1.mul_assign(&var10); + // 104 : 102187077849458184245890816 + square!(var1, 8); + // 112 : 102187077849458184245890845 + var1.mul_assign(&var8); + // 113 : 6539972982365323791737014080 + square!(var1, 6); + // 119 : 6539972982365323791737014101 + var1.mul_assign(&var4); + // 120 : 1674233083485522890684675609856 + square!(var1, 8); + // 128 : 1674233083485522890684675609873 + var1.mul_assign(&var9); + // 129 : 53575458671536732501909619515936 + square!(var1, 5); + // 134 : 53575458671536732501909619515951 + var1.mul_assign(&var10); + // 135 : 3428829354978350880122215649020864 + square!(var1, 6); + // 141 : 3428829354978350880122215649020873 + var1.mul_assign(&var14); + // 142 : 109722539359307228163910900768667936 + square!(var1, 5); + // 147 : 109722539359307228163910900768667951 + var1.mul_assign(&var10); + // 148 : 438890157437228912655643603074671804 + square!(var1, 2); + // 150 : 438890157437228912655643603074671805 + var1.mul_assign(var0); + // 151 : 28088970075982650409961190596778995520 + square!(var1, 6); + // 157 : 28088970075982650409961190596778995535 + var1.mul_assign(&var10); + // 158 : 3595388169725779252475032396387711428480 + square!(var1, 7); + // 165 : 3595388169725779252475032396387711428491 + var1.mul_assign(&var13); + // 166 : 57526210715612468039600518342203382855856 + square!(var1, 4); + // 170 : 57526210715612468039600518342203382855863 + var1.mul_assign(&var3); + // 171 : 3681677485799197954534433173901016502775232 + square!(var1, 6); + // 177 : 3681677485799197954534433173901016502775241 + var1.mul_assign(&var14); + // 178 : 471254718182297338180407446259330112355230848 + square!(var1, 7); + // 185 : 471254718182297338180407446259330112355230855 + var1.mul_assign(&var3); + // 186 : 15080150981833514821773038280298563595367387360 + square!(var1, 5); + // 191 : 15080150981833514821773038280298563595367387365 + var1.mul_assign(&var15); + // 192 : 1930259325674689897186948899878216140207025582720 + square!(var1, 7); + // 199 : 1930259325674689897186948899878216140207025582727 + var1.mul_assign(&var3); + // 200 : 61768298421590076709982364796102916486624818647264 + square!(var1, 5); + // 205 : 61768298421590076709982364796102916486624818647271 + var1.mul_assign(&var3); + // 206 : 63250737583708238551021941551209386482303814294805504 + square!(var1, 10); + // 216 : 63250737583708238551021941551209386482303814294805521 + var1.mul_assign(&var9); + // 217 : 506005900669665908408175532409675091858430514358444168 + square!(var1, 3); + // 220 : 506005900669665908408175532409675091858430514358444173 + var1.mul_assign(&var15); + // 221 : 16192188821429309069061617037109602939469776459470213536 + square!(var1, 5); + // 226 : 16192188821429309069061617037109602939469776459470213549 + var1.mul_assign(&var5); + // 227 : 4145200338285903121679773961500058352504262773624374668544 + square!(var1, 8); + // 235 : 4145200338285903121679773961500058352504262773624374668569 + var1.mul_assign(&var6); + // 236 : 132646410825148899893752766768001867280136408755979989394208 + square!(var1, 5); + // 241 : 132646410825148899893752766768001867280136408755979989394231 + var1.mul_assign(&var7); + // 242 : 8489370292809529593200177073152119505928730160382719321230784 + square!(var1, 6); + // 248 : 8489370292809529593200177073152119505928730160382719321230795 + var1.mul_assign(&var13); + // 249 : 543319698739809893964811332681735648379438730264494036558770880 + square!(var1, 6); + // 255 : 543319698739809893964811332681735648379438730264494036558770895 + var1.mul_assign(&var10); + // 256 : 34772460719347833213747925291631081496284078736927618339761337280 + square!(var1, 6); + // 262 : 34772460719347833213747925291631081496284078736927618339761337289 + var1.mul_assign(&var14); + // 263 : 4450874972076522651359734437328778431524362078326735147489451172992 + square!(var1, 7); + // 270 : 4450874972076522651359734437328778431524362078326735147489451173011 + var1.mul_assign(&var16); + // 271 : 142427999106448724843511501994520909808779586506455524719662437536352 + square!(var1, 5); + // 276 : 142427999106448724843511501994520909808779586506455524719662437536361 + var1.mul_assign(&var14); + // 277 : 9115391942812718389984736127649338227761893536413153582058396002327104 + square!(var1, 6); + // 283 : 9115391942812718389984736127649338227761893536413153582058396002327119 + var1.mul_assign(&var10); + // 284 : 583385084340013976959023112169557646576761186330441829251737344148935616 + square!(var1, 6); + // 290 : 583385084340013976959023112169557646576761186330441829251737344148935633 + var1.mul_assign(&var9); + // 291 : 18668322698880447262688739589425844690456357962574138536055595012765940256 + square!(var1, 5); + // 296 : 18668322698880447262688739589425844690456357962574138536055595012765940271 + var1.mul_assign(&var10); + // 297 : 74673290795521789050754958357703378761825431850296554144222380051063761084 + square!(var1, 2); + // 299 : 74673290795521789050754958357703378761825431850296554144222380051063761085 + var1.mul_assign(var0); + // 300 : 19116362443653577996993269339572064963027310553675917860920929293072322837760 + square!(var1, 8); + // 308 : 19116362443653577996993269339572064963027310553675917860920929293072322837765 + var1.mul_assign(&var15); + // 309 : 2446894392787657983615138475465224315267495750870517486197878949513257323233920 + square!(var1, 7); + // 316 : 2446894392787657983615138475465224315267495750870517486197878949513257323233925 + var1.mul_assign(&var15); + // 317 : 39150310284602527737842215607443589044279932013928279779166063192212117171742800 + square!(var1, 4); + // 321 : 39150310284602527737842215607443589044279932013928279779166063192212117171742803 + var1.mul_assign(&var2); + // 322 : 5011239716429123550443803597752779397667831297782819811733256088603150997983078784 + square!(var1, 7); + // 329 : 5011239716429123550443803597752779397667831297782819811733256088603150997983078795 + var1.mul_assign(&var13); + // 330 : 320719341851463907228403430256177881450741203058100467950928389670601663870917042880 + square!(var1, 6); + // 336 : 320719341851463907228403430256177881450741203058100467950928389670601663870917042895 + var1.mul_assign(&var10); + // 337 : 5131509469623422515654454884098846103211859248929607487214854234729626621934672686320 + square!(var1, 4); + // 341 : 5131509469623422515654454884098846103211859248929607487214854234729626621934672686333 + var1.mul_assign(&var5); + // 342 : 656833212111798082003770225164652301211117983862989758363501342045392207607638103850624 + square!(var1, 7); + // 349 : 656833212111798082003770225164652301211117983862989758363501342045392207607638103850635 + var1.mul_assign(&var13); + // 350 : 42037325575155077248241294410537747277511550967231344535264085890905101286888838646440640 + square!(var1, 6); + // 356 : 42037325575155077248241294410537747277511550967231344535264085890905101286888838646440667 + var1.mul_assign(&var12); + // 357 : 1345194418404962471943721421137207912880369630951403025128450748508963241180442836686101344 + square!(var1, 5); + // 362 : 1345194418404962471943721421137207912880369630951403025128450748508963241180442836686101367 + var1.mul_assign(&var7); + // 363 : 43046221388958799102199085476390653212171828190444896804110423952286823717774170773955243744 + square!(var1, 5); + // 368 : 43046221388958799102199085476390653212171828190444896804110423952286823717774170773955243749 + var1.mul_assign(&var15); + // 369 : 5509916337786726285081482940978003611157994008376946790926134265892713435875093859066271199872 + square!(var1, 7); + // 376 : 5509916337786726285081482940978003611157994008376946790926134265892713435875093859066271199899 + var1.mul_assign(&var12); + // 377 : 176317322809175241122607454111296115557055808268062297309636296508566829948003003490120678396768 + square!(var1, 5); + // 382 : 176317322809175241122607454111296115557055808268062297309636296508566829948003003490120678396791 + var1.mul_assign(&var7); + // 383 : 5642154329893607715923438531561475697825785864577993513908361488274138558336096111683861708697312 + square!(var1, 5); + // 388 : 5642154329893607715923438531561475697825785864577993513908361488274138558336096111683861708697333 + var1.mul_assign(&var4); + // 389 : 90274469278297723454775016504983611165212573833247896222533783812386216933377537786941787339157328 + square!(var1, 4); + // 393 : 90274469278297723454775016504983611165212573833247896222533783812386216933377537786941787339157331 + var1.mul_assign(&var2); + // 394 : 5777566033811054301105601056318951114573604725327865358242162163992717883736162418364274389706069184 + square!(var1, 6); + // 400 : 5777566033811054301105601056318951114573604725327865358242162163992717883736162418364274389706069189 + var1.mul_assign(&var15); + // 401 : 369764226163907475270758467604412871332710702420983382927498378495533944559114394775313560941188428096 + square!(var1, 6); + // 407 : 369764226163907475270758467604412871332710702420983382927498378495533944559114394775313560941188428105 + var1.mul_assign(&var14); + // 408 : 5916227618622519604332135481670605941323371238735734126839974055928543112945830316405016975059014849680 + square!(var1, 4); + // 412 : 5916227618622519604332135481670605941323371238735734126839974055928543112945830316405016975059014849683 + var1.mul_assign(&var2); + // 413 : 94659641897960313669314167706729695061173939819771746029439584894856689807133285062480271600944237594928 + square!(var1, 4); + // 417 : 94659641897960313669314167706729695061173939819771746029439584894856689807133285062480271600944237594931 + var1.mul_assign(&var2); + // 418 : 24232868325877840299344426932922801935660528593861566983536533733083312590626120975994949529841724824302336 + square!(var1, 8); + // 426 : 24232868325877840299344426932922801935660528593861566983536533733083312590626120975994949529841724824302345 + var1.mul_assign(&var14); + // 427 : 775451786428090889579021661853529661941136915003570143473169079458666002900035871231838384954935194377675040 + square!(var1, 5); + // 432 : 775451786428090889579021661853529661941136915003570143473169079458666002900035871231838384954935194377675055 + var1.mul_assign(&var10); + // 433 : 49628914331397816933057386358625898364232762560228489182282821085354624185602295758837656637115852440171203520 + square!(var1, 6); + // 439 : 49628914331397816933057386358625898364232762560228489182282821085354624185602295758837656637115852440171203527 + var1.mul_assign(&var3); + // 440 : 1588125258604730141857836363476028747655448401927311653833050274731347973939273464282805012387707278085478512864 + square!(var1, 5); + // 445 : 1588125258604730141857836363476028747655448401927311653833050274731347973939273464282805012387707278085478512879 + var1.mul_assign(&var10); + // 446 : 6504961059244974661049697744797813750396716654294268534100173925299601301255264109702369330740049011038119988752384 + square!(var1, 12); + // 458 : 6504961059244974661049697744797813750396716654294268534100173925299601301255264109702369330740049011038119988752401 + var1.mul_assign(&var9); + // 459 : 104079376947919594576795163916765020006347466468708296545602782804793620820084225755237909291840784176609919820038416 + square!(var1, 4); + // 463 : 104079376947919594576795163916765020006347466468708296545602782804793620820084225755237909291840784176609919820038429 + var1.mul_assign(&var5); + // 464 : 3330540062333427026457445245336480640203118926998665489459289049753395866242695224167613097338905093651517434241229728 + square!(var1, 5); + // 469 : 3330540062333427026457445245336480640203118926998665489459289049753395866242695224167613097338905093651517434241229741 + var1.mul_assign(&var5); + // 470 : 213154563989339329693276495701534760972999611327914591325394499184217335439532494346727238229689925993697115791438703424 + square!(var1, 6); + // 476 : 213154563989339329693276495701534760972999611327914591325394499184217335439532494346727238229689925993697115791438703427 + var1.mul_assign(&var2); + // 477 : 109135136762541736802957565799185797618175800999892270758601983582319275745040637105524345973601242108772923285216616154624 + square!(var1, 9); + // 486 : 109135136762541736802957565799185797618175800999892270758601983582319275745040637105524345973601242108772923285216616154649 + var1.mul_assign(&var6); + // 487 : 3492324376401335577694642105573945523781625631996552664275263474634216823841300387376779071155239747480733545126931716948768 + square!(var1, 5); + // 492 : 3492324376401335577694642105573945523781625631996552664275263474634216823841300387376779071155239747480733545126931716948793 + var1.mul_assign(&var6); + // 493 : 223508760089685476972457094756732513522024040447779370513616862376589876725843224792113860553935343838766946888123629884722752 + square!(var1, 6); + // 499 : 223508760089685476972457094756732513522024040447779370513616862376589876725843224792113860553935343838766946888123629884722755 + var1.mul_assign(&var2); + // 500 : 14304560645739870526237254064430880865409538588657879712871479192101752110453966386695287075451862005681084600839912312622256320 + square!(var1, 6); + // 506 : 14304560645739870526237254064430880865409538588657879712871479192101752110453966386695287075451862005681084600839912312622256323 + var1.mul_assign(&var2); + // 507 : 7323935050618813709433474080988611003089683757392834412990197346356097080552430789987986982631353346908715315630035104062595237376 + square!(var1, 9); + // 516 : 7323935050618813709433474080988611003089683757392834412990197346356097080552430789987986982631353346908715315630035104062595237399 + var1.mul_assign(&var7); + // 517 : 937463686479208154807484682366542208395479520946282804862745260333580426310711141118462333776813228404315560400644493320012190387072 + square!(var1, 7); + // 524 : 937463686479208154807484682366542208395479520946282804862745260333580426310711141118462333776813228404315560400644493320012190387087 + var1.mul_assign(&var10); + // 525 : 59997675934669321907679019671458701337310689340562099511215696661349147283885513031581589361716046617876195865641247572480780184773568 + square!(var1, 6); + // 531 : 59997675934669321907679019671458701337310689340562099511215696661349147283885513031581589361716046617876195865641247572480780184773593 + var1.mul_assign(&var6); + // 532 : 1919925629909418301045728629486678442793942058897987184358902293163172713084336417010610859574913491772038267700519922319384965912754976 + square!(var1, 5); + // 537 : 1919925629909418301045728629486678442793942058897987184358902293163172713084336417010610859574913491772038267700519922319384965912754985 + var1.mul_assign(&var14); + // 538 : 245750480628405542533853264574294840677624583538942359597939493524886107274795061377358190025588926946820898265666550056881275636832638080 + square!(var1, 7); + // 545 : 245750480628405542533853264574294840677624583538942359597939493524886107274795061377358190025588926946820898265666550056881275636832638103 + var1.mul_assign(&var7); + // 546 : 983001922513622170135413058297179362710498334155769438391757974099544429099180245509432760102355707787283593062666200227525102547330552412 + square!(var1, 2); + // 548 : 983001922513622170135413058297179362710498334155769438391757974099544429099180245509432760102355707787283593062666200227525102547330552413 + var1.mul_assign(var0); + // 549 : 251648492163487275554665742924077916853887573543876976228290041369483373849390142850414786586203061193544599824042547258246426252116621417728 + square!(var1, 8); + // 557 : 251648492163487275554665742924077916853887573543876976228290041369483373849390142850414786586203061193544599824042547258246426252116621417739 + var1.mul_assign(&var13); + // 558 : 4026375874615796408874651886785246669662201176702031619652640661911733981590242285606636585379248979096713597184680756131942820033865942683824 + square!(var1, 4); + // 562 : 4026375874615796408874651886785246669662201176702031619652640661911733981590242285606636585379248979096713597184680756131942820033865942683829 + var1.mul_assign(&var15); + // 563 : 515376111950821940335955441508511573716761750617860047315538004724701949643551012557649482928543869324379340439639136784888680964334840663530112 + square!(var1, 7); + // 570 : 515376111950821940335955441508511573716761750617860047315538004724701949643551012557649482928543869324379340439639136784888680964334840663530119 + var1.mul_assign(&var3); + // 571 : 131936284659410416726004593026178962871491008158172172112777729209523699108749059214758267629707230547041111152547619016931502326869719209863710464 + square!(var1, 8); + // 579 : 131936284659410416726004593026178962871491008158172172112777729209523699108749059214758267629707230547041111152547619016931502326869719209863710473 + var1.mul_assign(&var14); + // 580 : 16887844436404533340928587907350907247550849044246038030435549338819033485919879579489058256602525510021262227526095234167232297839324058862554940544 + square!(var1, 7); + // 587 : 16887844436404533340928587907350907247550849044246038030435549338819033485919879579489058256602525510021262227526095234167232297839324058862554940557 + var1.mul_assign(&var5); + // 588 : 17293152702878242141110874017127329021492069421307942943166002522950690289581956689396795654760986122261772520986721519787245872987467836275256259130368 + square!(var1, 10); + // 598 : 17293152702878242141110874017127329021492069421307942943166002522950690289581956689396795654760986122261772520986721519787245872987467836275256259130377 + var1.mul_assign(&var14); + // 599 : 1106761772984207497031095937096149057375492442963708348362624161468844178533245228121394921904703111824753441343150177266383735871197941521616400584344128 + square!(var1, 6); + // 605 : 1106761772984207497031095937096149057375492442963708348362624161468844178533245228121394921904703111824753441343150177266383735871197941521616400584344139 + var1.mul_assign(&var13); + // 606 : 70832753470989279809990139974153539672031516349677334295207946334006027426127694599769275001900999156784220245961611345048559095756668257383449637398024896 + square!(var1, 6); + // 612 : 70832753470989279809990139974153539672031516349677334295207946334006027426127694599769275001900999156784220245961611345048559095756668257383449637398024909 + var1.mul_assign(&var5); + // 613 : 4533296222143313907839368958345826539010017046379349394893308565376385755272172454385233600121663946034190095741543126083107782128426768472540776793473594176 + square!(var1, 6); + // 619 : 4533296222143313907839368958345826539010017046379349394893308565376385755272172454385233600121663946034190095741543126083107782128426768472540776793473594207 + var1.mul_assign(&var11); + // 620 : 145065479108586045050859806667066449248320545484139180636585874092044344168709518540327475203893246273094083063729380034659449028109656591121304857391155014624 + square!(var1, 5); + // 625 : 145065479108586045050859806667066449248320545484139180636585874092044344168709518540327475203893246273094083063729380034659449028109656591121304857391155014649 + var1.mul_assign(&var6); + // 626 : 18568381325899013766510055253384505503785029821969815121482991883781676053594818373161916826098335522956042632157360644436409475598036043663527021746067841875072 + square!(var1, 7); + // 633 : 18568381325899013766510055253384505503785029821969815121482991883781676053594818373161916826098335522956042632157360644436409475598036043663527021746067841875087 + var1.mul_assign(&var10); + // 634 : 594188202428768440528321768108304176121120954303034083887455740281013633715034187941181338435146736734593364229035540621965103219137153397232864695874170940002784 + square!(var1, 5); + // 639 : 594188202428768440528321768108304176121120954303034083887455740281013633715034187941181338435146736734593364229035540621965103219137153397232864695874170940002797 + var1.mul_assign(&var5); + // 640 : 76056089910882360387625186317862934543503482150788362737594334755969745115524376056471211319698782302027950621316549199611533212049555634845806681071893880320358016 + square!(var1, 7); + // 647 : 76056089910882360387625186317862934543503482150788362737594334755969745115524376056471211319698782302027950621316549199611533212049555634845806681071893880320358047 + var1.mul_assign(&var11); + // 648 : 2433794877148235532404005962171613905392111428825227607603018712191031843696780033807078762230361033664894419882129574387569062785585780315065813794300604170251457504 + square!(var1, 5); + // 653 : 2433794877148235532404005962171613905392111428825227607603018712191031843696780033807078762230361033664894419882129574387569062785585780315065813794300604170251457511 + var1.mul_assign(&var3); + // 654 : 623051488549948296295425526315933159780380525779258267546372790320904151986375688654612163130972424618212971489825171043217680073109959760656848331340954667584373122816 + square!(var1, 8); + // 662 : 623051488549948296295425526315933159780380525779258267546372790320904151986375688654612163130972424618212971489825171043217680073109959760656848331340954667584373122843 + var1.mul_assign(&var12); + // 663 : 39875295267196690962907233684219722225944353649872529122967858580537865727128044073895178440382235175565630175348810946765931524679037424682038293205821098725399879861952 + square!(var1, 6); + // 669 : 39875295267196690962907233684219722225944353649872529122967858580537865727128044073895178440382235175565630175348810946765931524679037424682038293205821098725399879861981 + var1.mul_assign(&var8); + // 670 : 2552018897100588221626062955790062222460438633591841863869942949154423406536194820729291420184463051236200331222323900593019617579458395179650450765172550318425592311166784 + square!(var1, 6); + // 676 : 2552018897100588221626062955790062222460438633591841863869942949154423406536194820729291420184463051236200331222323900593019617579458395179650450765172550318425592311166787 + var1.mul_assign(&var2); + // 677 : 326658418828875292368136058341127964474936145099755758575352697491766196036632937053349301783611270558233642396457459275906511050170674582995257697942086440758475815829348736 + square!(var1, 7); + // 684 : 326658418828875292368136058341127964474936145099755758575352697491766196036632937053349301783611270558233642396457459275906511050170674582995257697942086440758475815829348747 + var1.mul_assign(&var13); + // 685 : 41812277610096037423121415467664379452791826572768737097645145278946073092689015942828710628302242631453906226746554787316033414421846346623392985336587064417084904426156639616 + square!(var1, 7); + // 692 : 41812277610096037423121415467664379452791826572768737097645145278946073092689015942828710628302242631453906226746554787316033414421846346623392985336587064417084904426156639627 + var1.mul_assign(&var13); + // 693 : 2675985767046146395079770589930520284978676900657199174249289297852548677932097020341037480211343528413049998511779506388226138522998166183897151061541572122693433883274024936128 + square!(var1, 6); + // 699 : 2675985767046146395079770589930520284978676900657199174249289297852548677932097020341037480211343528413049998511779506388226138522998166183897151061541572122693433883274024936131 + var1.mul_assign(&var2); + // 700 : 85631544545476684642552658877776649119317660821030373575977257531281557693827104650913199366762992909217599952376944204423236432735941317884708833969330307926189884264768797956192 + square!(var1, 5); + // 705 : 85631544545476684642552658877776649119317660821030373575977257531281557693827104650913199366762992909217599952376944204423236432735941317884708833969330307926189884264768797956199 + var1.mul_assign(&var3); + // 706 : 87686701614568125073973922690843288698181284680735102541800711712032315078478955162535116151565304739038822351233990865329394107121603909513941845984594235316418441487123249107147776 + square!(var1, 10); + // 716 : 87686701614568125073973922690843288698181284680735102541800711712032315078478955162535116151565304739038822351233990865329394107121603909513941845984594235316418441487123249107147803 + var1.mul_assign(&var12); + // 717 : 1402987225833090001183582763053492619170900554891761640668811387392517041255663282600561858425044875824621157619743853845270305713945662552223069535753507765062695063793971985714364848 + square!(var1, 4); + // 721 : 1402987225833090001183582763053492619170900554891761640668811387392517041255663282600561858425044875824621157619743853845270305713945662552223069535753507765062695063793971985714364849 + var1.mul_assign(var0); + // 722 : 718329459626542080605994374683388221015501084104581960022431430344968725122899600691487671513622976422206032701308853168778396525540179226738211602305795975712099872662513656685754802688 + square!(var1, 9); + // 731 : 718329459626542080605994374683388221015501084104581960022431430344968725122899600691487671513622976422206032701308853168778396525540179226738211602305795975712099872662513656685754802705 + var1.mul_assign(&var9); + // 732 : 45973085416098693158783639979736846144992069382693245441435611542077998407865574444255210976871870491021186092883766602801817377634571470511245542547570942445574391850400874027888307373120 + square!(var1, 6); + // 738 : 45973085416098693158783639979736846144992069382693245441435611542077998407865574444255210976871870491021186092883766602801817377634571470511245542547570942445574391850400874027888307373135 + var1.mul_assign(&var10); + // 739 : 5884554933260632724324305917406316306558984880984735416503758277385983796206793528864667005039599422850711819889122125158632624337225148225439429446089080633033522156851311875569703343761280 + square!(var1, 7); + // 746 : 5884554933260632724324305917406316306558984880984735416503758277385983796206793528864667005039599422850711819889122125158632624337225148225439429446089080633033522156851311875569703343761311 + var1.mul_assign(&var11); + // 747 : 188305757864340247178377789357002121809887516191511533328120264876351481478617392923669344161267181531222778236451908005076243978791204743214061742274850580257072709019241980018230507000361952 + square!(var1, 5); + // 752 : 188305757864340247178377789357002121809887516191511533328120264876351481478617392923669344161267181531222778236451908005076243978791204743214061742274850580257072709019241980018230507000361973 + var1.mul_assign(&var4); + // 753 : 3012892125829443954854044629712033948958200259064184533249924238021623703657878286778709506580274904499564451783230528081219903660659275891424987876397609284113163344307871680291688112005791568 + square!(var1, 4); + // 757 : 3012892125829443954854044629712033948958200259064184533249924238021623703657878286778709506580274904499564451783230528081219903660659275891424987876397609284113163344307871680291688112005791583 + var1.mul_assign(&var10); + // 758 : 385650192106168826221317712603140345466649633160215620255990302466767834068208420707674816842275187775944249828253507594396147668564387314102398448178893988366484908071407575077336078336741322624 + square!(var1, 7); + // 765 : 385650192106168826221317712603140345466649633160215620255990302466767834068208420707674816842275187775944249828253507594396147668564387314102398448178893988366484908071407575077336078336741322653 + var1.mul_assign(&var8); + // 766 : 12340806147397402439082166803300491054932788261126899848191689678936570690182669462645594138952806008830215994504112243020676725394060394051276750341724607627727517058285042402474754506775722324896 + square!(var1, 5); + // 771 : 12340806147397402439082166803300491054932788261126899848191689678936570690182669462645594138952806008830215994504112243020676725394060394051276750341724607627727517058285042402474754506775722324917 + var1.mul_assign(&var4); + // 772 : 394905796716716878050629337705615713757849224356060795142134069725970262085845422804659012446489792282566911824131591776661655212609932609640856010935187444087280545865121356879192144216823114397344 + square!(var1, 5); + // 777 : 394905796716716878050629337705615713757849224356060795142134069725970262085845422804659012446489792282566911824131591776661655212609932609640856010935187444087280545865121356879192144216823114397365 + var1.mul_assign(&var4); + // 778 : 12636985494934940097620138806579702840251175179393945444548290231231048386747053529749088398287673353042141178372210936853172966803517843508507392349925998210792977467683883420134148614938339660715680 + square!(var1, 5); + // 783 : 12636985494934940097620138806579702840251175179393945444548290231231048386747053529749088398287673353042141178372210936853172966803517843508507392349925998210792977467683883420134148614938339660715697 + var1.mul_assign(&var9); + // 784 : 202191767918959041561922220905275245444018802870303127112772643699696774187952856475985414372602773648674258853955374989650767468856285496136118277598815971372687639482942134722146377839013434571451152 + square!(var1, 4); + // 788 : 202191767918959041561922220905275245444018802870303127112772643699696774187952856475985414372602773648674258853955374989650767468856285496136118277598815971372687639482942134722146377839013434571451165 + var1.mul_assign(&var5); + // 789 : 12940273146813378659963022137937615708417203383699400135217449196780593548028982814463066519846577513515152566653143999337649118006802271752711569766324222167852008926908296622217368181696859812572874560 + square!(var1, 6); + // 795 : 12940273146813378659963022137937615708417203383699400135217449196780593548028982814463066519846577513515152566653143999337649118006802271752711569766324222167852008926908296622217368181696859812572874589 + var1.mul_assign(&var8); + // 796 : 25880546293626757319926044275875231416834406767398800270434898393561187096057965628926133039693155027030305133306287998675298236013604543505423139532648444335704017853816593244434736363393719625145749178 + var1 = var1.square(); + // 797 : 25880546293626757319926044275875231416834406767398800270434898393561187096057965628926133039693155027030305133306287998675298236013604543505423139532648444335704017853816593244434736363393719625145749179 + var1.mul_assign(var0); + // 798 : 1656354962792112468475266833656014810677402033113523217307833497187915974147709800251272514540361921729939528531602431915219087104870690784347080930089500437485057142644261967643823127257198056009327947456 + square!(var1, 6); + // 804 : 1656354962792112468475266833656014810677402033113523217307833497187915974147709800251272514540361921729939528531602431915219087104870690784347080930089500437485057142644261967643823127257198056009327947463 + var1.mul_assign(&var3); + // 805 : 1696107481899123167718673237663759166133659681908247774523221501120425957527254835457303054889330607851458077216360890281184345195387587363171410872411648447984698514067724254867274882311370809353551818202112 + square!(var1, 10); + // 815 : 1696107481899123167718673237663759166133659681908247774523221501120425957527254835457303054889330607851458077216360890281184345195387587363171410872411648447984698514067724254867274882311370809353551818202135 + var1.mul_assign(&var7); + // 816 : 108550878841543882733995087210480586632554219642127857569486176071707261281744309469267395512917158902493316941847096977995798092504805591242970295834345500671020704900334352311505592467927731798627316364936640 + square!(var1, 6); + // 822 : 108550878841543882733995087210480586632554219642127857569486176071707261281744309469267395512917158902493316941847096977995798092504805591242970295834345500671020704900334352311505592467927731798627316364936661 + var1.mul_assign(&var4); + // 823 : 6947256245858808494975685581470757544483470057096182884447115268589264722031635806033113312826698169759572284278214206591731077920307557839550098933398112042945325113621398547936357917947374835112148247355946304 + square!(var1, 6); + // 829 : 6947256245858808494975685581470757544483470057096182884447115268589264722031635806033113312826698169759572284278214206591731077920307557839550098933398112042945325113621398547936357917947374835112148247355946329 + var1.mul_assign(&var6); + // 830 : 444624399734963743678443877214128482846942083654155704604615377189712942210024691586119252020908682864612626193805709221870788986899683701731206331737479170748500807271769507067926906748631989447177487830780565056 + square!(var1, 6); + // 836 : 444624399734963743678443877214128482846942083654155704604615377189712942210024691586119252020908682864612626193805709221870788986899683701731206331737479170748500807271769507067926906748631989447177487830780565069 + var1.mul_assign(&var5); + // 837 : 28455961583037679595420408141704222902204293353865965094695384140141628301441580261511632129338155703335208076403565390199730495161579756910797205231198666927904051665393248452347322031912447324619359221169956164416 + square!(var1, 6); + // 843 : 28455961583037679595420408141704222902204293353865965094695384140141628301441580261511632129338155703335208076403565390199730495161579756910797205231198666927904051665393248452347322031912447324619359221169956164437 + var1.mul_assign(&var4); + // 844 : 238705906983162543355580399100765177871214152862586865721082456961065184302499251714358569373223087638243353151383559860752580829556389241459968722180074986980751351032731127113348364375477010926880553717580063640645533696 + square!(var1, 23); + // 867 : 238705906983162543355580399100765177871214152862586865721082456961065184302499251714358569373223087638243353151383559860752580829556389241459968722180074986980751351032731127113348364375477010926880553717580063640645533703 + var1.mul_assign(&var3); + // 868 : 15277178046922402774757145542448971383757705783205559406149277245508171795359952109718948439886277608847574601688547831088165173091608911453437998219524799166768086466094792135254295320030528699320355437925124073001314156992 + square!(var1, 6); + // 874 : 15277178046922402774757145542448971383757705783205559406149277245508171795359952109718948439886277608847574601688547831088165173091608911453437998219524799166768086466094792135254295320030528699320355437925124073001314156999 + var1.mul_assign(&var3); + // 875 : 488869697501516888792228657358367084280246585062577900996776871856261497451518467511006350076360883483122387254033530594821285538931485166510015943024793573336578766915033348328137450240976918378251374013603970336042053023968 + square!(var1, 5); + // 880 : 488869697501516888792228657358367084280246585062577900996776871856261497451518467511006350076360883483122387254033530594821285538931485166510015943024793573336578766915033348328137450240976918378251374013603970336042053023971 + var1.mul_assign(&var2); + // 881 : 31287660640097080882702634070935493393935781444004985663793719798800735836897181920704406404887096542919832784258145958068562274491615050656641020353586788693541041082562134293000796815422522776208087936870654101506691393534144 + square!(var1, 6); + // 887 : 31287660640097080882702634070935493393935781444004985663793719798800735836897181920704406404887096542919832784258145958068562274491615050656641020353586788693541041082562134293000796815422522776208087936870654101506691393534151 + var1.mul_assign(&var3); + // 888 : 1001205140483106588246484290269935788605945006208159541241399033561623546780709821462541004956387089373434649096260670658193992783731681621012512651314777238193313314641988297376025498093520728838658813979860931248214124593092832 + square!(var1, 5); + // 893 : 1001205140483106588246484290269935788605945006208159541241399033561623546780709821462541004956387089373434649096260670658193992783731681621012512651314777238193313314641988297376025498093520728838658813979860931248214124593092835 + var1 * var2 +} + +/// Tests for addition chains +#[cfg(test)] +mod tests { + use super::*; + use rand_core::SeedableRng; + + const SEED: [u8; 16] = [ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]; + + #[test] + fn test_fp_chain() { + let mut rng = rand_xorshift::XorShiftRng::from_seed(SEED); + let p_m3_over4 = [ + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]; + + for _ in 0..32 { + let input = Fp::random(&mut rng); + assert_eq!(chain_pm3div4(&input), input.pow_vartime(&p_m3_over4)); + } + } + + #[test] + fn test_fp2_chain() { + let mut rng = rand_xorshift::XorShiftRng::from_seed(SEED); + let p_sq_m9_over16 = [ + 0xb26a_a000_01c7_18e3, + 0xd7ce_d6b1_d763_82ea, + 0x3162_c338_3621_13cf, + 0x966b_f91e_d3e7_1b74, + 0xb292_e85a_8709_1a04, + 0x11d6_8619_c861_85c7, + 0xef53_1493_3097_8ef0, + 0x050a_62cf_d16d_dca6, + 0x466e_59e4_9349_e8bd, + 0x9e2d_c90e_50e7_046b, + 0x74bd_278e_aa22_f25e, + 0x002a_437a_4b8c_35fc, + ]; + + for _ in 0..32 { + let input = Fp2::random(&mut rng); + assert_eq!( + chain_p2m9div16(&input), + input.pow_vartime_extended(&p_sq_m9_over16[..]), + ); + } + } +} diff --git a/src/bls12_381/hash_to_curve/expand_msg.rs b/src/bls12_381/hash_to_curve/expand_msg.rs new file mode 100644 index 00000000..2f0fbf24 --- /dev/null +++ b/src/bls12_381/hash_to_curve/expand_msg.rs @@ -0,0 +1,1289 @@ +//! This module implements message expansion consistent with the +//! hash-to-curve RFC drafts 7 through 10 +//! +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use core::{ + fmt::{self, Debug, Formatter}, + marker::PhantomData, +}; + +use digest::{crypto_common::BlockSizeUser, Digest, ExtendableOutput, Update, XofReader}; + +use crate::bls12_381::generic_array::{ + typenum::{Unsigned, U32}, + ArrayLength, GenericArray, +}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +const OVERSIZE_DST_SALT: &[u8] = b"H2C-OVERSIZE-DST-"; + +/// The domain separation tag for a message expansion. +/// +/// Implements [section 5.4.3 of `draft-irtf-cfrg-hash-to-curve-12`][dst]. +/// +/// [dst]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-12#section-5.4.3 +#[derive(Debug)] +enum ExpandMsgDst<'x, L: ArrayLength> { + /// DST produced by hashing a very long (> 255 chars) input DST. + Hashed(GenericArray), + /// A raw input DST (<= 255 chars). + Raw(&'x [u8]), +} + +impl<'x, L: ArrayLength> ExpandMsgDst<'x, L> { + /// Produces a DST for use with `expand_message_xof`. + pub fn process_xof(dst: &'x [u8]) -> Self + where + H: Default + Update + ExtendableOutput, + { + if dst.len() > 255 { + let mut data = GenericArray::::default(); + H::default() + .chain(OVERSIZE_DST_SALT) + .chain(dst) + .finalize_xof() + .read(&mut data); + Self::Hashed(data) + } else { + Self::Raw(dst) + } + } + + /// Produces a DST for use with `expand_message_xmd`. + pub fn process_xmd(dst: &'x [u8]) -> Self + where + H: Digest, + { + if dst.len() > 255 { + Self::Hashed( + H::new() + .chain_update(OVERSIZE_DST_SALT) + .chain_update(dst) + .finalize(), + ) + } else { + Self::Raw(dst) + } + } + + /// Returns the raw bytes of the DST. + pub fn data(&'x self) -> &'x [u8] { + match self { + Self::Hashed(arr) => &arr[..], + Self::Raw(buf) => buf, + } + } + + /// Returns the length of the DST. + pub fn len(&'x self) -> usize { + match self { + Self::Hashed(_) => L::to_usize(), + Self::Raw(buf) => buf.len(), + } + } +} + +/// A trait for message expansion methods supported by hash-to-curve. +pub trait ExpandMessage: for<'x> InitExpandMessage<'x> { + // This intermediate is likely only necessary until GATs allow + // associated types with lifetimes. +} + +/// Trait for constructing a new message expander. +pub trait InitExpandMessage<'x> { + /// The state object used during message expansion. + type Expander: ExpandMessageState<'x>; + + /// Initializes a message expander. + fn init_expand(message: &[u8], dst: &'x [u8], len_in_bytes: usize) -> Self::Expander; +} + +// Automatically derive trait +impl InitExpandMessage<'x>> ExpandMessage for X {} + +/// Trait for types implementing the `expand_message` interface for `hash_to_field`. +pub trait ExpandMessageState<'x> { + /// Reads bytes from the generated output. + fn read_into(&mut self, output: &mut [u8]) -> usize; + + /// Retrieves the number of bytes remaining in the generator. + fn remain(&self) -> usize; + + #[cfg(feature = "alloc")] + /// Constructs a `Vec` containing the remaining bytes of the output. + fn into_vec(mut self) -> Vec + where + Self: Sized, + { + let mut result = alloc::vec![0u8; self.remain()]; + self.read_into(&mut result[..]); + result + } +} + +/// A generator for the output of `expand_message_xof` for a given +/// extendable hash function, message, DST, and output length. +/// +/// Implements [section 5.4.2 of `draft-irtf-cfrg-hash-to-curve-12`][expand_message_xof] +/// with `k = 128`. +/// +/// [expand_message_xof]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-12#section-5.4.2 +pub struct ExpandMsgXof { + hash: ::Reader, + remain: usize, +} + +impl Debug for ExpandMsgXof { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ExpandMsgXof") + .field("remain", &self.remain) + .finish() + } +} + +impl<'x, H> ExpandMessageState<'x> for ExpandMsgXof +where + H: ExtendableOutput, +{ + fn read_into(&mut self, output: &mut [u8]) -> usize { + let len = self.remain.min(output.len()); + self.hash.read(&mut output[..len]); + self.remain -= len; + len + } + + fn remain(&self) -> usize { + self.remain + } +} + +impl<'x, H> InitExpandMessage<'x> for ExpandMsgXof +where + H: Default + Update + ExtendableOutput, +{ + type Expander = Self; + + fn init_expand(message: &[u8], dst: &[u8], len_in_bytes: usize) -> Self { + // Use U32 here for k = 128. + let dst = ExpandMsgDst::::process_xof::(dst); + let hash = H::default() + .chain(message) + .chain((len_in_bytes as u16).to_be_bytes()) + .chain(dst.data()) + .chain([dst.len() as u8]) + .finalize_xof(); + Self { + hash, + remain: len_in_bytes, + } + } +} + +/// Constructor for `expand_message_xmd` for a given digest hash function, message, DST, +/// and output length. +/// +/// Implements [section 5.4.1 of `draft-irtf-cfrg-hash-to-curve-12`][expand_message_xmd]. +/// +/// [expand_message_xmd]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-12#section-5.4.1 +#[derive(Debug)] +pub struct ExpandMsgXmd(PhantomData); + +/// A generator for the output of `expand_message_xmd` for a given +/// digest hash function, message, DST, and output length. +/// +/// Implements [section 5.4.1 of `draft-irtf-cfrg-hash-to-curve-12`][expand_message_xmd]. +/// +/// [expand_message_xmd]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-12#section-5.4.1 +pub struct ExpandMsgXmdState<'x, H: Digest> { + dst: ExpandMsgDst<'x, H::OutputSize>, + b_0: GenericArray, + b_i: GenericArray, + i: usize, + b_offs: usize, + remain: usize, +} + +impl Debug for ExpandMsgXmdState<'_, H> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ExpandMsgXmdState") + .field("remain", &self.remain) + .finish() + } +} + +impl<'x, H> InitExpandMessage<'x> for ExpandMsgXmd +where + H: Digest + BlockSizeUser, +{ + type Expander = ExpandMsgXmdState<'x, H>; + + fn init_expand(message: &[u8], dst: &'x [u8], len_in_bytes: usize) -> Self::Expander { + let hash_size = ::output_size(); + let ell = (len_in_bytes + hash_size - 1) / hash_size; + if ell > 255 { + panic!("Invalid ExpandMsgXmd usage: ell > 255"); + } + let dst = ExpandMsgDst::process_xmd::(dst); + let b_0 = H::new() + .chain_update(GenericArray::::BlockSize>::default()) + .chain_update(message) + .chain_update((len_in_bytes as u16).to_be_bytes()) + .chain_update([0u8]) + .chain_update(dst.data()) + .chain_update([dst.len() as u8]) + .finalize(); + // init with b_1 + let b_i = H::new() + .chain_update(&b_0) + .chain_update([1u8]) + .chain_update(dst.data()) + .chain_update([dst.len() as u8]) + .finalize(); + ExpandMsgXmdState { + dst, + b_0, + b_i, + i: 2, + b_offs: 0, + remain: len_in_bytes, + } + } +} + +impl<'x, H> ExpandMessageState<'x> for ExpandMsgXmdState<'x, H> +where + H: Digest, +{ + fn read_into(&mut self, output: &mut [u8]) -> usize { + let read_len = self.remain.min(output.len()); + let mut offs = 0; + let hash_size = H::OutputSize::to_usize(); + while offs < read_len { + let b_offs = self.b_offs; + let mut copy_len = hash_size - b_offs; + if copy_len > 0 { + copy_len = copy_len.min(read_len - offs); + output[offs..(offs + copy_len)] + .copy_from_slice(&self.b_i[b_offs..(b_offs + copy_len)]); + offs += copy_len; + self.b_offs = b_offs + copy_len; + } else { + let mut b_prev_xor = self.b_0.clone(); + for j in 0..hash_size { + b_prev_xor[j] ^= self.b_i[j]; + } + self.b_i = H::new() + .chain_update(b_prev_xor) + .chain_update([self.i as u8]) + .chain_update(self.dst.data()) + .chain_update([self.dst.len() as u8]) + .finalize(); + self.b_offs = 0; + self.i += 1; + } + } + self.remain -= read_len; + read_len + } + + fn remain(&self) -> usize { + self.remain + } +} + +#[cfg(feature = "alloc")] +#[cfg(test)] +mod tests { + use super::*; + use sha2::{Sha256, Sha512}; + use sha3::{Shake128, Shake256}; + + /// From + #[test] + fn expand_message_xmd_works_for_draft12_testvectors_sha256() { + let dst = b"QUUX-V01-CS02-with-expander-SHA256-128"; + + let msg = b""; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "68a985b87eb6b46952128911f2a4412bbc302a9d759667f8\ + 7f7a21d803f07235", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "d8ccab23b5985ccea865c6c97b6e5b8350e794e603b4b979\ + 02f53a8a0d605615", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "eff31487c770a893cfb36f912fbfcbff40d5661771ca4b2c\ + b4eafe524333f5c1", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "b23a1d2b4d97b2ef7785562a7e8bac7eed54ed6e97e29aa5\ + 1bfe3f12ddad1ff9", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "4623227bcc01293b8c130bf771da8c298dede7383243dc09\ + 93d2d94823958c4c", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b""; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "af84c27ccfd45d41914fdff5df25293e221afc53d8ad2ac0\ + 6d5e3e29485dadbee0d121587713a3e0dd4d5e69e93eb7cd4f5df4\ + cd103e188cf60cb02edc3edf18eda8576c412b18ffb658e3dd6ec8\ + 49469b979d444cf7b26911a08e63cf31f9dcc541708d3491184472\ + c2c29bb749d4286b004ceb5ee6b9a7fa5b646c993f0ced", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "abba86a6129e366fc877aab32fc4ffc70120d8996c88aee2\ + fe4b32d6c7b6437a647e6c3163d40b76a73cf6a5674ef1d890f95b\ + 664ee0afa5359a5c4e07985635bbecbac65d747d3d2da7ec2b8221\ + b17b0ca9dc8a1ac1c07ea6a1e60583e2cb00058e77b7b72a298425\ + cd1b941ad4ec65e8afc50303a22c0f99b0509b4c895f40", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "ef904a29bffc4cf9ee82832451c946ac3c8f8058ae97d8d6\ + 29831a74c6572bd9ebd0df635cd1f208e2038e760c4994984ce73f\ + 0d55ea9f22af83ba4734569d4bc95e18350f740c07eef653cbb9f8\ + 7910d833751825f0ebefa1abe5420bb52be14cf489b37fe1a72f7d\ + e2d10be453b2c9d9eb20c7e3f6edc5a60629178d9478df", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "80be107d0884f0d881bb460322f0443d38bd222db8bd0b0a\ + 5312a6fedb49c1bbd88fd75d8b9a09486c60123dfa1d73c1cc3169\ + 761b17476d3c6b7cbbd727acd0e2c942f4dd96ae3da5de368d26b3\ + 2286e32de7e5a8cb2949f866a0b80c58116b29fa7fabb3ea7d520e\ + e603e0c25bcaf0b9a5e92ec6a1fe4e0391d1cdbce8c68a", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "546aff5444b5b79aa6148bd81728704c32decb73a3ba76e9\ + e75885cad9def1d06d6792f8a7d12794e90efed817d96920d72889\ + 6a4510864370c207f99bd4a608ea121700ef01ed879745ee3e4cee\ + f777eda6d9e5e38b90c86ea6fb0b36504ba4a45d22e86f6db5dd43\ + d98a294bebb9125d5b794e9d2a81181066eb954966a487", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + } + + /// From + #[test] + fn expand_message_xmd_works_for_draft12_testvectors_sha256_long_dst() { + let dst = b"QUUX-V01-CS02-with-expander-SHA256-128-long-DST-111111\ + 111111111111111111111111111111111111111111111111111111\ + 111111111111111111111111111111111111111111111111111111\ + 111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111"; + + let msg = b""; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "e8dc0c8b686b7ef2074086fbdd2f30e3f8bfbd3bdf177f73\ + f04b97ce618a3ed3", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "52dbf4f36cf560fca57dedec2ad924ee9c266341d8f3d6af\ + e5171733b16bbb12", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "35387dcf22618f3728e6c686490f8b431f76550b0b2c61cb\ + c1ce7001536f4521", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "01b637612bb18e840028be900a833a74414140dde0c4754c\ + 198532c3a0ba42bc", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "20cce7033cabc5460743180be6fa8aac5a103f56d481cf36\ + 9a8accc0c374431b", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b""; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "14604d85432c68b757e485c8894db3117992fc57e0e136f7\ + 1ad987f789a0abc287c47876978e2388a02af86b1e8d1342e5ce4f\ + 7aaa07a87321e691f6fba7e0072eecc1218aebb89fb14a0662322d\ + 5edbd873f0eb35260145cd4e64f748c5dfe60567e126604bcab1a3\ + ee2dc0778102ae8a5cfd1429ebc0fa6bf1a53c36f55dfc", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "1a30a5e36fbdb87077552b9d18b9f0aee16e80181d5b951d\ + 0471d55b66684914aef87dbb3626eaabf5ded8cd0686567e503853\ + e5c84c259ba0efc37f71c839da2129fe81afdaec7fbdc0ccd4c794\ + 727a17c0d20ff0ea55e1389d6982d1241cb8d165762dbc39fb0cee\ + 4474d2cbbd468a835ae5b2f20e4f959f56ab24cd6fe267", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "d2ecef3635d2397f34a9f86438d772db19ffe9924e28a1ca\ + f6f1c8f15603d4028f40891044e5c7e39ebb9b31339979ff33a424\ + 9206f67d4a1e7c765410bcd249ad78d407e303675918f20f26ce6d\ + 7027ed3774512ef5b00d816e51bfcc96c3539601fa48ef1c07e494\ + bdc37054ba96ecb9dbd666417e3de289d4f424f502a982", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "ed6e8c036df90111410431431a232d41a32c86e296c05d42\ + 6e5f44e75b9a50d335b2412bc6c91e0a6dc131de09c43110d9180d\ + 0a70f0d6289cb4e43b05f7ee5e9b3f42a1fad0f31bac6a625b3b5c\ + 50e3a83316783b649e5ecc9d3b1d9471cb5024b7ccf40d41d1751a\ + 04ca0356548bc6e703fca02ab521b505e8e45600508d32", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "78b53f2413f3c688f07732c10e5ced29a17c6a16f717179f\ + fbe38d92d6c9ec296502eb9889af83a1928cd162e845b0d3c5424e\ + 83280fed3d10cffb2f8431f14e7a23f4c68819d40617589e4c4116\ + 9d0b56e0e3535be1fd71fbb08bb70c5b5ffed953d6c14bf7618b35\ + fc1f4c4b30538236b4b08c9fbf90462447a8ada60be495", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + } + + /// From + #[test] + fn expand_message_xmd_works_for_draft12_testvectors_sha512() { + let dst = b"QUUX-V01-CS02-with-expander-SHA512-256"; + + let msg = b""; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "6b9a7312411d92f921c6f68ca0b6380730a1a4d982c50721\ + 1a90964c394179ba", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "0da749f12fbe5483eb066a5f595055679b976e93abe9be6f\ + 0f6318bce7aca8dc", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "087e45a86e2939ee8b91100af1583c4938e0f5fc6c9db4b1\ + 07b83346bc967f58", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "7336234ee9983902440f6bc35b348352013becd88938d2af\ + ec44311caf8356b3", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "57b5f7e766d5be68a6bfe1768e3c2b7f1228b3e4b3134956\ + dd73a59b954c66f4", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b""; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "41b037d1734a5f8df225dd8c7de38f851efdb45c372887be\ + 655212d07251b921b052b62eaed99b46f72f2ef4cc96bfaf254ebb\ + bec091e1a3b9e4fb5e5b619d2e0c5414800a1d882b62bb5cd1778f\ + 098b8eb6cb399d5d9d18f5d5842cf5d13d7eb00a7cff859b605da6\ + 78b318bd0e65ebff70bec88c753b159a805d2c89c55961", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "7f1dddd13c08b543f2e2037b14cefb255b44c83cc397c178\ + 6d975653e36a6b11bdd7732d8b38adb4a0edc26a0cef4bb4521713\ + 5456e58fbca1703cd6032cb1347ee720b87972d63fbf232587043e\ + d2901bce7f22610c0419751c065922b488431851041310ad659e4b\ + 23520e1772ab29dcdeb2002222a363f0c2b1c972b3efe1", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "3f721f208e6199fe903545abc26c837ce59ac6fa45733f1b\ + aaf0222f8b7acb0424814fcb5eecf6c1d38f06e9d0a6ccfbf85ae6\ + 12ab8735dfdf9ce84c372a77c8f9e1c1e952c3a61b7567dd069301\ + 6af51d2745822663d0c2367e3f4f0bed827feecc2aaf98c949b5ed\ + 0d35c3f1023d64ad1407924288d366ea159f46287e61ac", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "b799b045a58c8d2b4334cf54b78260b45eec544f9f2fb5bd\ + 12fb603eaee70db7317bf807c406e26373922b7b8920fa29142703\ + dd52bdf280084fb7ef69da78afdf80b3586395b433dc66cde048a2\ + 58e476a561e9deba7060af40adf30c64249ca7ddea79806ee5beb9\ + a1422949471d267b21bc88e688e4014087a0b592b695ed", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "05b0bfef265dcee87654372777b7c44177e2ae4c13a27f10\ + 3340d9cd11c86cb2426ffcad5bd964080c2aee97f03be1ca18e30a\ + 1f14e27bc11ebbd650f305269cc9fb1db08bf90bfc79b42a952b46\ + daf810359e7bc36452684784a64952c343c52e5124cd1f71d474d5\ + 197fefc571a92929c9084ffe1112cf5eea5192ebff330b", + ) + .unwrap(); + assert_eq!( + ExpandMsgXmd::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + } + + /// From + #[test] + fn expand_message_xof_works_for_draft12_testvectors_shake128() { + let dst = b"QUUX-V01-CS02-with-expander-SHAKE128"; + + let msg = b""; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "86518c9cd86581486e9485aa74ab35ba150d1c75c88e26b7\ + 043e44e2acd735a2", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "8696af52a4d862417c0763556073f47bc9b9ba43c99b5053\ + 05cb1ec04a9ab468", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "912c58deac4821c3509dbefa094df54b34b8f5d01a191d1d\ + 3108a2c89077acca", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "1adbcc448aef2a0cebc71dac9f756b22e51839d348e031e6\ + 3b33ebb50faeaf3f", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "df3447cc5f3e9a77da10f819218ddf31342c310778e0e4ef\ + 72bbaecee786a4fe", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b""; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "7314ff1a155a2fb99a0171dc71b89ab6e3b2b7d59e38e644\ + 19b8b6294d03ffee42491f11370261f436220ef787f8f76f5b26bd\ + cd850071920ce023f3ac46847744f4612b8714db8f5db83205b2e6\ + 25d95afd7d7b4d3094d3bdde815f52850bb41ead9822e08f22cf41\ + d615a303b0d9dde73263c049a7b9898208003a739a2e57", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "c952f0c8e529ca8824acc6a4cab0e782fc3648c563ddb00d\ + a7399f2ae35654f4860ec671db2356ba7baa55a34a9d7f79197b60\ + ddae6e64768a37d699a78323496db3878c8d64d909d0f8a7de4927\ + dcab0d3dbbc26cb20a49eceb0530b431cdf47bc8c0fa3e0d88f53b\ + 318b6739fbed7d7634974f1b5c386d6230c76260d5337a", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "19b65ee7afec6ac06a144f2d6134f08eeec185f1a890fe34\ + e68f0e377b7d0312883c048d9b8a1d6ecc3b541cb4987c26f45e0c\ + 82691ea299b5e6889bbfe589153016d8131717ba26f07c3c14ffbe\ + f1f3eff9752e5b6183f43871a78219a75e7000fbac6a7072e2b83c\ + 790a3a5aecd9d14be79f9fd4fb180960a3772e08680495", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "ca1b56861482b16eae0f4a26212112362fcc2d76dcc80c93\ + c4182ed66c5113fe41733ed68be2942a3487394317f3379856f482\ + 2a611735e50528a60e7ade8ec8c71670fec6661e2c59a09ed36386\ + 513221688b35dc47e3c3111ee8c67ff49579089d661caa29db1ef1\ + 0eb6eace575bf3dc9806e7c4016bd50f3c0e2a6481ee6d", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "9d763a5ce58f65c91531b4100c7266d479a5d9777ba76169\ + 3d052acd37d149e7ac91c796a10b919cd74a591a1e38719fb91b72\ + 03e2af31eac3bff7ead2c195af7d88b8bc0a8adf3d1e90ab9bed6d\ + dc2b7f655dd86c730bdeaea884e73741097142c92f0e3fc1811b69\ + 9ba593c7fbd81da288a29d423df831652e3a01a9374999", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + } + + /// From + #[test] + fn expand_message_xof_works_for_draft12_testvectors_shake128_long_dst() { + let dst = b"QUUX-V01-CS02-with-expander-SHAKE128-long-DST-11111111\ + 111111111111111111111111111111111111111111111111111111\ + 111111111111111111111111111111111111111111111111111111\ + 111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111"; + + let msg = b""; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "827c6216330a122352312bccc0c8d6e7a146c5257a776dbd\ + 9ad9d75cd880fc53", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "690c8d82c7213b4282c6cb41c00e31ea1d3e2005f93ad19b\ + bf6da40f15790c5c", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "979e3a15064afbbcf99f62cc09fa9c85028afcf3f825eb07\ + 11894dcfc2f57057", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "c5a9220962d9edc212c063f4f65b609755a1ed96e62f9db5\ + d1fd6adb5a8dc52b", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "f7b96a5901af5d78ce1d071d9c383cac66a1dfadb508300e\ + c6aeaea0d62d5d62", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b""; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "3890dbab00a2830be398524b71c2713bbef5f4884ac2e6f0\ + 70b092effdb19208c7df943dc5dcbaee3094a78c267ef276632ee2\ + c8ea0c05363c94b6348500fae4208345dd3475fe0c834c2beac7fa\ + 7bc181692fb728c0a53d809fc8111495222ce0f38468b11becb15b\ + 32060218e285c57a60162c2c8bb5b6bded13973cd41819", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "41b7ffa7a301b5c1441495ebb9774e2a53dbbf4e54b9a1af\ + 6a20fd41eafd69ef7b9418599c5545b1ee422f363642b01d4a5344\ + 9313f68da3e49dddb9cd25b97465170537d45dcbdf92391b5bdff3\ + 44db4bd06311a05bca7dcd360b6caec849c299133e5c9194f4e15e\ + 3e23cfaab4003fab776f6ac0bfae9144c6e2e1c62e7d57", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "55317e4a21318472cd2290c3082957e1242241d9e0d04f47\ + 026f03401643131401071f01aa03038b2783e795bdfa8a3541c194\ + ad5de7cb9c225133e24af6c86e748deb52e560569bd54ef4dac034\ + 65111a3a44b0ea490fb36777ff8ea9f1a8a3e8e0de3cf0880b4b2f\ + 8dd37d3a85a8b82375aee4fa0e909f9763319b55778e71", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "19fdd2639f082e31c77717ac9bb032a22ff0958382b2dbb3\ + 9020cdc78f0da43305414806abf9a561cb2d0067eb2f7bc544482f\ + 75623438ed4b4e39dd9e6e2909dd858bd8f1d57cd0fce2d3150d90\ + aa67b4498bdf2df98c0100dd1a173436ba5d0df6be1defb0b2ce55\ + ccd2f4fc05eb7cb2c019c35d5398b85adc676da4238bc7", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "945373f0b3431a103333ba6a0a34f1efab2702efde41754c\ + 4cb1d5216d5b0a92a67458d968562bde7fa6310a83f53dda138368\ + 0a276a283438d58ceebfa7ab7ba72499d4a3eddc860595f63c93b1\ + c5e823ea41fc490d938398a26db28f61857698553e93f0574eb8c5\ + 017bfed6249491f9976aaa8d23d9485339cc85ca329308", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + } + + /// From + #[test] + fn expand_message_xof_works_for_draft12_testvectors_shake256() { + let dst = b"QUUX-V01-CS02-with-expander-SHAKE256"; + + let msg = b""; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "2ffc05c48ed32b95d72e807f6eab9f7530dd1c2f013914c8\ + fed38c5ccc15ad76", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "b39e493867e2767216792abce1f2676c197c0692aed06156\ + 0ead251821808e07", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "245389cf44a13f0e70af8665fe5337ec2dcd138890bb7901\ + c4ad9cfceb054b65", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "719b3911821e6428a5ed9b8e600f2866bcf23c8f0515e52d\ + 6c6c019a03f16f0e", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x20; + let uniform_bytes = hex::decode( + "9181ead5220b1963f1b5951f35547a5ea86a820562287d6c\ + a4723633d17ccbbc", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b""; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "7a1361d2d7d82d79e035b8880c5a3c86c5afa719478c007d\ + 96e6c88737a3f631dd74a2c88df79a4cb5e5d9f7504957c70d669e\ + c6bfedc31e01e2bacc4ff3fdf9b6a00b17cc18d9d72ace7d6b81c2\ + e481b4f73f34f9a7505dccbe8f5485f3d20c5409b0310093d5d649\ + 2dea4e18aa6979c23c8ea5de01582e9689612afbb353df", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abc"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "a54303e6b172909783353ab05ef08dd435a558c3197db0c1\ + 32134649708e0b9b4e34fb99b92a9e9e28fc1f1d8860d85897a8e0\ + 21e6382f3eea10577f968ff6df6c45fe624ce65ca25932f679a42a\ + 404bc3681efe03fcd45ef73bb3a8f79ba784f80f55ea8a3c367408\ + f30381299617f50c8cf8fbb21d0f1e1d70b0131a7b6fbe", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"abcdef0123456789"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "e42e4d9538a189316e3154b821c1bafb390f78b2f010ea40\ + 4e6ac063deb8c0852fcd412e098e231e43427bd2be1330bb47b403\ + 9ad57b30ae1fc94e34993b162ff4d695e42d59d9777ea18d3848d9\ + d336c25d2acb93adcad009bcfb9cde12286df267ada283063de0bb\ + 1505565b2eb6c90e31c48798ecdc71a71756a9110ff373", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "4ac054dda0a38a65d0ecf7afd3c2812300027c8789655e47\ + aecf1ecc1a2426b17444c7482c99e5907afd9c25b991990490bb9c\ + 686f43e79b4471a23a703d4b02f23c669737a886a7ec28bddb92c3\ + a98de63ebf878aa363a501a60055c048bea11840c4717beae7eee2\ + 8c3cfa42857b3d130188571943a7bd747de831bd6444e0", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + + let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let len_in_bytes = 0x80; + let uniform_bytes = hex::decode( + "09afc76d51c2cccbc129c2315df66c2be7295a231203b8ab\ + 2dd7f95c2772c68e500bc72e20c602abc9964663b7a03a389be128\ + c56971ce81001a0b875e7fd17822db9d69792ddf6a23a151bf4700\ + 79c518279aef3e75611f8f828994a9988f4a8a256ddb8bae161e65\ + 8d5a2a09bcfe839c6396dc06ee5c8ff3c22d3b1f9deb7e", + ) + .unwrap(); + assert_eq!( + ExpandMsgXof::::init_expand(msg, dst, len_in_bytes).into_vec(), + uniform_bytes + ); + } +} diff --git a/src/bls12_381/hash_to_curve/map_g1.rs b/src/bls12_381/hash_to_curve/map_g1.rs new file mode 100644 index 00000000..270f398e --- /dev/null +++ b/src/bls12_381/hash_to_curve/map_g1.rs @@ -0,0 +1,964 @@ +//! Implementation of hash-to-curve for the G1 group. +//! +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq}; + +use super::chain::chain_pm3div4; +use super::{HashToField, MapToCurve, Sgn0}; +use crate::bls12_381::fp::Fp; +use crate::bls12_381::g1::G1Projective; +use crate::bls12_381::generic_array::{typenum::U64, GenericArray}; +use ff::Field; + +/// Coefficients of the 11-isogeny x map's numerator +const ISO11_XNUM: [Fp; 12] = [ + Fp::from_raw_unchecked([ + 0x4d18_b6f3_af00_131c, + 0x19fa_2197_93fe_e28c, + 0x3f28_85f1_467f_19ae, + 0x23dc_ea34_f2ff_b304, + 0xd15b_58d2_ffc0_0054, + 0x0913_be20_0a20_bef4, + ]), + Fp::from_raw_unchecked([ + 0x8989_8538_5cdb_bd8b, + 0x3c79_e43c_c7d9_66aa, + 0x1597_e193_f4cd_233a, + 0x8637_ef1e_4d66_23ad, + 0x11b2_2dee_d20d_827b, + 0x0709_7bc5_9987_84ad, + ]), + Fp::from_raw_unchecked([ + 0xa542_583a_480b_664b, + 0xfc71_69c0_26e5_68c6, + 0x5ba2_ef31_4ed8_b5a6, + 0x5b54_91c0_5102_f0e7, + 0xdf6e_9970_7d2a_0079, + 0x0784_151e_d760_5524, + ]), + Fp::from_raw_unchecked([ + 0x494e_2128_70f7_2741, + 0xab9b_e52f_bda4_3021, + 0x26f5_5779_94e3_4c3d, + 0x049d_fee8_2aef_bd60, + 0x65da_dd78_2850_5289, + 0x0e93_d431_ea01_1aeb, + ]), + Fp::from_raw_unchecked([ + 0x90ee_774b_d6a7_4d45, + 0x7ada_1c8a_41bf_b185, + 0x0f1a_8953_b325_f464, + 0x104c_2421_1be4_805c, + 0x1691_39d3_19ea_7a8f, + 0x09f2_0ead_8e53_2bf6, + ]), + Fp::from_raw_unchecked([ + 0x6ddd_93e2_f436_26b7, + 0xa548_2c9a_a1cc_d7bd, + 0x1432_4563_1883_f4bd, + 0x2e0a_94cc_f77e_c0db, + 0xb028_2d48_0e56_489f, + 0x18f4_bfcb_b436_8929, + ]), + Fp::from_raw_unchecked([ + 0x23c5_f0c9_5340_2dfd, + 0x7a43_ff69_58ce_4fe9, + 0x2c39_0d3d_2da5_df63, + 0xd0df_5c98_e1f9_d70f, + 0xffd8_9869_a572_b297, + 0x1277_ffc7_2f25_e8fe, + ]), + Fp::from_raw_unchecked([ + 0x79f4_f049_0f06_a8a6, + 0x85f8_94a8_8030_fd81, + 0x12da_3054_b18b_6410, + 0xe2a5_7f65_0588_0d65, + 0xbba0_74f2_60e4_00f1, + 0x08b7_6279_f621_d028, + ]), + Fp::from_raw_unchecked([ + 0xe672_45ba_78d5_b00b, + 0x8456_ba9a_1f18_6475, + 0x7888_bff6_e6b3_3bb4, + 0xe215_85b9_a30f_86cb, + 0x05a6_9cdc_ef55_feee, + 0x09e6_99dd_9adf_a5ac, + ]), + Fp::from_raw_unchecked([ + 0x0de5_c357_bff5_7107, + 0x0a0d_b4ae_6b1a_10b2, + 0xe256_bb67_b3b3_cd8d, + 0x8ad4_5657_4e9d_b24f, + 0x0443_915f_50fd_4179, + 0x098c_4bf7_de8b_6375, + ]), + Fp::from_raw_unchecked([ + 0xe6b0_617e_7dd9_29c7, + 0xfe6e_37d4_4253_7375, + 0x1daf_deda_137a_489e, + 0xe4ef_d1ad_3f76_7ceb, + 0x4a51_d866_7f0f_e1cf, + 0x054f_df4b_bf1d_821c, + ]), + Fp::from_raw_unchecked([ + 0x72db_2a50_658d_767b, + 0x8abf_91fa_a257_b3d5, + 0xe969_d683_3764_ab47, + 0x4641_7014_2a10_09eb, + 0xb14f_01aa_db30_be2f, + 0x18ae_6a85_6f40_715d, + ]), +]; + +/// Coefficients of the 11-isogeny x map's denominator +const ISO11_XDEN: [Fp; 11] = [ + Fp::from_raw_unchecked([ + 0xb962_a077_fdb0_f945, + 0xa6a9_740f_efda_13a0, + 0xc14d_568c_3ed6_c544, + 0xb43f_c37b_908b_133e, + 0x9c0b_3ac9_2959_9016, + 0x0165_aa6c_93ad_115f, + ]), + Fp::from_raw_unchecked([ + 0x2327_9a3b_a506_c1d9, + 0x92cf_ca0a_9465_176a, + 0x3b29_4ab1_3755_f0ff, + 0x116d_da1c_5070_ae93, + 0xed45_3092_4cec_2045, + 0x0833_83d6_ed81_f1ce, + ]), + Fp::from_raw_unchecked([ + 0x9885_c2a6_449f_ecfc, + 0x4a2b_54cc_d377_33f0, + 0x17da_9ffd_8738_c142, + 0xa0fb_a727_32b3_fafd, + 0xff36_4f36_e54b_6812, + 0x0f29_c13c_6605_23e2, + ]), + Fp::from_raw_unchecked([ + 0xe349_cc11_8278_f041, + 0xd487_228f_2f32_04fb, + 0xc9d3_2584_9ade_5150, + 0x43a9_2bd6_9c15_c2df, + 0x1c2c_7844_bc41_7be4, + 0x1202_5184_f407_440c, + ]), + Fp::from_raw_unchecked([ + 0x587f_65ae_6acb_057b, + 0x1444_ef32_5140_201f, + 0xfbf9_95e7_1270_da49, + 0xccda_0660_7243_6a42, + 0x7408_904f_0f18_6bb2, + 0x13b9_3c63_edf6_c015, + ]), + Fp::from_raw_unchecked([ + 0xfb91_8622_cd14_1920, + 0x4a4c_6442_3eca_ddb4, + 0x0beb_2329_27f7_fb26, + 0x30f9_4df6_f83a_3dc2, + 0xaeed_d424_d780_f388, + 0x06cc_402d_d594_bbeb, + ]), + Fp::from_raw_unchecked([ + 0xd41f_7611_51b2_3f8f, + 0x32a9_2465_4357_19b3, + 0x64f4_36e8_88c6_2cb9, + 0xdf70_a9a1_f757_c6e4, + 0x6933_a38d_5b59_4c81, + 0x0c6f_7f72_37b4_6606, + ]), + Fp::from_raw_unchecked([ + 0x693c_0874_7876_c8f7, + 0x22c9_850b_f9cf_80f0, + 0x8e90_71da_b950_c124, + 0x89bc_62d6_1c7b_af23, + 0xbc6b_e2d8_dad5_7c23, + 0x1791_6987_aa14_a122, + ]), + Fp::from_raw_unchecked([ + 0x1be3_ff43_9c13_16fd, + 0x9965_243a_7571_dfa7, + 0xc7f7_f629_62f5_cd81, + 0x32c6_aa9a_f394_361c, + 0xbbc2_ee18_e1c2_27f4, + 0x0c10_2cba_c531_bb34, + ]), + Fp::from_raw_unchecked([ + 0x9976_14c9_7bac_bf07, + 0x61f8_6372_b991_92c0, + 0x5b8c_95fc_1435_3fc3, + 0xca2b_066c_2a87_492f, + 0x1617_8f5b_bf69_8711, + 0x12a6_dcd7_f0f4_e0e8, + ]), + Fp::from_raw_unchecked([ + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, + ]), +]; + +/// Coefficients of the 11-isogeny y map's numerator +const ISO11_YNUM: [Fp; 16] = [ + Fp::from_raw_unchecked([ + 0x2b56_7ff3_e283_7267, + 0x1d4d_9e57_b958_a767, + 0xce02_8fea_04bd_7373, + 0xcc31_a30a_0b6c_d3df, + 0x7d7b_18a6_8269_2693, + 0x0d30_0744_d42a_0310, + ]), + Fp::from_raw_unchecked([ + 0x99c2_555f_a542_493f, + 0xfe7f_53cc_4874_f878, + 0x5df0_608b_8f97_608a, + 0x14e0_3832_052b_49c8, + 0x7063_26a6_957d_d5a4, + 0x0a8d_add9_c241_4555, + ]), + Fp::from_raw_unchecked([ + 0x13d9_4292_2a5c_f63a, + 0x357e_33e3_6e26_1e7d, + 0xcf05_a27c_8456_088d, + 0x0000_bd1d_e7ba_50f0, + 0x83d0_c753_2f8c_1fde, + 0x13f7_0bf3_8bbf_2905, + ]), + Fp::from_raw_unchecked([ + 0x5c57_fd95_bfaf_bdbb, + 0x28a3_59a6_5e54_1707, + 0x3983_ceb4_f636_0b6d, + 0xafe1_9ff6_f97e_6d53, + 0xb346_8f45_5019_2bf7, + 0x0bb6_cde4_9d8b_a257, + ]), + Fp::from_raw_unchecked([ + 0x590b_62c7_ff8a_513f, + 0x314b_4ce3_72ca_cefd, + 0x6bef_32ce_94b8_a800, + 0x6ddf_84a0_9571_3d5f, + 0x64ea_ce4c_b098_2191, + 0x0386_213c_651b_888d, + ]), + Fp::from_raw_unchecked([ + 0xa531_0a31_111b_bcdd, + 0xa14a_c0f5_da14_8982, + 0xf9ad_9cc9_5423_d2e9, + 0xaa6e_c095_283e_e4a7, + 0xcf5b_1f02_2e1c_9107, + 0x01fd_df5a_ed88_1793, + ]), + Fp::from_raw_unchecked([ + 0x65a5_72b0_d7a7_d950, + 0xe25c_2d81_8347_3a19, + 0xc2fc_ebe7_cb87_7dbd, + 0x05b2_d36c_769a_89b0, + 0xba12_961b_e86e_9efb, + 0x07eb_1b29_c1df_de1f, + ]), + Fp::from_raw_unchecked([ + 0x93e0_9572_f7c4_cd24, + 0x364e_9290_7679_5091, + 0x8569_467e_68af_51b5, + 0xa47d_a894_39f5_340f, + 0xf4fa_9180_82e4_4d64, + 0x0ad5_2ba3_e669_5a79, + ]), + Fp::from_raw_unchecked([ + 0x9114_2984_4e0d_5f54, + 0xd03f_51a3_516b_b233, + 0x3d58_7e56_4053_6e66, + 0xfa86_d2a3_a9a7_3482, + 0xa90e_d5ad_f1ed_5537, + 0x149c_9c32_6a5e_7393, + ]), + Fp::from_raw_unchecked([ + 0x462b_beb0_3c12_921a, + 0xdc9a_f5fa_0a27_4a17, + 0x9a55_8ebd_e836_ebed, + 0x649e_f8f1_1a4f_ae46, + 0x8100_e165_2b3c_dc62, + 0x1862_bd62_c291_dacb, + ]), + Fp::from_raw_unchecked([ + 0x05c9_b8ca_89f1_2c26, + 0x0194_160f_a9b9_ac4f, + 0x6a64_3d5a_6879_fa2c, + 0x1466_5bdd_8846_e19d, + 0xbb1d_0d53_af3f_f6bf, + 0x12c7_e1c3_b289_62e5, + ]), + Fp::from_raw_unchecked([ + 0xb55e_bf90_0b8a_3e17, + 0xfedc_77ec_1a92_01c4, + 0x1f07_db10_ea1a_4df4, + 0x0dfb_d15d_c41a_594d, + 0x3895_47f2_334a_5391, + 0x0241_9f98_1658_71a4, + ]), + Fp::from_raw_unchecked([ + 0xb416_af00_0745_fc20, + 0x8e56_3e9d_1ea6_d0f5, + 0x7c76_3e17_763a_0652, + 0x0145_8ef0_159e_bbef, + 0x8346_fe42_1f96_bb13, + 0x0d2d_7b82_9ce3_24d2, + ]), + Fp::from_raw_unchecked([ + 0x9309_6bb5_38d6_4615, + 0x6f2a_2619_951d_823a, + 0x8f66_b3ea_5951_4fa4, + 0xf563_e637_04f7_092f, + 0x724b_136c_4cf2_d9fa, + 0x0469_59cf_cfd0_bf49, + ]), + Fp::from_raw_unchecked([ + 0xea74_8d4b_6e40_5346, + 0x91e9_079c_2c02_d58f, + 0x4106_4965_946d_9b59, + 0xa067_31f1_d2bb_e1ee, + 0x07f8_97e2_67a3_3f1b, + 0x1017_2909_1921_0e5f, + ]), + Fp::from_raw_unchecked([ + 0x872a_a6c1_7d98_5097, + 0xeecc_5316_1264_562a, + 0x07af_e37a_fff5_5002, + 0x5475_9078_e5be_6838, + 0xc4b9_2d15_db8a_cca8, + 0x106d_87d1_b51d_13b9, + ]), +]; + +/// Coefficients of the 11-isogeny y map's denominator +const ISO11_YDEN: [Fp; 16] = [ + Fp::from_raw_unchecked([ + 0xeb6c_359d_47e5_2b1c, + 0x18ef_5f8a_1063_4d60, + 0xddfa_71a0_889d_5b7e, + 0x723e_71dc_c5fc_1323, + 0x52f4_5700_b70d_5c69, + 0x0a8b_981e_e476_91f1, + ]), + Fp::from_raw_unchecked([ + 0x616a_3c4f_5535_b9fb, + 0x6f5f_0373_95db_d911, + 0xf25f_4cc5_e35c_65da, + 0x3e50_dffe_a3c6_2658, + 0x6a33_dca5_2356_0776, + 0x0fad_eff7_7b6b_fe3e, + ]), + Fp::from_raw_unchecked([ + 0x2be9_b66d_f470_059c, + 0x24a2_c159_a3d3_6742, + 0x115d_be7a_d10c_2a37, + 0xb663_4a65_2ee5_884d, + 0x04fe_8bb2_b8d8_1af4, + 0x01c2_a7a2_56fe_9c41, + ]), + Fp::from_raw_unchecked([ + 0xf27b_f8ef_3b75_a386, + 0x898b_3674_76c9_073f, + 0x2448_2e6b_8c2f_4e5f, + 0xc8e0_bbd6_fe11_0806, + 0x59b0_c17f_7631_448a, + 0x1103_7cd5_8b3d_bfbd, + ]), + Fp::from_raw_unchecked([ + 0x31c7_912e_a267_eec6, + 0x1dbf_6f1c_5fcd_b700, + 0xd30d_4fe3_ba86_fdb1, + 0x3cae_528f_bee9_a2a4, + 0xb1cc_e69b_6aa9_ad9a, + 0x0443_93bb_632d_94fb, + ]), + Fp::from_raw_unchecked([ + 0xc66e_f6ef_eeb5_c7e8, + 0x9824_c289_dd72_bb55, + 0x71b1_a4d2_f119_981d, + 0x104f_c1aa_fb09_19cc, + 0x0e49_df01_d942_a628, + 0x096c_3a09_7732_72d4, + ]), + Fp::from_raw_unchecked([ + 0x9abc_11eb_5fad_eff4, + 0x32dc_a50a_8857_28f0, + 0xfb1f_a372_1569_734c, + 0xc4b7_6271_ea65_06b3, + 0xd466_a755_99ce_728e, + 0x0c81_d464_5f4c_b6ed, + ]), + Fp::from_raw_unchecked([ + 0x4199_f10e_5b8b_e45b, + 0xda64_e495_b1e8_7930, + 0xcb35_3efe_9b33_e4ff, + 0x9e9e_fb24_aa64_24c6, + 0xf08d_3368_0a23_7465, + 0x0d33_7802_3e4c_7406, + ]), + Fp::from_raw_unchecked([ + 0x7eb4_ae92_ec74_d3a5, + 0xc341_b4aa_9fac_3497, + 0x5be6_0389_9e90_7687, + 0x03bf_d9cc_a75c_bdeb, + 0x564c_2935_a96b_fa93, + 0x0ef3_c333_71e2_fdb5, + ]), + Fp::from_raw_unchecked([ + 0x7ee9_1fd4_49f6_ac2e, + 0xe5d5_bd5c_b935_7a30, + 0x773a_8ca5_196b_1380, + 0xd0fd_a172_174e_d023, + 0x6cb9_5e0f_a776_aead, + 0x0d22_d5a4_0cec_7cff, + ]), + Fp::from_raw_unchecked([ + 0xf727_e092_85fd_8519, + 0xdc9d_55a8_3017_897b, + 0x7549_d8bd_0578_94ae, + 0x1784_1961_3d90_d8f8, + 0xfce9_5ebd_eb5b_490a, + 0x0467_ffae_f23f_c49e, + ]), + Fp::from_raw_unchecked([ + 0xc176_9e6a_7c38_5f1b, + 0x79bc_930d_eac0_1c03, + 0x5461_c75a_23ed_e3b5, + 0x6e20_829e_5c23_0c45, + 0x828e_0f1e_772a_53cd, + 0x116a_efa7_4912_7bff, + ]), + Fp::from_raw_unchecked([ + 0x101c_10bf_2744_c10a, + 0xbbf1_8d05_3a6a_3154, + 0xa0ec_f39e_f026_f602, + 0xfc00_9d49_96dc_5153, + 0xb900_0209_d5bd_08d3, + 0x189e_5fe4_470c_d73c, + ]), + Fp::from_raw_unchecked([ + 0x7ebd_546c_a157_5ed2, + 0xe47d_5a98_1d08_1b55, + 0x57b2_b625_b6d4_ca21, + 0xb0a1_ba04_2285_20cc, + 0x9873_8983_c210_7ff3, + 0x13dd_dbc4_799d_81d6, + ]), + Fp::from_raw_unchecked([ + 0x0931_9f2e_3983_4935, + 0x039e_952c_bdb0_5c21, + 0x55ba_77a9_a2f7_6493, + 0xfd04_e3df_c608_6467, + 0xfb95_832e_7d78_742e, + 0x0ef9_c24e_ccaf_5e0e, + ]), + Fp::from_raw_unchecked([ + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, + ]), +]; + +const SSWU_ELLP_A: Fp = Fp::from_raw_unchecked([ + 0x2f65_aa0e_9af5_aa51, + 0x8646_4c2d_1e84_16c3, + 0xb85c_e591_b7bd_31e2, + 0x27e1_1c91_b5f2_4e7c, + 0x2837_6eda_6bfc_1835, + 0x1554_55c3_e507_1d85, +]); + +const SSWU_ELLP_B: Fp = Fp::from_raw_unchecked([ + 0xfb99_6971_fe22_a1e0, + 0x9aa9_3eb3_5b74_2d6f, + 0x8c47_6013_de99_c5c4, + 0x873e_27c3_a221_e571, + 0xca72_b5e4_5a52_d888, + 0x0682_4061_418a_386b, +]); + +const SSWU_XI: Fp = Fp::from_raw_unchecked([ + 0x886c_0000_0023_ffdc, + 0x0f70_008d_3090_001d, + 0x7767_2417_ed58_28c3, + 0x9dac_23e9_43dc_1740, + 0x5055_3f1b_9c13_1521, + 0x078c_712f_be0a_b6e8, +]); + +const SQRT_M_XI_CUBED: Fp = Fp::from_raw_unchecked([ + 0x43b5_71ca_d321_5f1f, + 0xccb4_60ef_1c70_2dc2, + 0x742d_884f_4f97_100b, + 0xdb2c_3e32_38a3_382b, + 0xe40f_3fa1_3fce_8f88, + 0x0073_a2af_9892_a2ff, +]); + +impl HashToField for Fp { + // ceil(log2(p)) = 381, m = 1, k = 128. + type InputLength = U64; + + fn from_okm(okm: &GenericArray) -> Fp { + const F_2_256: Fp = Fp::from_raw_unchecked([ + 0x075b_3cd7_c5ce_820f, + 0x3ec6_ba62_1c3e_db0b, + 0x168a_13d8_2bff_6bce, + 0x8766_3c4b_f8c4_49d2, + 0x15f3_4c83_ddc8_d830, + 0x0f96_28b4_9caa_2e85, + ]); + + let mut bs = [0u8; 48]; + bs[16..].copy_from_slice(&okm[..32]); + let db = Fp::from_bytes_be(&bs).unwrap(); + + bs[16..].copy_from_slice(&okm[32..]); + let da = Fp::from_bytes_be(&bs).unwrap(); + + db * F_2_256 + da + } +} + +impl Sgn0 for Fp { + fn sgn0(&self) -> Choice { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fp::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, + ); + Choice::from((tmp.0[0] & 1) as u8) + } +} + +/// Maps an element of [`Fp`] to a point on iso-G1. +/// +/// Implements [section 6.6.2 of `draft-irtf-cfrg-hash-to-curve-12`][sswu]. +/// +/// [sswu]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-12#section-6.6.2 +fn map_to_curve_simple_swu(u: &Fp) -> G1Projective { + let usq = u.square(); + let xi_usq = SSWU_XI * usq; + let xisq_u4 = xi_usq.square(); + let nd_common = xisq_u4 + xi_usq; // XI^2 * u^4 + XI * u^2 + let x_den = SSWU_ELLP_A * Fp::conditional_select(&(-nd_common), &SSWU_XI, nd_common.is_zero()); + let x0_num = SSWU_ELLP_B * (Fp::one() + nd_common); // B * (1 + (XI^2 * u^4 + XI * u^2)) + + // compute g(x0(u)) + let x_densq = x_den.square(); + let gx_den = x_densq * x_den; + // x0_num^3 + A * x0_num * x_den^2 + B * x_den^3 + let gx0_num = (x0_num.square() + SSWU_ELLP_A * x_densq) * x0_num + SSWU_ELLP_B * gx_den; + + // compute g(X0(u)) ^ ((p - 3) // 4) + let sqrt_candidate = { + let u_v = gx0_num * gx_den; // u*v + let vsq = gx_den.square(); // v^2 + u_v * chain_pm3div4(&(u_v * vsq)) // u v (u v^3) ^ ((p - 3) // 4) + }; + + let gx0_square = (sqrt_candidate.square() * gx_den).ct_eq(&gx0_num); // g(x0) is square + let x1_num = x0_num * xi_usq; + // sqrt(-XI**3) * u^3 g(x0) ^ ((p - 3) // 4) + let y1 = SQRT_M_XI_CUBED * usq * u * sqrt_candidate; + + let x_num = Fp::conditional_select(&x1_num, &x0_num, gx0_square); + let mut y = Fp::conditional_select(&y1, &sqrt_candidate, gx0_square); + // ensure sign of y and sign of u agree + y.conditional_negate(y.sgn0() ^ u.sgn0()); + + G1Projective { + x: x_num, + y: y * x_den, + z: x_den, + } +} + +/// Maps an iso-G1 point to a G1 point. +fn iso_map(u: &G1Projective) -> G1Projective { + const COEFFS: [&[Fp]; 4] = [&ISO11_XNUM, &ISO11_XDEN, &ISO11_YNUM, &ISO11_YDEN]; + + // unpack input point + let G1Projective { x, y, z } = *u; + + // xnum, xden, ynum, yden + let mut mapvals = [Fp::zero(); 4]; + + // pre-compute powers of z + let zpows = { + let mut zpows = [Fp::zero(); 15]; + zpows[0] = z; + for idx in 1..zpows.len() { + zpows[idx] = zpows[idx - 1] * z; + } + zpows + }; + + // compute map value by Horner's rule + for idx in 0..4 { + let coeff = COEFFS[idx]; + let clast = coeff.len() - 1; + mapvals[idx] = coeff[clast]; + for jdx in 0..clast { + mapvals[idx] = mapvals[idx] * x + zpows[jdx] * coeff[clast - 1 - jdx]; + } + } + + // x denominator is order 1 less than x numerator, so we need an extra factor of z + mapvals[1] *= z; + + // multiply result of Y map by the y-coord, y / z + mapvals[2] *= y; + mapvals[3] *= z; + + G1Projective { + x: mapvals[0] * mapvals[3], // xnum * yden, + y: mapvals[2] * mapvals[1], // ynum * xden, + z: mapvals[1] * mapvals[3], // xden * yden + } +} + +impl MapToCurve for G1Projective { + type Field = Fp; + + fn map_to_curve(u: &Fp) -> G1Projective { + let pt = map_to_curve_simple_swu(u); + iso_map(&pt) + } + + fn clear_h(&self) -> Self { + self.clear_cofactor() + } +} + +#[cfg(test)] +fn check_g1_prime(pt: &G1Projective) -> bool { + // (X : Y : Z)==(X/Z, Y/Z) is on E': y^2 = x^3 + A * x + B. + // y^2 z = (x^3) + A (x z^2) + B z^3 + let zsq = pt.z.square(); + (pt.y.square() * pt.z) + == (pt.x.square() * pt.x + SSWU_ELLP_A * pt.x * zsq + SSWU_ELLP_B * zsq * pt.z) +} + +#[test] +fn test_simple_swu_expected() { + // exceptional case: zero + let p = map_to_curve_simple_swu(&Fp::zero()); + let G1Projective { x, y, z } = &p; + let xo = Fp::from_raw_unchecked([ + 0xfb99_6971_fe22_a1e0, + 0x9aa9_3eb3_5b74_2d6f, + 0x8c47_6013_de99_c5c4, + 0x873e_27c3_a221_e571, + 0xca72_b5e4_5a52_d888, + 0x0682_4061_418a_386b, + ]); + let yo = Fp::from_raw_unchecked([ + 0xfd6f_ced8_7a7f_11a3, + 0x9a6b_314b_03c8_db31, + 0x41f8_5416_e0ea_b593, + 0xfeeb_089f_7e6e_c4d7, + 0x85a1_34c3_7ed1_278f, + 0x0575_c525_bb9f_74bb, + ]); + let zo = Fp::from_raw_unchecked([ + 0x7f67_4ea0_a891_5178, + 0xb0f9_45fc_13b8_fa65, + 0x4b46_759a_38e8_7d76, + 0x2e7a_9296_41bb_b6a1, + 0x1668_ddfa_462b_f6b6, + 0x0096_0e2e_d1cf_294c, + ]); + assert_eq!(x, &xo); + assert_eq!(y, &yo); + assert_eq!(z, &zo); + assert!(check_g1_prime(&p)); + + // exceptional case: sqrt(-1/XI) (positive) + let excp = Fp::from_raw_unchecked([ + 0x00f3_d047_7e91_edbf, + 0x08d6_621e_4ca8_dc69, + 0xb9cf_7927_b19b_9726, + 0xba13_3c99_6caf_a2ec, + 0xed2a_5ccd_5ca7_bb68, + 0x19cb_022f_8ee9_d73b, + ]); + let p = map_to_curve_simple_swu(&excp); + let G1Projective { x, y, z } = &p; + assert_eq!(x, &xo); + assert_eq!(y, &yo); + assert_eq!(z, &zo); + assert!(check_g1_prime(&p)); + + // exceptional case: sqrt(-1/XI) (negative) + let excp = Fp::from_raw_unchecked([ + 0xb90b_2fb8_816d_bcec, + 0x15d5_9de0_64ab_2396, + 0xad61_5979_4515_5efe, + 0xaa64_0eeb_86d5_6fd2, + 0x5df1_4ae8_e6a3_f16e, + 0x0036_0fba_aa96_0f5e, + ]); + let p = map_to_curve_simple_swu(&excp); + let G1Projective { x, y, z } = &p; + let myo = -yo; + assert_eq!(x, &xo); + assert_eq!(y, &myo); + assert_eq!(z, &zo); + assert!(check_g1_prime(&p)); + + let u = Fp::from_raw_unchecked([ + 0xa618_fa19_f7e2_eadc, + 0x93c7_f1fc_876b_a245, + 0xe2ed_4cc4_7b5c_0ae0, + 0xd49e_fa74_e4a8_d000, + 0xa0b2_3ba6_92b5_431c, + 0x0d15_51f2_d7d8_d193, + ]); + let xo = Fp::from_raw_unchecked([ + 0x2197_ca55_fab3_ba48, + 0x591d_eb39_f434_949a, + 0xf9df_7fb4_f1fa_6a08, + 0x59e3_c16a_9dfa_8fa5, + 0xe592_9b19_4aad_5f7a, + 0x130a_46a4_c61b_44ed, + ]); + let yo = Fp::from_raw_unchecked([ + 0xf721_5b58_c720_0ad0, + 0x8905_1631_3a4e_66bf, + 0xc903_1acc_8a36_19a8, + 0xea1f_9978_fde3_ffec, + 0x0548_f02d_6cfb_f472, + 0x1693_7557_3529_163f, + ]); + let zo = Fp::from_raw_unchecked([ + 0xf36f_eb2e_1128_ade0, + 0x42e2_2214_250b_cd94, + 0xb94f_6ba2_dddf_62d6, + 0xf56d_4392_782b_f0a2, + 0xb2d7_ce1e_c263_09e7, + 0x182b_57ed_6b99_f0a1, + ]); + let p = map_to_curve_simple_swu(&u); + let G1Projective { x, y, z } = &p; + assert_eq!(x, &xo); + assert_eq!(y, &yo); + assert_eq!(z, &zo); + assert!(check_g1_prime(&p)); +} + +#[test] +fn test_osswu_semirandom() { + use rand_core::SeedableRng; + let mut rng = rand_xorshift::XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + for _ in 0..32 { + let input = Fp::random(&mut rng); + let p = map_to_curve_simple_swu(&input); + assert!(check_g1_prime(&p)); + + let p_iso = iso_map(&p); + assert!(bool::from(p_iso.is_on_curve())); + } +} + +// test vectors from the draft 10 RFC +#[test] +fn test_encode_to_curve_10() { + use crate::bls12_381::{ + g1::G1Affine, + hash_to_curve::{ExpandMsgXmd, HashToCurve}, + }; + use std::string::{String, ToString}; + + struct TestCase { + msg: &'static [u8], + expected: [&'static str; 2], + } + impl TestCase { + fn expected(&self) -> String { + self.expected[0].to_string() + self.expected[1] + } + } + + const DOMAIN: &[u8] = b"QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_NU_"; + + let cases = vec![ + TestCase { + msg: b"", + expected: [ + "184bb665c37ff561a89ec2122dd343f20e0f4cbcaec84e3c3052ea81d1834e192c426074b02ed3dca4e7676ce4ce48ba", + "04407b8d35af4dacc809927071fc0405218f1401a6d15af775810e4e460064bcc9468beeba82fdc751be70476c888bf3", + ], + }, + TestCase { + msg: b"abc", + expected: [ + "009769f3ab59bfd551d53a5f846b9984c59b97d6842b20a2c565baa167945e3d026a3755b6345df8ec7e6acb6868ae6d", + "1532c00cf61aa3d0ce3e5aa20c3b531a2abd2c770a790a2613818303c6b830ffc0ecf6c357af3317b9575c567f11cd2c", + ], + }, + TestCase { + msg: b"abcdef0123456789", + expected: [ + "1974dbb8e6b5d20b84df7e625e2fbfecb2cdb5f77d5eae5fb2955e5ce7313cae8364bc2fff520a6c25619739c6bdcb6a", + "15f9897e11c6441eaa676de141c8d83c37aab8667173cbe1dfd6de74d11861b961dccebcd9d289ac633455dfcc7013a3", + ] + }, + TestCase { + msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq", + expected: [ + "0a7a047c4a8397b3446450642c2ac64d7239b61872c9ae7a59707a8f4f950f101e766afe58223b3bff3a19a7f754027c", + "1383aebba1e4327ccff7cf9912bda0dbc77de048b71ef8c8a81111d71dc33c5e3aa6edee9cf6f5fe525d50cc50b77cc9", + ] + }, + TestCase { + msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + expected: [ + "0e7a16a975904f131682edbb03d9560d3e48214c9986bd50417a77108d13dc957500edf96462a3d01e62dc6cd468ef11", + "0ae89e677711d05c30a48d6d75e76ca9fb70fe06c6dd6ff988683d89ccde29ac7d46c53bb97a59b1901abf1db66052db", + ] + } + ]; + + for case in cases { + let g = >>::encode_to_curve( + case.msg, DOMAIN, + ); + let aff = G1Affine::from(g); + let g_uncompressed = aff.to_uncompressed_be(); + + assert_eq!(case.expected(), hex::encode(g_uncompressed.as_ref())); + } +} + +// test vectors from the draft 10 RFC +#[test] +fn test_hash_to_curve_10() { + use crate::bls12_381::{ + g1::G1Affine, + hash_to_curve::{ExpandMsgXmd, HashToCurve}, + }; + use std::string::{String, ToString}; + + struct TestCase { + msg: &'static [u8], + expected: [&'static str; 2], + } + impl TestCase { + fn expected(&self) -> String { + self.expected[0].to_string() + self.expected[1] + } + } + + const DOMAIN: &[u8] = b"QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_"; + + let cases = vec![ + TestCase { + msg: b"", + expected: [ + "052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a1", + "08ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265", + ], + }, + TestCase { + msg: b"abc", + expected: [ + "03567bc5ef9c690c2ab2ecdf6a96ef1c139cc0b2f284dca0a9a7943388a49a3aee664ba5379a7655d3c68900be2f6903", + "0b9c15f3fe6e5cf4211f346271d7b01c8f3b28be689c8429c85b67af215533311f0b8dfaaa154fa6b88176c229f2885d" + ], + }, + TestCase { + msg: b"abcdef0123456789", + expected: [ + "11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d98", + "03a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709" + ] + }, + TestCase { + msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq", + expected: [ + "15f68eaa693b95ccb85215dc65fa81038d69629f70aeee0d0f677cf22285e7bf58d7cb86eefe8f2e9bc3f8cb84fac488", + "1807a1d50c29f430b8cafc4f8638dfeeadf51211e1602a5f184443076715f91bb90a48ba1e370edce6ae1062f5e6dd38" + ] + }, + TestCase { + msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + expected: [ + "082aabae8b7dedb0e78aeb619ad3bfd9277a2f77ba7fad20ef6aabdc6c31d19ba5a6d12283553294c1825c4b3ca2dcfe", + "05b84ae5a942248eea39e1d91030458c40153f3b654ab7872d779ad1e942856a20c438e8d99bc8abfbf74729ce1f7ac8" + ] + } + ]; + + for case in cases { + let g = >>::hash_to_curve( + case.msg, DOMAIN, + ); + let g_uncompressed = G1Affine::from(g).to_uncompressed_be(); + + assert_eq!(case.expected(), hex::encode(g_uncompressed.as_ref())); + } +} + +#[cfg(test)] +// p-1 / 2 +pub const P_M1_OVER2: Fp = Fp::from_raw_unchecked([ + 0xa1fa_ffff_fffe_5557, + 0x995b_fff9_76a3_fffe, + 0x03f4_1d24_d174_ceb4, + 0xf654_7998_c199_5dbd, + 0x778a_468f_507a_6034, + 0x0205_5993_1f7f_8103, +]); + +#[test] +fn test_sgn0() { + assert!(!bool::from(Fp::zero().sgn0())); + assert!(bool::from(Fp::one().sgn0())); + assert!(!bool::from((-Fp::one()).sgn0())); + assert!(!bool::from((-Fp::zero()).sgn0())); + assert!(bool::from(P_M1_OVER2.sgn0())); + + let p_p1_over2 = P_M1_OVER2 + Fp::one(); + assert!(!bool::from(p_p1_over2.sgn0())); + + let neg_p_p1_over2 = { + let mut tmp = p_p1_over2; + tmp.conditional_negate(Choice::from(1u8)); + tmp + }; + assert_eq!(neg_p_p1_over2, P_M1_OVER2); +} diff --git a/src/bls12_381/hash_to_curve/map_g2.rs b/src/bls12_381/hash_to_curve/map_g2.rs new file mode 100644 index 00000000..110c1155 --- /dev/null +++ b/src/bls12_381/hash_to_curve/map_g2.rs @@ -0,0 +1,849 @@ +//! Implementation of hash-to-curve for the G2 group +//! +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq}; + +use super::chain::chain_p2m9div16; +use super::{HashToField, MapToCurve, Sgn0}; +use crate::bls12_381::generic_array::{ + typenum::{U128, U64}, + GenericArray, +}; +use crate::bls12_381::{fp::Fp, fp2::Fp2, g2::G2Projective}; +use ff::Field; + +/// Coefficients of the 3-isogeny x map's numerator +const ISO3_XNUM: [Fp2; 4] = [ + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f6_71c7_1ce0_5e62, + 0x06dd_5707_1206_393e, + 0x7c80_cd2a_f3fd_71a2, + 0x0481_03ea_9e6c_d062, + 0xc545_16ac_c8d0_37f6, + 0x1380_8f55_0920_ea41, + ]), + c1: Fp::from_raw_unchecked([ + 0x47f6_71c7_1ce0_5e62, + 0x06dd_5707_1206_393e, + 0x7c80_cd2a_f3fd_71a2, + 0x0481_03ea_9e6c_d062, + 0xc545_16ac_c8d0_37f6, + 0x1380_8f55_0920_ea41, + ]), + }, + Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0x5fe5_5555_554c_71d0, + 0x873f_ffdd_236a_aaa3, + 0x6a6b_4619_b26e_f918, + 0x21c2_8884_0887_4945, + 0x2836_cda7_028c_abc5, + 0x0ac7_3310_a7fd_5abd, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0a0c_5555_5559_71c3, + 0xdb0c_0010_1f9e_aaae, + 0xb1fb_2f94_1d79_7997, + 0xd396_0742_ef41_6e1c, + 0xb700_40e2_c205_56f4, + 0x149d_7861_e581_393b, + ]), + c1: Fp::from_raw_unchecked([ + 0xaff2_aaaa_aaa6_38e8, + 0x439f_ffee_91b5_5551, + 0xb535_a30c_d937_7c8c, + 0x90e1_4442_0443_a4a2, + 0x941b_66d3_8146_55e2, + 0x0563_9988_53fe_ad5e, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x40aa_c71c_71c7_25ed, + 0x1909_5555_7a84_e38e, + 0xd817_050a_8f41_abc3, + 0xd864_85d4_c87f_6fb1, + 0x696e_b479_f885_d059, + 0x198e_1a74_3280_02d2, + ]), + c1: Fp::zero(), + }, +]; + +/// Coefficients of the 3-isogeny x map's denominator +const ISO3_XDEN: [Fp2; 3] = [ + Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0x1f3a_ffff_ff13_ab97, + 0xf25b_fc61_1da3_ff3e, + 0xca37_57cb_3819_b208, + 0x3e64_2736_6f8c_ec18, + 0x0397_7bc8_6095_b089, + 0x04f6_9db1_3f39_a952, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4476_0000_0027_552e, + 0xdcb8_009a_4348_0020, + 0x6f7e_e9ce_4a6e_8b59, + 0xb103_30b7_c0a9_5bc6, + 0x6140_b1fc_fb1e_54b7, + 0x0381_be09_7f0b_b4e1, + ]), + c1: Fp::from_raw_unchecked([ + 0x7588_ffff_ffd8_557d, + 0x41f3_ff64_6e0b_ffdf, + 0xf7b1_e8d2_ac42_6aca, + 0xb374_1acd_32db_b6f8, + 0xe9da_f5b9_482d_581f, + 0x167f_53e0_ba74_31b8, + ]), + }, + Fp2::one(), +]; + +/// Coefficients of the 3-isogeny y map's numerator +const ISO3_YNUM: [Fp2; 4] = [ + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x96d8_f684_bdfc_77be, + 0xb530_e4f4_3b66_d0e2, + 0x184a_88ff_3796_52fd, + 0x57cb_23ec_fae8_04e1, + 0x0fd2_e39e_ada3_eba9, + 0x08c8_055e_31c5_d5c3, + ]), + c1: Fp::from_raw_unchecked([ + 0x96d8_f684_bdfc_77be, + 0xb530_e4f4_3b66_d0e2, + 0x184a_88ff_3796_52fd, + 0x57cb_23ec_fae8_04e1, + 0x0fd2_e39e_ada3_eba9, + 0x08c8_055e_31c5_d5c3, + ]), + }, + Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0xbf0a_71c7_1c91_b406, + 0x4d6d_55d2_8b76_38fd, + 0x9d82_f98e_5f20_5aee, + 0xa27a_a27b_1d1a_18d5, + 0x02c3_b2b2_d293_8e86, + 0x0c7d_1342_0b09_807f, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0xd7f9_5555_5553_1c74, + 0x21cf_fff7_48da_aaa8, + 0x5a9a_d186_6c9b_be46, + 0x4870_a221_0221_d251, + 0x4a0d_b369_c0a3_2af1, + 0x02b1_ccc4_29ff_56af, + ]), + c1: Fp::from_raw_unchecked([ + 0xe205_aaaa_aaac_8e37, + 0xfcdc_0007_6879_5556, + 0x0c96_011a_8a15_37dd, + 0x1c06_a963_f163_406e, + 0x010d_f44c_82a8_81e6, + 0x174f_4526_0f80_8feb, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa470_bda1_2f67_f35c, + 0xc0fe_38e2_3327_b425, + 0xc9d3_d0f2_c6f0_678d, + 0x1c55_c993_5b5a_982e, + 0x27f6_c0e2_f074_6764, + 0x117c_5e6e_28aa_9054, + ]), + c1: Fp::zero(), + }, +]; + +/// Coefficients of the 3-isogeny y map's denominator +const ISO3_YDEN: [Fp2; 4] = [ + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0162_ffff_fa76_5adf, + 0x8f7b_ea48_0083_fb75, + 0x561b_3c22_59e9_3611, + 0x11e1_9fc1_a9c8_75d5, + 0xca71_3efc_0036_7660, + 0x03c6_a03d_41da_1151, + ]), + c1: Fp::from_raw_unchecked([ + 0x0162_ffff_fa76_5adf, + 0x8f7b_ea48_0083_fb75, + 0x561b_3c22_59e9_3611, + 0x11e1_9fc1_a9c8_75d5, + 0xca71_3efc_0036_7660, + 0x03c6_a03d_41da_1151, + ]), + }, + Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0x5db0_ffff_fd3b_02c5, + 0xd713_f523_58eb_fdba, + 0x5ea6_0761_a84d_161a, + 0xbb2c_75a3_4ea6_c44a, + 0x0ac6_7359_21c1_119b, + 0x0ee3_d913_bdac_fbf6, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x66b1_0000_003a_ffc5, + 0xcb14_00e7_64ec_0030, + 0xa73e_5eb5_6fa5_d106, + 0x8984_c913_a0fe_09a9, + 0x11e1_0afb_78ad_7f13, + 0x0542_9d0e_3e91_8f52, + ]), + c1: Fp::from_raw_unchecked([ + 0x534d_ffff_ffc4_aae6, + 0x5397_ff17_4c67_ffcf, + 0xbff2_73eb_870b_251d, + 0xdaf2_8271_5287_0915, + 0x393a_9cba_ca9e_2dc3, + 0x14be_74db_faee_5748, + ]), + }, + Fp2::one(), +]; + +const SSWU_ELLP_A: Fp2 = Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0xe53a_0000_0313_5242, + 0x0108_0c0f_def8_0285, + 0xe788_9edb_e340_f6bd, + 0x0b51_3751_2631_0601, + 0x02d6_9857_17c7_44ab, + 0x1220_b4e9_79ea_5467, + ]), +}; + +const SSWU_ELLP_B: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x22ea_0000_0cf8_9db2, + 0x6ec8_32df_7138_0aa4, + 0x6e1b_9440_3db5_a66e, + 0x75bf_3c53_a794_73ba, + 0x3dd3_a569_412c_0a34, + 0x125c_db5e_74dc_4fd1, + ]), + c1: Fp::from_raw_unchecked([ + 0x22ea_0000_0cf8_9db2, + 0x6ec8_32df_7138_0aa4, + 0x6e1b_9440_3db5_a66e, + 0x75bf_3c53_a794_73ba, + 0x3dd3_a569_412c_0a34, + 0x125c_db5e_74dc_4fd1, + ]), +}; + +const SSWU_XI: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x87eb_ffff_fff9_555c, + 0x656f_ffe5_da8f_fffa, + 0x0fd0_7493_45d3_3ad2, + 0xd951_e663_0665_76f4, + 0xde29_1a3d_41e9_80d3, + 0x0815_664c_7dfe_040d, + ]), + c1: Fp::from_raw_unchecked([ + 0x43f5_ffff_fffc_aaae, + 0x32b7_fff2_ed47_fffd, + 0x07e8_3a49_a2e9_9d69, + 0xeca8_f331_8332_bb7a, + 0xef14_8d1e_a0f4_c069, + 0x040a_b326_3eff_0206, + ]), +}; + +const SSWU_ETAS: [Fp2; 4] = [ + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x05e5_1466_8ac7_36d2, + 0x9089_b4d6_b84f_3ea5, + 0x603c_384c_224a_8b32, + 0xf325_7909_536a_fea6, + 0x5c5c_dbab_ae65_6d81, + 0x075b_fa08_63c9_87e9, + ]), + c1: Fp::from_raw_unchecked([ + 0x338d_9bfe_0808_7330, + 0x7b8e_48b2_bd83_cefe, + 0x530d_ad5d_306b_5be7, + 0x5a4d_7e8e_6c40_8b6d, + 0x6258_f7a6_232c_ab9b, + 0x0b98_5811_cce1_4db5, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8671_6401_f7f7_377b, + 0xa31d_b74b_f3d0_3101, + 0x1423_2543_c645_9a3c, + 0x0a29_ccf6_8744_8752, + 0xe8c2_b010_201f_013c, + 0x0e68_b9d8_6c9e_98e4, + ]), + c1: Fp::from_raw_unchecked([ + 0x05e5_1466_8ac7_36d2, + 0x9089_b4d6_b84f_3ea5, + 0x603c_384c_224a_8b32, + 0xf325_7909_536a_fea6, + 0x5c5c_dbab_ae65_6d81, + 0x075b_fa08_63c9_87e9, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x718f_dad2_4ee1_d90f, + 0xa58c_025b_ed82_76af, + 0x0c3a_1023_0ab7_976f, + 0xf0c5_4df5_c8f2_75e1, + 0x4ec2_478c_28ba_f465, + 0x1129_373a_90c5_08e6, + ]), + c1: Fp::from_raw_unchecked([ + 0x019a_f5f9_80a3_680c, + 0x4ed7_da0e_6606_3afa, + 0x6003_5472_3b5d_9972, + 0x8b2f_958b_20d0_9d72, + 0x0474_938f_02d4_61db, + 0x0dcf_8b9e_0684_ab1c, + ]), + }, + Fp2 { + c0: Fp::from_raw_unchecked([ + 0xb864_0a06_7f5c_429f, + 0xcfd4_25f0_4b4d_c505, + 0x072d_7e2e_bb53_5cb1, + 0xd947_b5f9_d2b4_754d, + 0x46a7_1427_4077_4afb, + 0x0c31_864c_32fb_3b7e, + ]), + c1: Fp::from_raw_unchecked([ + 0x718f_dad2_4ee1_d90f, + 0xa58c_025b_ed82_76af, + 0x0c3a_1023_0ab7_976f, + 0xf0c5_4df5_c8f2_75e1, + 0x4ec2_478c_28ba_f465, + 0x1129_373a_90c5_08e6, + ]), + }, +]; + +const SSWU_RV1: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7bcf_a7a2_5aa3_0fda, + 0xdc17_dec1_2a92_7e7c, + 0x2f08_8dd8_6b4e_bef1, + 0xd1ca_2087_da74_d4a7, + 0x2da2_5966_96ce_bc1d, + 0x0e2b_7eed_bbfd_87d2, + ]), + c1: Fp::from_raw_unchecked([ + 0x7bcf_a7a2_5aa3_0fda, + 0xdc17_dec1_2a92_7e7c, + 0x2f08_8dd8_6b4e_bef1, + 0xd1ca_2087_da74_d4a7, + 0x2da2_5966_96ce_bc1d, + 0x0e2b_7eed_bbfd_87d2, + ]), +}; + +impl HashToField for Fp2 { + // ceil(log2(p)) = 381, m = 2, k = 128. + type InputLength = U128; + + fn from_okm(okm: &GenericArray) -> Fp2 { + let c0 = ::from_okm(GenericArray::::from_slice(&okm[..64])); + let c1 = ::from_okm(GenericArray::::from_slice(&okm[64..])); + Fp2 { c0, c1 } + } +} + +impl Sgn0 for Fp2 { + fn sgn0(&self) -> Choice { + let sign_0 = self.c0.sgn0(); + let zero_0 = self.c0.is_zero(); + let sign_1 = self.c1.sgn0(); + sign_0 | (zero_0 & sign_1) + } +} + +/// Maps from an [`Fp2]` element to a point on iso-G2. +fn map_to_curve_simple_swu(u: &Fp2) -> G2Projective { + let usq = u.square(); + let xi_usq = SSWU_XI * usq; + let xisq_u4 = xi_usq.square(); + let nd_common = xisq_u4 + xi_usq; // XI^2 * u^4 + XI * u^2 + let x_den = SSWU_ELLP_A * Fp2::conditional_select(&(-nd_common), &SSWU_XI, nd_common.is_zero()); + let x0_num = SSWU_ELLP_B * (Fp2::one() + nd_common); // B * (1 + (XI^2 * u^4 + XI * u^2)) + + // compute g(x0(u)) + let x_densq = x_den.square(); + let gx_den = x_densq * x_den; + // x0_num^3 + A * x0_num * x_den^2 + B * x_den^3 + let gx0_num = (x0_num.square() + SSWU_ELLP_A * x_densq) * x0_num + SSWU_ELLP_B * gx_den; + + // compute g(x0(u)) ^ ((p^2 - 9) // 16) + let sqrt_candidate = { + let vsq = gx_den.square(); // v^2 + let v_3 = vsq * gx_den; // v^3 + let v_4 = vsq.square(); // v^4 + let uv_7 = gx0_num * v_3 * v_4; // u v^7 + let uv_15 = uv_7 * v_4.square(); // u v^15 + uv_7 * chain_p2m9div16(&uv_15) // u v^7 (u v^15) ^ ((p^2 - 9) // 16) + }; + + // set y = sqrt_candidate * Fp2::one(), check candidate against other roots of unity + let mut y = sqrt_candidate; + // check Fp2(0, 1) + let tmp = Fp2 { + c0: -sqrt_candidate.c1, + c1: sqrt_candidate.c0, + }; + y.conditional_assign(&tmp, (tmp.square() * gx_den).ct_eq(&gx0_num)); + // check Fp2(RV1, RV1) + let tmp = sqrt_candidate * SSWU_RV1; + y.conditional_assign(&tmp, (tmp.square() * gx_den).ct_eq(&gx0_num)); + // check Fp2(RV1, -RV1) + let tmp = Fp2 { + c0: tmp.c1, + c1: -tmp.c0, + }; + y.conditional_assign(&tmp, (tmp.square() * gx_den).ct_eq(&gx0_num)); + + // compute g(x1(u)) = g(x0(u)) * XI^3 * u^6 + let gx1_num = gx0_num * xi_usq * xisq_u4; + // compute g(x1(u)) * u^3 + let sqrt_candidate = sqrt_candidate * usq * u; + let mut eta_found = Choice::from(0u8); + for eta in &SSWU_ETAS[..] { + let tmp = sqrt_candidate * eta; + let found = (tmp.square() * gx_den).ct_eq(&gx1_num); + y.conditional_assign(&tmp, found); + eta_found |= found; + } + + let x_num = Fp2::conditional_select(&x0_num, &(x0_num * xi_usq), eta_found); + // ensure sign of y and sign of u agree + y.conditional_negate(u.sgn0() ^ y.sgn0()); + + G2Projective { + x: x_num, + y: y * x_den, + z: x_den, + } +} + +/// Maps from an iso-G2 point to a G2 point. +fn iso_map(u: &G2Projective) -> G2Projective { + const COEFFS: [&[Fp2]; 4] = [&ISO3_XNUM, &ISO3_XDEN, &ISO3_YNUM, &ISO3_YDEN]; + + // unpack input point + let G2Projective { x, y, z } = *u; + + // xnum, xden, ynum, yden + let mut mapvals = [Fp2::zero(); 4]; + + // compute powers of z + let zsq = z.square(); + let zpows = [z, zsq, zsq * z]; + + // compute map value by Horner's rule + for idx in 0..4 { + let coeff = COEFFS[idx]; + let clast = coeff.len() - 1; + mapvals[idx] = coeff[clast]; + for jdx in 0..clast { + mapvals[idx] = mapvals[idx] * x + zpows[jdx] * coeff[clast - 1 - jdx]; + } + } + + // x denominator is order 1 less than x numerator, so we need an extra factor of z + mapvals[1] *= z; + + // multiply result of Y map by the y-coord, y / z + mapvals[2] *= y; + mapvals[3] *= z; + + G2Projective { + x: mapvals[0] * mapvals[3], // xnum * yden, + y: mapvals[2] * mapvals[1], // ynum * xden, + z: mapvals[1] * mapvals[3], // xden * yden + } +} + +impl MapToCurve for G2Projective { + type Field = Fp2; + + fn map_to_curve(u: &Fp2) -> G2Projective { + let pt = map_to_curve_simple_swu(u); + iso_map(&pt) + } + + fn clear_h(&self) -> Self { + self.clear_cofactor() + } +} + +#[cfg(test)] +fn check_g2_prime(pt: &G2Projective) -> bool { + // (X : Y : Z)==(X/Z, Y/Z) is on E': y^2 = x^3 + A * x + B. + // y^2 z = (x^3) + A (x z^2) + B z^3 + let zsq = pt.z.square(); + (pt.y.square() * pt.z) + == (pt.x.square() * pt.x + SSWU_ELLP_A * pt.x * zsq + SSWU_ELLP_B * zsq * pt.z) +} + +#[test] +fn test_osswu_semirandom() { + use rand_core::SeedableRng; + let mut rng = rand_xorshift::XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + for _ in 0..32 { + let input = Fp2::random(&mut rng); + let p = map_to_curve_simple_swu(&input); + assert!(check_g2_prime(&p)); + + let p_iso = iso_map(&p); + assert!(bool::from(p_iso.is_on_curve())); + } +} + +// test vectors from the draft 10 RFC +#[test] +fn test_encode_to_curve_10() { + use crate::bls12_381::{ + g2::G2Affine, + hash_to_curve::{ExpandMsgXmd, HashToCurve}, + }; + use std::string::{String, ToString}; + + struct TestCase { + msg: &'static [u8], + expected: [&'static str; 4], + } + impl TestCase { + fn expected(&self) -> String { + self.expected[0].to_string() + self.expected[1] + self.expected[2] + self.expected[3] + } + } + + const DOMAIN: &[u8] = b"QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_NU_"; + + let cases = vec![ + TestCase { + msg: b"", + expected: [ + "126b855e9e69b1f691f816e48ac6977664d24d99f8724868a184186469ddfd4617367e94527d4b74fc86413483afb35b", + "00e7f4568a82b4b7dc1f14c6aaa055edf51502319c723c4dc2688c7fe5944c213f510328082396515734b6612c4e7bb7", + "1498aadcf7ae2b345243e281ae076df6de84455d766ab6fcdaad71fab60abb2e8b980a440043cd305db09d283c895e3d", + "0caead0fd7b6176c01436833c79d305c78be307da5f6af6c133c47311def6ff1e0babf57a0fb5539fce7ee12407b0a42", + ], + }, + TestCase { + msg: b"abc", + expected: [ + "0296238ea82c6d4adb3c838ee3cb2346049c90b96d602d7bb1b469b905c9228be25c627bffee872def773d5b2a2eb57d", + "108ed59fd9fae381abfd1d6bce2fd2fa220990f0f837fa30e0f27914ed6e1454db0d1ee957b219f61da6ff8be0d6441f", + "153606c417e59fb331b7ae6bce4fbf7c5190c33ce9402b5ebe2b70e44fca614f3f1382a3625ed5493843d0b0a652fc3f", + "033f90f6057aadacae7963b0a0b379dd46750c1c94a6357c99b65f63b79e321ff50fe3053330911c56b6ceea08fee656", + ], + }, + TestCase { + msg: b"abcdef0123456789", + expected: [ + "0da75be60fb6aa0e9e3143e40c42796edf15685cafe0279afd2a67c3dff1c82341f17effd402e4f1af240ea90f4b659b", + "038af300ef34c7759a6caaa4e69363cafeed218a1f207e93b2c70d91a1263d375d6730bd6b6509dcac3ba5b567e85bf3", + "0492f4fed741b073e5a82580f7c663f9b79e036b70ab3e51162359cec4e77c78086fe879b65ca7a47d34374c8315ac5e", + "19b148cbdf163cf0894f29660d2e7bfb2b68e37d54cc83fd4e6e62c020eaa48709302ef8e746736c0e19342cc1ce3df4", + ] + }, + TestCase { + msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq", + expected: [ + "12c8c05c1d5fc7bfa847f4d7d81e294e66b9a78bc9953990c358945e1f042eedafce608b67fdd3ab0cb2e6e263b9b1ad", + "0c5ae723be00e6c3f0efe184fdc0702b64588fe77dda152ab13099a3bacd3876767fa7bbad6d6fd90b3642e902b208f9", + "11c624c56dbe154d759d021eec60fab3d8b852395a89de497e48504366feedd4662d023af447d66926a28076813dd646", + "04e77ddb3ede41b5ec4396b7421dd916efc68a358a0d7425bddd253547f2fb4830522358491827265dfc5bcc1928a569", + ] + }, + TestCase { + msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + expected: [ + "1565c2f625032d232f13121d3cfb476f45275c303a037faa255f9da62000c2c864ea881e2bcddd111edc4a3c0da3e88d", + "0ea4e7c33d43e17cc516a72f76437c4bf81d8f4eac69ac355d3bf9b71b8138d55dc10fd458be115afa798b55dac34be1", + "0f8991d2a1ad662e7b6f58ab787947f1fa607fce12dde171bc17903b012091b657e15333e11701edcf5b63ba2a561247", + "043b6f5fe4e52c839148dc66f2b3751e69a0f6ebb3d056d6465d50d4108543ecd956e10fa1640dfd9bc0030cc2558d28", + ] + } + ]; + + for case in cases { + let g = >>::encode_to_curve( + case.msg, DOMAIN, + ); + let g_uncompressed = G2Affine::from(g).to_uncompressed_be(); + + assert_eq!(case.expected(), hex::encode(g_uncompressed.as_ref())); + } +} + +// test vectors from the draft 10 RFC +#[test] +fn test_hash_to_curve_10() { + use crate::bls12_381::{ + g2::G2Affine, + hash_to_curve::{ExpandMsgXmd, HashToCurve}, + }; + use std::string::{String, ToString}; + + struct TestCase { + msg: &'static [u8], + expected: [&'static str; 4], + } + impl TestCase { + fn expected(&self) -> String { + self.expected[0].to_string() + self.expected[1] + self.expected[2] + self.expected[3] + } + } + + const DOMAIN: &[u8] = b"QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_"; + + let cases = vec![ + TestCase { + msg: b"", + expected: [ + "05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d", + "0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a", + "12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d6", + "0503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92", + ], + }, + TestCase { + msg: b"abc", + expected: [ + "139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd8", + "02c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e6", + "00aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd16", + "1787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48", + ], + }, + TestCase { + msg: b"abcdef0123456789", + expected: [ + "190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c", + "121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd0", + "0bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be", + "05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8", + ] + }, + TestCase { + msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ + qqqqqqqqqqqqqqqqqqqqqqqqq", + expected: [ + "0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb91", + "19a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da", + "09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e5662", + "14f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192", + ] + }, + TestCase { + msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + expected: [ + "11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d01569", + "01a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f62534", + "03a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab52", + "0b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e", + ] + } + ]; + + for case in cases { + let g = >>::hash_to_curve( + case.msg, DOMAIN, + ); + let g_uncompressed = G2Affine::from(g).to_uncompressed_be(); + + assert_eq!(case.expected(), hex::encode(g_uncompressed.as_ref())); + } +} + +#[test] +fn test_sgn0() { + use super::map_g1::P_M1_OVER2; + + assert!(!bool::from(Fp2::zero().sgn0())); + assert!(bool::from(Fp2::one().sgn0())); + assert!(bool::from( + Fp2 { + c0: P_M1_OVER2, + c1: Fp::zero() + } + .sgn0() + )); + assert!(bool::from( + Fp2 { + c0: P_M1_OVER2, + c1: Fp::one() + } + .sgn0() + )); + assert!(bool::from( + Fp2 { + c0: Fp::zero(), + c1: P_M1_OVER2, + } + .sgn0() + )); + assert!(bool::from( + Fp2 { + c0: Fp::one(), + c1: P_M1_OVER2, + } + .sgn0() + )); + + let p_p1_over2 = P_M1_OVER2 + Fp::one(); + assert!(!bool::from( + Fp2 { + c0: p_p1_over2, + c1: Fp::zero() + } + .sgn0() + )); + assert!(!bool::from( + Fp2 { + c0: p_p1_over2, + c1: Fp::one() + } + .sgn0() + )); + assert!(!bool::from( + Fp2 { + c0: Fp::zero(), + c1: p_p1_over2, + } + .sgn0() + )); + assert!(bool::from( + Fp2 { + c0: Fp::one(), + c1: p_p1_over2, + } + .sgn0() + )); + + assert!(bool::from( + Fp2 { + c0: P_M1_OVER2, + c1: -Fp::one() + } + .sgn0() + )); + assert!(!bool::from( + Fp2 { + c0: p_p1_over2, + c1: -Fp::one() + } + .sgn0() + )); + assert!(!bool::from( + Fp2 { + c0: Fp::zero(), + c1: -Fp::one() + } + .sgn0() + )); + assert!(bool::from( + Fp2 { + c0: P_M1_OVER2, + c1: p_p1_over2 + } + .sgn0() + )); + assert!(!bool::from( + Fp2 { + c0: p_p1_over2, + c1: P_M1_OVER2 + } + .sgn0() + )); + + assert!(!bool::from( + Fp2 { + c0: -Fp::one(), + c1: P_M1_OVER2, + } + .sgn0() + )); + assert!(!bool::from( + Fp2 { + c0: -Fp::one(), + c1: p_p1_over2, + } + .sgn0() + )); + assert!(!bool::from( + Fp2 { + c0: -Fp::one(), + c1: Fp::zero(), + } + .sgn0() + )); + assert!(!bool::from( + Fp2 { + c0: p_p1_over2, + c1: P_M1_OVER2, + } + .sgn0() + )); + assert!(bool::from( + Fp2 { + c0: P_M1_OVER2, + c1: p_p1_over2, + } + .sgn0() + )); +} diff --git a/src/bls12_381/hash_to_curve/map_scalar.rs b/src/bls12_381/hash_to_curve/map_scalar.rs new file mode 100644 index 00000000..8c839aaa --- /dev/null +++ b/src/bls12_381/hash_to_curve/map_scalar.rs @@ -0,0 +1,41 @@ +//! Implementation of hash-to-field for Scalar values +//! +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use super::HashToField; +use crate::bls12_381::generic_array::{typenum::U48, GenericArray}; +use crate::bls12_381::scalar::Scalar; + +impl HashToField for Scalar { + // ceil(log2(p)) = 255, m = 1, k = 128. + type InputLength = U48; + + fn from_okm(okm: &GenericArray) -> Scalar { + let mut bs = [0u8; 64]; + bs[16..].copy_from_slice(okm); + bs.reverse(); // into little endian + Scalar::from_bytes_wide(&bs) + } +} + +#[test] +fn test_hash_to_scalar() { + let tests: &[(&[u8], &str)] = &[ + ( + &[0u8; 48], + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ( + b"aaaaaabbbbbbccccccddddddeeeeeeffffffgggggghhhhhh", + "0x2228450bf55d8fe62395161bd3677ff6fc28e45b89bc87e02a818eda11a8c5da", + ), + ( + b"111111222222333333444444555555666666777777888888", + "0x4aa543cbd2f0c8f37f8a375ce2e383eb343e7e3405f61e438b0a15fb8899d1ae", + ), + ]; + for (input, expected) in tests { + let output = format!("{:?}", Scalar::from_okm(GenericArray::from_slice(input))); + assert_eq!(&output, expected); + } +} diff --git a/src/bls12_381/hash_to_curve/mod.rs b/src/bls12_381/hash_to_curve/mod.rs new file mode 100644 index 00000000..91db16ca --- /dev/null +++ b/src/bls12_381/hash_to_curve/mod.rs @@ -0,0 +1,115 @@ +//! This module implements hash_to_curve, hash_to_field and related +//! hashing primitives for use with BLS signatures. +//! +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use core::ops::Add; + +use subtle::Choice; + +pub(crate) mod chain; + +mod expand_msg; +pub use self::expand_msg::{ + ExpandMessage, ExpandMessageState, ExpandMsgXmd, ExpandMsgXof, InitExpandMessage, +}; + +mod map_g1; +mod map_g2; +mod map_scalar; + +use crate::bls12_381::generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; + +/// Enables a byte string to be hashed into one or more field elements for a given curve. +/// +/// Implements [section 5 of `draft-irtf-cfrg-hash-to-curve-12`][hash_to_field]. +/// +/// [hash_to_field]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-12#section-5 +pub trait HashToField: Sized { + /// The length of the data used to produce an individual field element. + /// + /// This must be set to `m * L = m * ceil((ceil(log2(p)) + k) / 8)`, where `p` is the + /// characteristic of `Self`, `m` is the extension degree of `Self`, and `k` is the + /// security parameter. + type InputLength: ArrayLength; + + /// Interprets the given output keying material as a big endian integer, and reduces + /// it into a field element. + fn from_okm(okm: &GenericArray) -> Self; + + /// Hashes a byte string of arbitrary length into one or more elements of `Self`, + /// using [`ExpandMessage`] variant `X`. + /// + /// Implements [section 5.3 of `draft-irtf-cfrg-hash-to-curve-12`][hash_to_field]. + /// + /// [hash_to_field]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-12#section-5.3 + fn hash_to_field(message: &[u8], dst: &[u8], output: &mut [Self]) { + let len_per_elm = Self::InputLength::to_usize(); + let len_in_bytes = output.len() * len_per_elm; + let mut expander = X::init_expand(message, dst, len_in_bytes); + + let mut buf = GenericArray::::default(); + output.iter_mut().for_each(|item| { + expander.read_into(&mut buf[..]); + *item = Self::from_okm(&buf); + }); + } +} + +/// Allow conversion from the output of hashed or encoded input into points on the curve +pub trait MapToCurve: Sized { + /// The field element type. + type Field: Copy + Default + HashToField; + + /// Maps an element of the finite field `Self::Field` to a point on the curve `Self`. + fn map_to_curve(elt: &Self::Field) -> Self; + + /// Clears the cofactor, sending a point on curve E to the target group (G1/G2). + fn clear_h(&self) -> Self; +} + +/// Implementation of random oracle maps to the curve. +pub trait HashToCurve: MapToCurve + for<'a> Add<&'a Self, Output = Self> { + /// Implements a uniform encoding from byte strings to elements of `Self`. + /// + /// This function is suitable for most applications requiring a random + /// oracle returning points in `Self`. + fn hash_to_curve(message: impl AsRef<[u8]>, dst: &[u8]) -> Self { + let mut u = [Self::Field::default(); 2]; + Self::Field::hash_to_field::(message.as_ref(), dst, &mut u); + let p1 = Self::map_to_curve(&u[0]); + let p2 = Self::map_to_curve(&u[1]); + (p1 + &p2).clear_h() + } + + /// Implements a **non-uniform** encoding from byte strings to elements of `Self`. + /// + /// The distribution of its output is not uniformly random in `Self`: the set of + /// possible outputs of this function is only a fraction of the points in `Self`, and + /// some elements of this set are more likely to be output than others. See + /// [section 10.1 of `draft-irtf-cfrg-hash-to-curve-12`][encode_to_curve-distribution] + /// for a more precise definition of `encode_to_curve`'s output distribution. + /// + /// [encode_to_curve-distribution]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-12#section-10.1 + fn encode_to_curve(message: impl AsRef<[u8]>, dst: &[u8]) -> Self { + let mut u = [Self::Field::default(); 1]; + Self::Field::hash_to_field::(message.as_ref(), dst, &mut u); + let p = Self::map_to_curve(&u[0]); + p.clear_h() + } +} + +impl HashToCurve for G +where + G: MapToCurve + for<'a> Add<&'a Self, Output = Self>, + X: ExpandMessage, +{ +} + +pub(crate) trait Sgn0 { + /// Returns either 0 or 1 indicating the "sign" of x, where sgn0(x) == 1 + /// just when x is "negative". (In other words, this function always considers 0 to be positive.) + /// + /// The equivalent for draft 6 would be `lexicographically_largest`. + fn sgn0(&self) -> Choice; +} diff --git a/src/bls12_381/mod.rs b/src/bls12_381/mod.rs new file mode 100644 index 00000000..ce9d5a7f --- /dev/null +++ b/src/bls12_381/mod.rs @@ -0,0 +1,64 @@ +//! # `bls12_381` +//! +//! This crate provides an implementation of the BLS12-381 pairing-friendly elliptic +//! curve construction. +//! +//! * **This implementation has not been reviewed or audited. Use at your own risk.** +//! * This implementation targets Rust `1.36` or later. +//! * This implementation does not require the Rust standard library. +//! * All operations are constant time unless explicitly noted. +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +// Catch documentation errors caused by code changes. +#![allow(clippy::too_many_arguments)] +#![allow(clippy::many_single_char_names)] +// This lint is described at +// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl +// In our library, some of the arithmetic involving extension fields will necessarily +// involve various binary operators, and so this lint is triggered unnecessarily. +#![allow(clippy::suspicious_arithmetic_impl)] + +// #[macro_use] +// mod util; + +mod scalar; + +pub use fp::Fp as Fq; +pub use scalar::Scalar as Fr; + +use scalar::Scalar; + +mod fp; +mod fp2; +mod g1; +mod g2; + +use g1::G1Projective; +use g2::G2Projective; + +pub use g1::{G1Affine, G1Projective as G1}; +pub use g2::{G2Affine, G2Projective as G2}; + +mod fp12; +mod fp6; + +pub use fp12::{Fp12 as Fq12, FROBENIUS_COEFF_FQ12_C1}; +pub use fp2::Fp2 as Fq2; +pub use fp6::Fp6 as Fq6; + +// The BLS parameter x for BLS12-381 is -0xd201000000010000 +pub const BLS_X: u64 = 0xd201_0000_0001_0000; +pub const BLS_X_IS_NEGATIVE: bool = true; + +mod pairings; + +pub use pairings::{multi_miller_loop, G2Prepared}; +pub use pairings::{pairing, Bls12, Gt, MillerLoopResult}; + +mod endo; + +pub(crate) use digest::generic_array; +pub mod hash_to_curve; + +#[cfg(test)] +mod tests; diff --git a/src/bls12_381/pairings.rs b/src/bls12_381/pairings.rs new file mode 100644 index 00000000..a40c0eb6 --- /dev/null +++ b/src/bls12_381/pairings.rs @@ -0,0 +1,961 @@ +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +use super::fp::Fp; +use super::fp12::Fp12; +use super::fp2::Fp2; +use super::fp6::Fp6; +use super::{G1Affine, G1Projective, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; + +use core::borrow::Borrow; +use core::fmt; +use core::iter::Sum; +use core::ops::{Add, AddAssign, Mul, Neg, Sub}; +use group::Group; +use pairing::{Engine, MultiMillerLoop, PairingCurveAffine}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; + +/// Represents results of a Miller loop, one of the most expensive portions +/// of the pairing function. `MillerLoopResult`s cannot be compared with each +/// other until `.final_exponentiation()` is called, which is also expensive. +#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] +#[derive(Copy, Clone, Debug)] +pub struct MillerLoopResult(pub(crate) Fp12); + +impl Default for MillerLoopResult { + fn default() -> Self { + MillerLoopResult(Fp12::one()) + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for MillerLoopResult {} + +impl ConditionallySelectable for MillerLoopResult { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + MillerLoopResult(Fp12::conditional_select(&a.0, &b.0, choice)) + } +} + +impl MillerLoopResult { + /// This performs a "final exponentiation" routine to convert the result + /// of a Miller loop into an element of `Gt` with help of efficient squaring + /// operation in the so-called `cyclotomic subgroup` of `Fq6` so that + /// it can be compared with other elements of `Gt`. + pub fn final_exponentiation(&self) -> Gt { + #[must_use] + fn fp4_square(a: Fp2, b: Fp2) -> (Fp2, Fp2) { + let t0 = a.square(); + let t1 = b.square(); + let mut t2 = t1.mul_by_nonresidue(); + let c0 = t2 + t0; + t2 = a + b; + t2 = t2.square(); + t2 -= t0; + let c1 = t2 - t1; + + (c0, c1) + } + // Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography + // Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions + // https://eprint.iacr.org/2009/565.pdf + #[must_use] + fn cyclotomic_square(f: Fp12) -> Fp12 { + let mut z0 = f.c0.c0; + let mut z4 = f.c0.c1; + let mut z3 = f.c0.c2; + let mut z2 = f.c1.c0; + let mut z1 = f.c1.c1; + let mut z5 = f.c1.c2; + + let (t0, t1) = fp4_square(z0, z1); + + // For A + z0 = t0 - z0; + z0 = z0 + z0 + t0; + + z1 = t1 + z1; + z1 = z1 + z1 + t1; + + let (mut t0, t1) = fp4_square(z2, z3); + let (t2, t3) = fp4_square(z4, z5); + + // For C + z4 = t0 - z4; + z4 = z4 + z4 + t0; + + z5 = t1 + z5; + z5 = z5 + z5 + t1; + + // For B + t0 = t3.mul_by_nonresidue(); + z2 = t0 + z2; + z2 = z2 + z2 + t0; + + z3 = t2 - z3; + z3 = z3 + z3 + t2; + + Fp12 { + c0: Fp6 { + c0: z0, + c1: z4, + c2: z3, + }, + c1: Fp6 { + c0: z2, + c1: z1, + c2: z5, + }, + } + } + #[must_use] + fn cycolotomic_exp(f: Fp12) -> Fp12 { + let x = BLS_X; + let mut tmp = Fp12::one(); + let mut found_one = false; + for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) { + if found_one { + tmp = cyclotomic_square(tmp) + } else { + found_one = i; + } + + if i { + tmp *= f; + } + } + + tmp.conjugate() + } + + let mut f = self.0; + let mut t0 = f + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map(); + Gt(f.invert() + .map(|mut t1| { + let mut t2 = t0 * t1; + t1 = t2; + t2 = t2.frobenius_map().frobenius_map(); + t2 *= t1; + t1 = cyclotomic_square(t2).conjugate(); + let mut t3 = cycolotomic_exp(t2); + let mut t4 = cyclotomic_square(t3); + let mut t5 = t1 * t3; + t1 = cycolotomic_exp(t5); + t0 = cycolotomic_exp(t1); + let mut t6 = cycolotomic_exp(t0); + t6 *= t4; + t4 = cycolotomic_exp(t6); + t5 = t5.conjugate(); + t4 *= t5 * t2; + t5 = t2.conjugate(); + t1 *= t2; + t1 = t1.frobenius_map().frobenius_map().frobenius_map(); + t6 *= t5; + t6 = t6.frobenius_map(); + t3 *= t0; + t3 = t3.frobenius_map().frobenius_map(); + t3 *= t1; + t3 *= t6; + f = t3 * t4; + + f + }) + // We unwrap() because `MillerLoopResult` can only be constructed + // by a function within this crate, and we uphold the invariant + // that the enclosed value is nonzero. + .unwrap()) + } +} + +impl<'a, 'b> Add<&'b MillerLoopResult> for &'a MillerLoopResult { + type Output = MillerLoopResult; + + #[inline] + fn add(self, rhs: &'b MillerLoopResult) -> MillerLoopResult { + MillerLoopResult(self.0 * rhs.0) + } +} + +impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopResult); + +impl AddAssign for MillerLoopResult { + #[inline] + fn add_assign(&mut self, rhs: MillerLoopResult) { + *self = *self + rhs; + } +} + +impl<'b> AddAssign<&'b MillerLoopResult> for MillerLoopResult { + #[inline] + fn add_assign(&mut self, rhs: &'b MillerLoopResult) { + *self = *self + rhs; + } +} + +/// This is an element of $\mathbb{G}_T$, the target group of the pairing function. As with +/// $\mathbb{G}_1$ and $\mathbb{G}_2$ this group has order $q$. +/// +/// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to +/// keep code and abstractions consistent. +#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] +#[derive(Copy, Clone, Debug)] +pub struct Gt(pub(crate) Fp12); + +impl Default for Gt { + fn default() -> Self { + Self::identity() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for Gt {} + +impl fmt::Display for Gt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") + } +} + +impl ConstantTimeEq for Gt { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } +} + +impl ConditionallySelectable for Gt { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Gt(Fp12::conditional_select(&a.0, &b.0, choice)) + } +} + +impl Eq for Gt {} +impl PartialEq for Gt { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl Gt { + /// Returns the group identity, which is $1$. + pub fn identity() -> Gt { + Gt(Fp12::one()) + } + + /// Doubles this group element. + pub fn double(&self) -> Gt { + Gt(self.0.square()) + } +} + +impl<'a> Neg for &'a Gt { + type Output = Gt; + + #[inline] + fn neg(self) -> Gt { + // The element is unitary, so we just conjugate. + Gt(self.0.conjugate()) + } +} + +impl Neg for Gt { + type Output = Gt; + + #[inline] + fn neg(self) -> Gt { + -&self + } +} + +impl<'a, 'b> Add<&'b Gt> for &'a Gt { + type Output = Gt; + + #[inline] + fn add(self, rhs: &'b Gt) -> Gt { + Gt(self.0 * rhs.0) + } +} + +impl<'a, 'b> Sub<&'b Gt> for &'a Gt { + type Output = Gt; + + #[inline] + fn sub(self, rhs: &'b Gt) -> Gt { + self + (-rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a Gt { + type Output = Gt; + + fn mul(self, other: &'b Scalar) -> Self::Output { + let mut acc = Gt::identity(); + + // This is a simple double-and-add implementation of group element + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + for bit in other + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = Gt::conditional_select(&acc, &(acc + self), bit); + } + + acc + } +} + +impl_binops_additive!(Gt, Gt); +impl_binops_multiplicative!(Gt, Scalar); + +impl Sum for Gt +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + +impl Group for Gt { + type Scalar = Scalar; + + fn random(mut rng: impl RngCore) -> Self { + loop { + let inner = Fp12::random(&mut rng); + + // Not all elements of Fp12 are elements of the prime-order multiplicative + // subgroup. We run the random element through final_exponentiation to obtain + // a valid element, which requires that it is non-zero. + if !bool::from(inner.is_zero()) { + return MillerLoopResult(inner).final_exponentiation(); + } + } + } + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + // pairing(&G1Affine::generator(), &G2Affine::generator()) + Gt(Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1972_e433_a01f_85c5, + 0x97d3_2b76_fd77_2538, + 0xc8ce_546f_c96b_cdf9, + 0xcef6_3e73_66d4_0614, + 0xa611_3427_8184_3780, + 0x13f3_448a_3fc6_d825, + ]), + c1: Fp::from_raw_unchecked([ + 0xd263_31b0_2e9d_6995, + 0x9d68_a482_f779_7e7d, + 0x9c9b_2924_8d39_ea92, + 0xf480_1ca2_e131_07aa, + 0xa16c_0732_bdbc_b066, + 0x083c_a4af_ba36_0478, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x59e2_61db_0916_b641, + 0x2716_b6f4_b23e_960d, + 0xc8e5_5b10_a0bd_9c45, + 0x0bdb_0bd9_9c4d_eda8, + 0x8cf8_9ebf_57fd_aac5, + 0x12d6_b792_9e77_7a5e, + ]), + c1: Fp::from_raw_unchecked([ + 0x5fc8_5188_b0e1_5f35, + 0x34a0_6e3a_8f09_6365, + 0xdb31_26a6_e02a_d62c, + 0xfc6f_5aa9_7d9a_990b, + 0xa12f_55f5_eb89_c210, + 0x1723_703a_926f_8889, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x9358_8f29_7182_8778, + 0x43f6_5b86_11ab_7585, + 0x3183_aaf5_ec27_9fdf, + 0xfa73_d7e1_8ac9_9df6, + 0x64e1_76a6_a64c_99b0, + 0x179f_a78c_5838_8f1f, + ]), + c1: Fp::from_raw_unchecked([ + 0x672a_0a11_ca2a_ef12, + 0x0d11_b9b5_2aa3_f16b, + 0xa444_12d0_699d_056e, + 0xc01d_0177_221a_5ba5, + 0x66e0_cede_6c73_5529, + 0x05f5_a71e_9fdd_c339, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xd30a_88a1_b062_c679, + 0x5ac5_6a5d_35fc_8304, + 0xd0c8_34a6_a81f_290d, + 0xcd54_30c2_da37_07c7, + 0xf0c2_7ff7_8050_0af0, + 0x0924_5da6_e2d7_2eae, + ]), + c1: Fp::from_raw_unchecked([ + 0x9f2e_0676_791b_5156, + 0xe2d1_c823_4918_fe13, + 0x4c9e_459f_3c56_1bf4, + 0xa3e8_5e53_b9d3_e3c1, + 0x820a_121e_21a7_0020, + 0x15af_6183_41c5_9acc, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7c95_658c_2499_3ab1, + 0x73eb_3872_1ca8_86b9, + 0x5256_d749_4774_34bc, + 0x8ba4_1902_ea50_4a8b, + 0x04a3_d3f8_0c86_ce6d, + 0x18a6_4a87_fb68_6eaa, + ]), + c1: Fp::from_raw_unchecked([ + 0xbb83_e71b_b920_cf26, + 0x2a52_77ac_92a7_3945, + 0xfc0e_e59f_94f0_46a0, + 0x7158_cdf3_7860_58f7, + 0x7cc1_061b_82f9_45f6, + 0x03f8_47aa_9fdb_e567, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8078_dba5_6134_e657, + 0x1cd7_ec9a_4399_8a6e, + 0xb1aa_599a_1a99_3766, + 0xc9a0_f62f_0842_ee44, + 0x8e15_9be3_b605_dffa, + 0x0c86_ba0d_4af1_3fc2, + ]), + c1: Fp::from_raw_unchecked([ + 0xe80f_f2a0_6a52_ffb1, + 0x7694_ca48_721a_906c, + 0x7583_183e_03b0_8514, + 0xf567_afdd_40ce_e4e2, + 0x9a6d_96d2_e526_a5fc, + 0x197e_9f49_861f_2242, + ]), + }, + }, + }) + } + + fn is_identity(&self) -> Choice { + self.ct_eq(&Self::identity()) + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } +} + +#[derive(Clone, Debug)] +/// This structure contains cached computations pertaining to a $\mathbb{G}_2$ +/// element as part of the pairing function (specifically, the Miller loop) and +/// so should be computed whenever a $\mathbb{G}_2$ element is being used in +/// multiple pairings or is otherwise known in advance. This should be used in +/// conjunction with the [`multi_miller_loop`](crate::multi_miller_loop) +/// function provided by this crate. +pub struct G2Prepared { + infinity: Choice, + coeffs: Vec<(Fp2, Fp2, Fp2)>, +} + +impl From for G2Prepared { + fn from(q: G2Affine) -> G2Prepared { + struct Adder { + cur: G2Projective, + base: G2Affine, + coeffs: Vec<(Fp2, Fp2, Fp2)>, + } + + impl MillerLoopDriver for Adder { + type Output = (); + + fn doubling_step(&mut self, _: Self::Output) -> Self::Output { + let coeffs = doubling_step(&mut self.cur); + self.coeffs.push(coeffs); + } + fn addition_step(&mut self, _: Self::Output) -> Self::Output { + let coeffs = addition_step(&mut self.cur, &self.base); + self.coeffs.push(coeffs); + } + fn square_output(_: Self::Output) -> Self::Output {} + fn conjugate(_: Self::Output) -> Self::Output {} + fn one() -> Self::Output {} + } + + let is_identity = q.is_identity(); + let q = G2Affine::conditional_select(&q, &G2Affine::generator(), is_identity); + + let mut adder = Adder { + cur: G2Projective::from(q), + base: q, + coeffs: Vec::with_capacity(68), + }; + + miller_loop(&mut adder); + + assert_eq!(adder.coeffs.len(), 68); + + G2Prepared { + infinity: is_identity, + coeffs: adder.coeffs, + } + } +} + +/// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms +/// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ +pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> MillerLoopResult { + struct Adder<'a, 'b, 'c> { + terms: &'c [(&'a G1Affine, &'b G2Prepared)], + index: usize, + } + + impl<'a, 'b, 'c> MillerLoopDriver for Adder<'a, 'b, 'c> { + type Output = Fp12; + + fn doubling_step(&mut self, mut f: Self::Output) -> Self::Output { + let index = self.index; + for term in self.terms { + let either_identity = term.0.is_identity() | term.1.infinity; + + let new_f = ell(f, &term.1.coeffs[index], term.0); + f = Fp12::conditional_select(&new_f, &f, either_identity); + } + self.index += 1; + + f + } + fn addition_step(&mut self, mut f: Self::Output) -> Self::Output { + let index = self.index; + for term in self.terms { + let either_identity = term.0.is_identity() | term.1.infinity; + + let new_f = ell(f, &term.1.coeffs[index], term.0); + f = Fp12::conditional_select(&new_f, &f, either_identity); + } + self.index += 1; + + f + } + fn square_output(f: Self::Output) -> Self::Output { + f.square() + } + fn conjugate(f: Self::Output) -> Self::Output { + f.conjugate() + } + fn one() -> Self::Output { + Fp12::one() + } + } + + let mut adder = Adder { terms, index: 0 }; + + let tmp = miller_loop(&mut adder); + + MillerLoopResult(tmp) +} + +/// Invoke the pairing function without the use of precomputation and other optimizations. +#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] +pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt { + struct Adder { + cur: G2Projective, + base: G2Affine, + p: G1Affine, + } + + impl MillerLoopDriver for Adder { + type Output = Fp12; + + fn doubling_step(&mut self, f: Self::Output) -> Self::Output { + let coeffs = doubling_step(&mut self.cur); + ell(f, &coeffs, &self.p) + } + fn addition_step(&mut self, f: Self::Output) -> Self::Output { + let coeffs = addition_step(&mut self.cur, &self.base); + ell(f, &coeffs, &self.p) + } + fn square_output(f: Self::Output) -> Self::Output { + f.square() + } + fn conjugate(f: Self::Output) -> Self::Output { + f.conjugate() + } + fn one() -> Self::Output { + Fp12::one() + } + } + + let either_identity = p.is_identity() | q.is_identity(); + let p = G1Affine::conditional_select(p, &G1Affine::generator(), either_identity); + let q = G2Affine::conditional_select(q, &G2Affine::generator(), either_identity); + + let mut adder = Adder { + cur: G2Projective::from(q), + base: q, + p, + }; + + let tmp = miller_loop(&mut adder); + let tmp = MillerLoopResult(Fp12::conditional_select( + &tmp, + &Fp12::one(), + either_identity, + )); + tmp.final_exponentiation() +} + +trait MillerLoopDriver { + type Output; + + fn doubling_step(&mut self, f: Self::Output) -> Self::Output; + fn addition_step(&mut self, f: Self::Output) -> Self::Output; + fn square_output(f: Self::Output) -> Self::Output; + fn conjugate(f: Self::Output) -> Self::Output; + fn one() -> Self::Output; +} + +/// This is a "generic" implementation of the Miller loop to avoid duplicating code +/// structure elsewhere; instead, we'll write concrete instantiations of +/// `MillerLoopDriver` for whatever purposes we need (such as caching modes). +fn miller_loop(driver: &mut D) -> D::Output { + let mut f = D::one(); + + let mut found_one = false; + for i in (0..64).rev().map(|b| (((BLS_X >> 1) >> b) & 1) == 1) { + if !found_one { + found_one = i; + continue; + } + + f = driver.doubling_step(f); + + if i { + f = driver.addition_step(f); + } + + f = D::square_output(f); + } + + f = driver.doubling_step(f); + + if BLS_X_IS_NEGATIVE { + f = D::conjugate(f); + } + + f +} + +fn ell(f: Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) -> Fp12 { + let mut c0 = coeffs.0; + let mut c1 = coeffs.1; + + c0.c0 *= p.y; + c0.c1 *= p.y; + + c1.c0 *= p.x; + c1.c1 *= p.x; + + f.mul_by_014(&coeffs.2, &c1, &c0) +} + +fn doubling_step(r: &mut G2Projective) -> (Fp2, Fp2, Fp2) { + // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf + let tmp0 = r.x.square(); + let tmp1 = r.y.square(); + let tmp2 = tmp1.square(); + let tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2; + let tmp3 = tmp3 + tmp3; + let tmp4 = tmp0 + tmp0 + tmp0; + let tmp6 = r.x + tmp4; + let tmp5 = tmp4.square(); + let zsquared = r.z.square(); + r.x = tmp5 - tmp3 - tmp3; + r.z = (r.z + r.y).square() - tmp1 - zsquared; + r.y = (tmp3 - r.x) * tmp4; + let tmp2 = tmp2 + tmp2; + let tmp2 = tmp2 + tmp2; + let tmp2 = tmp2 + tmp2; + r.y -= tmp2; + let tmp3 = tmp4 * zsquared; + let tmp3 = tmp3 + tmp3; + let tmp3 = -tmp3; + let tmp6 = tmp6.square() - tmp0 - tmp5; + let tmp1 = tmp1 + tmp1; + let tmp1 = tmp1 + tmp1; + let tmp6 = tmp6 - tmp1; + let tmp0 = r.z * zsquared; + let tmp0 = tmp0 + tmp0; + + (tmp0, tmp3, tmp6) +} + +fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) { + // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf + let zsquared = r.z.square(); + let ysquared = q.y.square(); + let t0 = zsquared * q.x; + let t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared; + let t2 = t0 - r.x; + let t3 = t2.square(); + let t4 = t3 + t3; + let t4 = t4 + t4; + let t5 = t4 * t2; + let t6 = t1 - r.y - r.y; + let t9 = t6 * q.x; + let t7 = t4 * r.x; + r.x = t6.square() - t5 - t7 - t7; + r.z = (r.z + t2).square() - zsquared - t3; + let t10 = q.y + r.z; + let t8 = (t7 - r.x) * t6; + let t0 = r.y * t5; + let t0 = t0 + t0; + r.y = t8 - t0; + let t10 = t10.square() - ysquared; + let ztsquared = r.z.square(); + let t10 = t10 - ztsquared; + let t9 = t9 + t9 - t10; + let t10 = r.z + r.z; + let t6 = -t6; + let t1 = t6 + t6; + + (t10, t1, t9) +} + +impl PairingCurveAffine for G1Affine { + type Pair = G2Affine; + type PairingResult = Gt; + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + pairing(self, other) + } +} + +impl PairingCurveAffine for G2Affine { + type Pair = G1Affine; + type PairingResult = Gt; + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + pairing(other, self) + } +} + +/// A [`pairing::Engine`] for BLS12-381 pairing operations. +#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] +#[derive(Clone, Debug)] +pub struct Bls12; + +impl Engine for Bls12 { + type Fr = Scalar; + type G1 = G1Projective; + type G1Affine = G1Affine; + type G2 = G2Projective; + type G2Affine = G2Affine; + type Gt = Gt; + + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { + pairing(p, q) + } +} + +impl pairing::MillerLoopResult for MillerLoopResult { + type Gt = Gt; + + fn final_exponentiation(&self) -> Self::Gt { + self.final_exponentiation() + } +} + +impl MultiMillerLoop for Bls12 { + type G2Prepared = G2Prepared; + type Result = MillerLoopResult; + + fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { + multi_miller_loop(terms) + } +} + +#[test] +fn test_gt_generator() { + assert_eq!( + Gt::generator(), + pairing(&G1Affine::generator(), &G2Affine::generator()) + ); +} + +#[test] +fn test_bilinearity() { + use super::Scalar; + + let a = Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(); + let b = Scalar::from_raw([5, 6, 7, 8]).invert().unwrap().square(); + let c = a * b; + + let g = G1Affine::from(G1Affine::generator() * a); + let h = G2Affine::from(G2Affine::generator() * b); + let p = pairing(&g, &h); + + assert!(p != Gt::identity()); + + let expected = G1Affine::from(G1Affine::generator() * c); + + assert_eq!(p, pairing(&expected, &G2Affine::generator())); + assert_eq!( + p, + pairing(&G1Affine::generator(), &G2Affine::generator()) * c + ); +} + +#[test] +fn test_unitary() { + let g = G1Affine::generator(); + let h = G2Affine::generator(); + let p = -pairing(&g, &h); + let q = pairing(&g, &-h); + let r = pairing(&-g, &h); + + assert_eq!(p, q); + assert_eq!(q, r); +} + +#[test] +fn test_multi_miller_loop() { + let a1 = G1Affine::generator(); + let b1 = G2Affine::generator(); + + let a2 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(), + ); + let b2 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(), + ); + + let a3 = G1Affine::identity(); + let b3 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(), + ); + + let a4 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(), + ); + let b4 = G2Affine::identity(); + + let a5 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([323, 32, 3, 1]).invert().unwrap().square(), + ); + let b5 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([4, 2, 2, 9099]).invert().unwrap().square(), + ); + + let b1_prepared = G2Prepared::from(b1); + let b2_prepared = G2Prepared::from(b2); + let b3_prepared = G2Prepared::from(b3); + let b4_prepared = G2Prepared::from(b4); + let b5_prepared = G2Prepared::from(b5); + + let expected = pairing(&a1, &b1) + + pairing(&a2, &b2) + + pairing(&a3, &b3) + + pairing(&a4, &b4) + + pairing(&a5, &b5); + + let test = multi_miller_loop(&[ + (&a1, &b1_prepared), + (&a2, &b2_prepared), + (&a3, &b3_prepared), + (&a4, &b4_prepared), + (&a5, &b5_prepared), + ]) + .final_exponentiation(); + + assert_eq!(expected, test); +} + +#[test] +fn test_miller_loop_result_default() { + assert_eq!( + MillerLoopResult::default().final_exponentiation(), + Gt::identity(), + ); +} + +#[cfg(feature = "zeroize")] +#[test] +fn test_miller_loop_result_zeroize() { + use zeroize::Zeroize; + + let mut m = multi_miller_loop(&[ + (&G1Affine::generator(), &G2Affine::generator().into()), + (&-G1Affine::generator(), &G2Affine::generator().into()), + ]); + m.zeroize(); + assert_eq!(m.0, MillerLoopResult::default().0); +} + +// #[test] +// fn tricking_miller_loop_result() { +// assert_eq!( +// multi_miller_loop(&[(&G1Affine::identity(), &G2Affine::generator().into())]).0, +// Fp12::one() +// ); +// assert_eq!( +// multi_miller_loop(&[(&G1Affine::generator(), &G2Affine::identity().into())]).0, +// Fp12::one() +// ); +// assert_ne!( +// multi_miller_loop(&[ +// (&G1Affine::generator(), &G2Affine::generator().into()), +// (&-G1Affine::generator(), &G2Affine::generator().into()) +// ]) +// .0, +// Fp12::one() +// ); +// assert_eq!( +// multi_miller_loop(&[ +// (&G1Affine::generator(), &G2Affine::generator().into()), +// (&-G1Affine::generator(), &G2Affine::generator().into()) +// ]) +// .final_exponentiation(), +// Gt::identity() +// ); +// } diff --git a/src/bls12_381/scalar.rs b/src/bls12_381/scalar.rs new file mode 100644 index 00000000..4269d0c0 --- /dev/null +++ b/src/bls12_381/scalar.rs @@ -0,0 +1,1358 @@ +//! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ +//! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` +//! Source: https://github.com/privacy-scaling-explorations/bls12_381 + +#![allow(clippy::needless_borrow)] +use core::cmp::Ordering; +use core::fmt; +use core::ops::{Add, Mul, MulAssign, Neg, Sub}; +use rand_core::RngCore; + +use ff::{Field, PrimeField, WithSmallOrderMulGroup}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +#[cfg(feature = "bits")] +use ff::{FieldBits, PrimeFieldBits}; + +use crate::arithmetic::{adc, mac, sbb}; +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; + +/// Represents an element of the scalar field $\mathbb{F}_q$ of the BLS12-381 elliptic +/// curve construction. +// The internal representation of this type is four 64-bit unsigned +// integers in little-endian order. `Scalar` values are always in +// Montgomery form; i.e., Scalar(a) = aR mod q, with R = 2^256. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct Scalar(pub(crate) [u64; 4]); + +impl fmt::Debug for Scalar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let tmp = self.to_bytes(); + write!(f, "0x")?; + for &b in tmp.iter().rev() { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + +impl fmt::Display for Scalar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{self:?}") + } +} + +impl From for Scalar { + fn from(val: u64) -> Scalar { + Scalar([val, 0, 0, 0]) * R2 + } +} + +impl ConstantTimeEq for Scalar { + fn ct_eq(&self, other: &Self) -> Choice { + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) + } +} + +// impl PartialEq for Scalar { +// #[inline] +// fn eq(&self, other: &Self) -> bool { +// bool::from(self.ct_eq(other)) +// } +// } + +impl Ord for Scalar { + fn cmp(&self, other: &Self) -> Ordering { + let left = self.to_repr(); + let right = other.to_repr(); + left.iter() + .zip(right.iter()) + .rev() + .find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) { + Ordering::Equal => None, + res => Some(res), + }) + .unwrap_or(Ordering::Equal) + } +} + +impl PartialOrd for Scalar { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl ConditionallySelectable for Scalar { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Scalar([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + ]) + } +} + +/// Constant representing the modulus +/// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +const MODULUS: Scalar = Scalar([ + 0xffff_ffff_0000_0001, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, +]); + +/// The modulus as u32 limbs. +#[cfg(all(feature = "bits", not(target_pointer_width = "64")))] +const MODULUS_LIMBS_32: [u32; 8] = [ + 0x0000_0001, + 0xffff_ffff, + 0xfffe_5bfe, + 0x53bd_a402, + 0x09a1_d805, + 0x3339_d808, + 0x299d_7d48, + 0x73ed_a753, +]; + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 255; + +// GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) +const GENERATOR: Scalar = Scalar([ + 0x0000_000e_ffff_fff1, + 0x17e3_63d3_0018_9c0f, + 0xff9c_5787_6f84_57b0, + 0x3513_3220_8fc5_a8c4, +]); + +impl<'a> Neg for &'a Scalar { + type Output = Scalar; + + #[inline] + fn neg(self) -> Scalar { + self.neg() + } +} + +impl Neg for Scalar { + type Output = Scalar; + + #[inline] + fn neg(self) -> Scalar { + -&self + } +} + +impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn sub(self, rhs: &'b Scalar) -> Scalar { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn add(self, rhs: &'b Scalar) -> Scalar { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn mul(self, rhs: &'b Scalar) -> Scalar { + self.mul(rhs) + } +} + +impl_binops_additive!(Scalar, Scalar); +impl_binops_multiplicative!(Scalar, Scalar); + +/// INV = -(q^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0xffff_fffe_ffff_ffff; + +/// R = 2^256 mod q +const R: Scalar = Scalar([ + 0x0000_0001_ffff_fffe, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f, +]); + +/// R^2 = 2^512 mod q +const R2: Scalar = Scalar([ + 0xc999_e990_f3f2_9c6d, + 0x2b6c_edcb_8792_5c23, + 0x05d3_1496_7254_398f, + 0x0748_d9d9_9f59_ff11, +]); + +/// R^3 = 2^768 mod q +const R3: Scalar = Scalar([ + 0xc62c_1807_439b_73af, + 0x1b3e_0d18_8cf0_6990, + 0x73d1_3c71_c7b5_f418, + 0x6e2a_5bb9_c8db_33e9, +]); + +/// 2^-1 +const TWO_INV: Scalar = Scalar([ + 0x0000_0000_ffff_ffff, + 0xac42_5bfd_0001_a401, + 0xccc6_27f7_f65e_27fa, + 0x0c12_58ac_d662_82b7, +]); + +// 2^S * t = MODULUS - 1 with t odd +const S: u32 = 32; + +/// GENERATOR^t where t * 2^s + 1 = q +/// with t odd. In other words, this +/// is a 2^s root of unity. +/// +/// `GENERATOR = 7 mod q` is a generator +/// of the q - 1 order multiplicative +/// subgroup. +const ROOT_OF_UNITY: Scalar = Scalar([ + 0xb9b5_8d8c_5f0e_466a, + 0x5b1b_4c80_1819_d7ec, + 0x0af5_3ae3_52a3_1e64, + 0x5bf3_adda_19e9_b27b, +]); + +/// ROOT_OF_UNITY^-1 +const ROOT_OF_UNITY_INV: Scalar = Scalar([ + 0x4256_481a_dcf3_219a, + 0x45f3_7b7f_96b6_cad3, + 0xf9c3_f1d7_5f7a_3b27, + 0x2d2f_c049_658a_fd43, +]); + +/// GENERATOR^{2^s} where t * 2^s + 1 = q with t odd. +/// In other words, this is a t root of unity. +const DELTA: Scalar = Scalar([ + 0x70e3_10d3_d146_f96a, + 0x4b64_c089_19e2_99e6, + 0x51e1_1418_6a8b_970d, + 0x6185_d066_27c0_67cb, +]); + +/// ZETA +// sage: modulus = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +//sage: GF(modulus).primitive_element() ^ ((modulus - 1) // 3) +//228988810152649578064853576960394133503 +const ZETA: Scalar = Scalar::from_raw([0x00000000ffffffff, 0xac45a4010001a402, 0, 0]); + +impl Default for Scalar { + #[inline] + fn default() -> Self { + Self::zero() + } +} + +#[cfg(feature = "zeroize")] +impl zeroize::DefaultIsZeroes for Scalar {} + +impl Scalar { + /// Returns zero, the additive identity. + #[inline] + pub const fn zero() -> Scalar { + Scalar([0, 0, 0, 0]) + } + + /// Returns one, the multiplicative identity. + #[inline] + pub const fn one() -> Scalar { + R + } + + /// Doubles this field element. + #[inline] + pub const fn double(&self) -> Scalar { + // TODO: This can be achieved more efficiently with a bitshift. + self.add(self) + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into a `Scalar`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { + let mut tmp = Scalar([0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); + tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + /// Converts an element of `Scalar` into a byte representation in + /// little-endian byte order. + pub fn to_bytes(&self) -> [u8; 32] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + let mut res = [0; 32]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + + res + } + + /// Converts a 512-bit little endian integer into + /// a `Scalar` by reducing by the modulus. + pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar { + Scalar::from_u512([ + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), + ]) + } + + fn from_u512(limbs: [u64; 8]) -> Scalar { + // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits + // with the higher bits multiplied by 2^256. 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^256 = R^3 + // + // and computing their sum in the field. It remains to see that arbitrary 256-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^256 multiplied by + // the modulus. This holds because for any `c` smaller than the modulus, we have + // that (2^256 - 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 d0 = Scalar([limbs[0], limbs[1], limbs[2], limbs[3]]); + let d1 = Scalar([limbs[4], limbs[5], limbs[6], limbs[7]]); + // Convert to Montgomery form + d0 * R2 + d1 * R3 + } + + /// Converts from an integer represented in little endian + /// into its (congruent) `Scalar` representation. + pub const fn from_raw(val: [u64; 4]) -> Self { + (&Scalar(val)).mul(&R2) + } + + /// Squares this element. + #[inline] + pub const fn square(&self) -> Scalar { + let (r1, carry) = mac(0, self.0[0], self.0[1], 0); + let (r2, carry) = mac(0, self.0[0], self.0[2], carry); + let (r3, r4) = mac(0, self.0[0], self.0[3], carry); + + let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); + let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); + + let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); + + let r7 = r6 >> 63; + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let (r0, carry) = mac(0, self.0[0], self.0[0], 0); + let (r1, carry) = adc(0, r1, carry); + let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); + let (r3, carry) = adc(0, r3, carry); + let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); + let (r5, carry) = adc(0, r5, carry); + let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); + let (r7, _) = adc(0, r7, carry); + + Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + pub fn pow(&self, by: &[u64; 4]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + let mut tmp = res; + tmp *= self; + res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); + } + } + res + } + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + /// + /// **This operation is variable time with respect + /// to the exponent.** If the exponent is fixed, + /// this operation is effectively constant time. + pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res.mul_assign(self); + } + } + } + res + } + + /// Computes the multiplicative inverse of this element, + /// failing if the element is zero. + pub fn invert(&self) -> CtOption { + #[inline(always)] + fn square_assign_multi(n: &mut Scalar, num_times: usize) { + for _ in 0..num_times { + *n = n.square(); + } + } + // found using https://github.com/kwantam/addchain + let mut t0 = self.square(); + let mut t1 = t0 * self; + let mut t16 = t0.square(); + let mut t6 = t16.square(); + let mut t5 = t6 * t0; + t0 = t6 * t16; + let mut t12 = t5 * t16; + let mut t2 = t6.square(); + let mut t7 = t5 * t6; + let mut t15 = t0 * t5; + let mut t17 = t12.square(); + t1 *= t17; + let mut t3 = t7 * t2; + let t8 = t1 * t17; + let t4 = t8 * t2; + let t9 = t8 * t7; + t7 = t4 * t5; + let t11 = t4 * t17; + t5 = t9 * t17; + let t14 = t7 * t15; + let t13 = t11 * t12; + t12 = t11 * t17; + t15 *= &t12; + t16 *= &t15; + t3 *= &t16; + t17 *= &t3; + t0 *= &t17; + t6 *= &t0; + t2 *= &t6; + square_assign_multi(&mut t0, 8); + t0 *= &t17; + square_assign_multi(&mut t0, 9); + t0 *= &t16; + square_assign_multi(&mut t0, 9); + t0 *= &t15; + square_assign_multi(&mut t0, 9); + t0 *= &t15; + square_assign_multi(&mut t0, 7); + t0 *= &t14; + square_assign_multi(&mut t0, 7); + t0 *= &t13; + square_assign_multi(&mut t0, 10); + t0 *= &t12; + square_assign_multi(&mut t0, 9); + t0 *= &t11; + square_assign_multi(&mut t0, 8); + t0 *= &t8; + square_assign_multi(&mut t0, 8); + t0 *= self; + square_assign_multi(&mut t0, 14); + t0 *= &t9; + square_assign_multi(&mut t0, 10); + t0 *= &t8; + square_assign_multi(&mut t0, 15); + t0 *= &t7; + square_assign_multi(&mut t0, 10); + t0 *= &t6; + square_assign_multi(&mut t0, 8); + t0 *= &t5; + square_assign_multi(&mut t0, 16); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 7); + t0 *= &t4; + square_assign_multi(&mut t0, 9); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 5); + t0 *= &t1; + square_assign_multi(&mut t0, 5); + t0 *= &t1; + + CtOption::new(t0, !self.ct_eq(&Self::zero())) + } + + #[inline(always)] + const fn montgomery_reduce( + r0: u64, + r1: u64, + r2: u64, + r3: u64, + r4: u64, + r5: u64, + r6: u64, + r7: u64, + ) -> Self { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let (_, carry) = mac(r0, k, MODULUS.0[0], 0); + let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); + let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); + let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); + let (r4, carry2) = adc(r4, 0, carry); + + let k = r1.wrapping_mul(INV); + let (_, carry) = mac(r1, k, MODULUS.0[0], 0); + let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); + let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); + let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); + let (r5, carry2) = adc(r5, carry2, carry); + + let k = r2.wrapping_mul(INV); + let (_, carry) = mac(r2, k, MODULUS.0[0], 0); + let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); + let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); + let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); + let (r6, carry2) = adc(r6, carry2, carry); + + let k = r3.wrapping_mul(INV); + let (_, carry) = mac(r3, k, MODULUS.0[0], 0); + let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); + let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); + let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); + let (r7, _) = adc(r7, carry2, carry); + + // Result may be within MODULUS of the correct value + (&Scalar([r4, r5, r6, r7])).sub(&MODULUS) + } + + /// Multiplies `rhs` by `self`, returning the result. + #[inline] + pub const fn mul(&self, rhs: &Self) -> Self { + // Schoolbook multiplication + + let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); + let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); + let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); + let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); + + let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); + let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); + let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); + let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); + + let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); + let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); + let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); + let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); + + let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); + let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); + let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); + let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); + + Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Subtracts `rhs` from `self`, returning the result. + #[inline] + pub const fn sub(&self, rhs: &Self) -> Self { + let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); + let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); + let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); + let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. + let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); + let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); + let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); + let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); + + Scalar([d0, d1, d2, d3]) + } + + /// Adds `rhs` to `self`, returning the result. + #[inline] + pub const fn add(&self, rhs: &Self) -> Self { + let (d0, carry) = adc(self.0[0], rhs.0[0], 0); + let (d1, carry) = adc(self.0[1], rhs.0[1], carry); + let (d2, carry) = adc(self.0[2], rhs.0[2], carry); + let (d3, _) = adc(self.0[3], rhs.0[3], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Scalar([d0, d1, d2, d3])).sub(&MODULUS) + } + + /// Negates `self`. + #[inline] + pub const fn neg(&self) -> Self { + // Subtract `self` from `MODULUS` to negate. Ignore the final + // borrow because it cannot underflow; self is guaranteed to + // be in the field. + let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); + let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); + let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); + let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); + + // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is + // zero if `self` was zero, and `u64::max_value()` if self was nonzero. + let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); + + Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) + } +} + +impl From for [u8; 32] { + fn from(value: Scalar) -> [u8; 32] { + value.to_bytes() + } +} + +impl<'a> From<&'a Scalar> for [u8; 32] { + fn from(value: &'a Scalar) -> [u8; 32] { + value.to_bytes() + } +} + +impl Field for Scalar { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + + fn random(mut rng: impl RngCore) -> Self { + let mut buf = [0; 64]; + rng.fill_bytes(&mut buf); + Self::from_bytes_wide(&buf) + } + + #[must_use] + fn square(&self) -> Self { + self.square() + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } + + fn invert(&self) -> CtOption { + self.invert() + } + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + ff::helpers::sqrt_ratio_generic(num, div) + } + + fn sqrt(&self) -> CtOption { + // (t - 1) // 2 = 6104339283789297388802252303364915521546564123189034618274734669823 + ff::helpers::sqrt_tonelli_shanks( + self, + [ + 0x7fff_2dff_7fff_ffff, + 0x04d0_ec02_a9de_d201, + 0x94ce_bea4_199c_ec04, + 0x0000_0000_39f6_d3a9, + ], + ) + } + + fn is_zero_vartime(&self) -> bool { + self.0 == Self::zero().0 + } +} + +impl PrimeField for Scalar { + type Repr = [u8; 32]; + + fn from_repr(r: Self::Repr) -> CtOption { + Self::from_bytes(&r) + } + + fn to_repr(&self) -> Self::Repr { + self.to_bytes() + } + + fn is_odd(&self) -> Choice { + Choice::from(self.to_bytes()[0] & 1) + } + + const MODULUS: &'static str = + "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"; + const NUM_BITS: u32 = MODULUS_BITS; + const CAPACITY: u32 = Self::NUM_BITS - 1; + const TWO_INV: Self = TWO_INV; + const MULTIPLICATIVE_GENERATOR: Self = GENERATOR; + const S: u32 = S; + const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; + const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; + const DELTA: Self = DELTA; +} + +impl WithSmallOrderMulGroup<3> for Scalar { + const ZETA: Self = ZETA; +} + +#[cfg(all(feature = "bits", not(target_pointer_width = "64")))] +type ReprBits = [u32; 8]; + +#[cfg(all(feature = "bits", target_pointer_width = "64"))] +type ReprBits = [u64; 4]; + +#[cfg(feature = "bits")] +impl PrimeFieldBits for Scalar { + type ReprBits = ReprBits; + + fn to_le_bits(&self) -> FieldBits { + let bytes = self.to_bytes(); + + #[cfg(not(target_pointer_width = "64"))] + let limbs = [ + u32::from_le_bytes(bytes[0..4].try_into().unwrap()), + u32::from_le_bytes(bytes[4..8].try_into().unwrap()), + u32::from_le_bytes(bytes[8..12].try_into().unwrap()), + u32::from_le_bytes(bytes[12..16].try_into().unwrap()), + u32::from_le_bytes(bytes[16..20].try_into().unwrap()), + u32::from_le_bytes(bytes[20..24].try_into().unwrap()), + u32::from_le_bytes(bytes[24..28].try_into().unwrap()), + u32::from_le_bytes(bytes[28..32].try_into().unwrap()), + ]; + + #[cfg(target_pointer_width = "64")] + let limbs = [ + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + u64::from_le_bytes(bytes[16..24].try_into().unwrap()), + u64::from_le_bytes(bytes[24..32].try_into().unwrap()), + ]; + + FieldBits::new(limbs) + } + + fn char_le_bits() -> FieldBits { + #[cfg(not(target_pointer_width = "64"))] + { + FieldBits::new(MODULUS_LIMBS_32) + } + + #[cfg(target_pointer_width = "64")] + FieldBits::new(MODULUS.0) + } +} + +impl core::iter::Sum for Scalar +where + T: core::borrow::Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::zero(), |acc, item| acc + item.borrow()) + } +} + +impl core::iter::Product for Scalar +where + T: core::borrow::Borrow, +{ + fn product(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::one(), |acc, item| acc * item.borrow()) + } +} + +impl From<[u64; 4]> for Scalar { + fn from(digits: [u64; 4]) -> Self { + Self::from_raw(digits) + } +} + +impl ff::FromUniformBytes<64> for Scalar { + /// Converts a 512-bit little endian integer into + /// an `Fq` by reducing by the modulus. + fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { + Self::from_u512([ + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + u64::from_le_bytes(bytes[16..24].try_into().unwrap()), + u64::from_le_bytes(bytes[24..32].try_into().unwrap()), + u64::from_le_bytes(bytes[32..40].try_into().unwrap()), + u64::from_le_bytes(bytes[40..48].try_into().unwrap()), + u64::from_le_bytes(bytes[48..56].try_into().unwrap()), + u64::from_le_bytes(bytes[56..64].try_into().unwrap()), + ]) + } +} + +impl From for Scalar { + fn from(bit: bool) -> Scalar { + if bit { + Scalar::one() + } else { + Scalar::zero() + } + } +} + +//test mod +#[cfg(test)] +mod tests { + use super::*; + + use std::ops::AddAssign; + + #[test] + fn test_constants() { + assert_eq!( + Scalar::MODULUS, + "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", + ); + + assert_eq!(Scalar::from(2) * Scalar::TWO_INV, Scalar::ONE); + + assert_eq!( + Scalar::ROOT_OF_UNITY * Scalar::ROOT_OF_UNITY_INV, + Scalar::ONE, + ); + + // ROOT_OF_UNITY^{2^s} mod m == 1 + assert_eq!( + Scalar::ROOT_OF_UNITY.pow(&[1u64 << Scalar::S, 0, 0, 0]), + Scalar::ONE, + ); + + // DELTA^{t} mod m == 1 + assert_eq!( + Scalar::DELTA.pow(&[ + 0xfffe_5bfe_ffff_ffff, + 0x09a1_d805_53bd_a402, + 0x299d_7d48_3339_d808, + 0x0000_0000_73ed_a753, + ]), + Scalar::ONE, + ); + } + + #[test] + fn test_inv() { + // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating + // by totient(2**64) - 1 + + let mut inv = 1u64; + for _ in 0..63 { + inv = inv.wrapping_mul(inv); + inv = inv.wrapping_mul(MODULUS.0[0]); + } + inv = inv.wrapping_neg(); + + assert_eq!(inv, INV); + } + + #[cfg(feature = "std")] + #[test] + fn test_debug() { + assert_eq!( + format!("{:?}", Scalar::zero()), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:?}", Scalar::one()), + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + assert_eq!( + format!("{:?}", R2), + "0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe" + ); + } + + #[test] + fn test_equality() { + assert_eq!(Scalar::zero(), Scalar::zero()); + assert_eq!(Scalar::one(), Scalar::one()); + assert_eq!(R2, R2); + + assert!(Scalar::zero() != Scalar::one()); + assert!(Scalar::one() != R2); + } + + #[test] + fn test_to_bytes() { + assert_eq!( + Scalar::zero().to_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ] + ); + + assert_eq!( + Scalar::one().to_bytes(), + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ] + ); + + assert_eq!( + R2.to_bytes(), + [ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, + 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + ] + ); + + assert_eq!( + (-&Scalar::one()).to_bytes(), + [ + 0, 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 + ] + ); + } + + #[test] + fn test_from_bytes() { + assert_eq!( + Scalar::from_bytes(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]) + .unwrap(), + Scalar::zero() + ); + + assert_eq!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]) + .unwrap(), + Scalar::one() + ); + + assert_eq!( + Scalar::from_bytes(&[ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, + 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + ]) + .unwrap(), + R2 + ); + + // -1 should work + assert!(bool::from( + Scalar::from_bytes(&[ + 0, 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 + ]) + .is_some() + )); + + // modulus is invalid + assert!(bool::from( + Scalar::from_bytes(&[ + 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 + ]) + .is_none() + )); + + // Anything larger than the modulus is invalid + assert!(bool::from( + Scalar::from_bytes(&[ + 2, 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 + ]) + .is_none() + )); + assert!(bool::from( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, + 8, 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_none() + )); + assert!(bool::from( + Scalar::from_bytes(&[ + 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, 116 + ]) + .is_none() + )); + } + + #[test] + fn test_from_u512_zero() { + assert_eq!( + Scalar::zero(), + Scalar::from_u512([ + MODULUS.0[0], + MODULUS.0[1], + MODULUS.0[2], + MODULUS.0[3], + 0, + 0, + 0, + 0 + ]) + ); + } + + #[test] + fn test_from_u512_r() { + assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); + } + + #[test] + fn test_from_u512_r2() { + assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); + } + + #[test] + fn test_from_u512_max() { + let max_u64 = 0xffff_ffff_ffff_ffff; + assert_eq!( + R3 - R, + Scalar::from_u512([ + max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64 + ]) + ); + } + + #[test] + fn test_from_bytes_wide_r2() { + assert_eq!( + R2, + Scalar::from_bytes_wide(&[ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, + 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + ); + } + + #[test] + fn test_from_bytes_wide_negative_one() { + assert_eq!( + -&Scalar::one(), + Scalar::from_bytes_wide(&[ + 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + ); + } + + #[test] + fn test_from_bytes_wide_maximum() { + assert_eq!( + Scalar([ + 0xc62c_1805_439b_73b1, + 0xc2b9_551e_8ced_218e, + 0xda44_ec81_daf9_a422, + 0x5605_aa60_1c16_2e79, + ]), + Scalar::from_bytes_wide(&[0xff; 64]) + ); + } + + #[test] + fn test_zero() { + assert_eq!(Scalar::zero(), -&Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); + } + + #[cfg(test)] + const LARGEST: Scalar = Scalar([ + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ]); + + #[test] + fn test_addition() { + let mut tmp = LARGEST; + tmp += &LARGEST; + + assert_eq!( + tmp, + Scalar([ + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ]) + ); + + let mut tmp = LARGEST; + tmp += &Scalar([1, 0, 0, 0]); + + assert_eq!(tmp, Scalar::zero()); + } + + #[test] + fn test_negation() { + let tmp = -&LARGEST; + + assert_eq!(tmp, Scalar([1, 0, 0, 0])); + + let tmp = -&Scalar::zero(); + assert_eq!(tmp, Scalar::zero()); + let tmp = -&Scalar([1, 0, 0, 0]); + assert_eq!(tmp, LARGEST); + } + + #[test] + fn test_subtraction() { + let mut tmp = LARGEST; + tmp -= &LARGEST; + + assert_eq!(tmp, Scalar::zero()); + + let mut tmp = Scalar::zero(); + tmp -= &LARGEST; + + let mut tmp2 = MODULUS; + tmp2 -= &LARGEST; + + assert_eq!(tmp, tmp2); + } + + #[test] + fn test_multiplication() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp *= &cur; + + let mut tmp2 = Scalar::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); + } + } + + #[test] + fn test_squaring() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp = tmp.square(); + + let mut tmp2 = Scalar::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); + } + } + + #[test] + fn test_inversion() { + assert!(bool::from(Scalar::zero().invert().is_none())); + assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); + assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); + + let mut tmp = R2; + + for _ in 0..100 { + let mut tmp2 = tmp.invert().unwrap(); + tmp2.mul_assign(&tmp); + + assert_eq!(tmp2, Scalar::one()); + + tmp.add_assign(&R2); + } + } + + #[test] + fn test_invert_is_pow() { + let q_minus_2 = [ + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ]; + + let mut r1 = R; + let mut r2 = R; + let mut r3 = R; + + for _ in 0..100 { + r1 = r1.invert().unwrap(); + r2 = r2.pow_vartime(&q_minus_2); + r3 = r3.pow(&q_minus_2); + + assert_eq!(r1, r2); + assert_eq!(r2, r3); + // Add R so we check something different next time around + r1.add_assign(&R); + r2 = r1; + r3 = r1; + } + } + + #[test] + fn test_sqrt() { + { + assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero()); + } + + let mut square = Scalar([ + 0x46cd_85a5_f273_077e, + 0x1d30_c47d_d68f_c735, + 0x77f6_56f6_0bec_a0eb, + 0x494a_a01b_df32_468d, + ]); + + let mut none_count = 0; + + for _ in 0..100 { + let square_root = square.sqrt(); + if bool::from(square_root.is_none()) { + none_count += 1; + } else { + assert_eq!(square_root.unwrap() * square_root.unwrap(), square); + } + square -= Scalar::one(); + } + + assert_eq!(49, none_count); + } + + #[test] + fn test_from_raw() { + assert_eq!( + Scalar::from_raw([ + 0x0001_ffff_fffd, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f, + ]), + Scalar::from_raw([0xffff_ffff_ffff_ffff; 4]) + ); + + assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); + + assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); + } + + #[test] + fn test_double() { + let a = Scalar::from_raw([ + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562, + ]); + + assert_eq!(a.double(), a + a); + } + + #[cfg(feature = "zeroize")] + #[test] + fn test_zeroize() { + use zeroize::Zeroize; + + let mut a = Scalar::from_raw([ + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562, + ]); + a.zeroize(); + assert!(bool::from(a.is_zero())); + } +} diff --git a/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat b/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat new file mode 100644 index 00000000..ea8cd676 Binary files /dev/null and b/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat differ diff --git a/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat b/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat new file mode 100644 index 00000000..86abfba9 Binary files /dev/null and b/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat differ diff --git a/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat b/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat new file mode 100644 index 00000000..a40bbe25 Binary files /dev/null and b/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat differ diff --git a/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat b/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat new file mode 100644 index 00000000..92e4bc52 Binary files /dev/null and b/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat differ diff --git a/src/bls12_381/tests/mod.rs b/src/bls12_381/tests/mod.rs new file mode 100644 index 00000000..6603d09d --- /dev/null +++ b/src/bls12_381/tests/mod.rs @@ -0,0 +1,231 @@ +use super::*; + +macro_rules! test_vectors { + ($projective:ident, $affine:ident, $serialize:ident, $deserialize:ident, $expected:ident) => { + let mut e = $projective::identity(); + + let mut v = vec![]; + { + let mut expected = $expected; + for _ in 0..1000 { + let e_affine = $affine::from(e); + let encoded = e_affine.$serialize(); + v.extend_from_slice(&encoded[..]); + + let mut decoded = encoded; + let len_of_encoding = decoded.len(); + (&mut decoded[..]).copy_from_slice(&expected[0..len_of_encoding]); + expected = &expected[len_of_encoding..]; + let decoded = $affine::$deserialize(&decoded).unwrap(); + assert_eq!(e_affine, decoded); + + e = &e + &$projective::generator(); + } + } + + assert_eq!(&v[..], $expected); + }; +} + +#[test] +fn g1_uncompressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g1_uncompressed_valid_test_vectors.dat"); + test_vectors!( + G1Projective, + G1Affine, + to_uncompressed_be, + from_uncompressed_be, + bytes + ); +} + +#[test] +fn g1_compressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat"); + test_vectors!( + G1Projective, + G1Affine, + to_compressed_be, + from_compressed_be, + bytes + ); +} + +#[test] +fn g2_uncompressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g2_uncompressed_valid_test_vectors.dat"); + test_vectors!( + G2Projective, + G2Affine, + to_uncompressed_be, + from_uncompressed_be, + bytes + ); +} + +#[test] +fn g2_compressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat"); + test_vectors!( + G2Projective, + G2Affine, + to_compressed_be, + from_compressed_be, + bytes + ); +} + +#[test] +#[cfg(all(feature = "alloc", feature = "pairing"))] +fn test_pairing_result_against_relic() { + /* + Sent to me from Diego Aranha (author of RELIC library): + 1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6 + 089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F + 1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87 + 193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F + 01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5 + 018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6 + 19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D + 06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A + 11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57 + 03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2 + 04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF + 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 + */ + + let a = G1Affine::generator(); + let b = G2Affine::generator(); + + use super::fp::Fp; + use super::fp12::Fp12; + use super::fp2::Fp2; + use super::fp6::Fp6; + + let res = pairing(&a, &b); + + let prep = G2Prepared::from(b); + + assert_eq!( + res, + multi_miller_loop(&[(&a, &prep)]).final_exponentiation() + ); + + assert_eq!( + res.0, + Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1972_e433_a01f_85c5, + 0x97d3_2b76_fd77_2538, + 0xc8ce_546f_c96b_cdf9, + 0xcef6_3e73_66d4_0614, + 0xa611_3427_8184_3780, + 0x13f3_448a_3fc6_d825, + ]), + c1: Fp::from_raw_unchecked([ + 0xd263_31b0_2e9d_6995, + 0x9d68_a482_f779_7e7d, + 0x9c9b_2924_8d39_ea92, + 0xf480_1ca2_e131_07aa, + 0xa16c_0732_bdbc_b066, + 0x083c_a4af_ba36_0478, + ]) + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x59e2_61db_0916_b641, + 0x2716_b6f4_b23e_960d, + 0xc8e5_5b10_a0bd_9c45, + 0x0bdb_0bd9_9c4d_eda8, + 0x8cf8_9ebf_57fd_aac5, + 0x12d6_b792_9e77_7a5e, + ]), + c1: Fp::from_raw_unchecked([ + 0x5fc8_5188_b0e1_5f35, + 0x34a0_6e3a_8f09_6365, + 0xdb31_26a6_e02a_d62c, + 0xfc6f_5aa9_7d9a_990b, + 0xa12f_55f5_eb89_c210, + 0x1723_703a_926f_8889, + ]) + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x9358_8f29_7182_8778, + 0x43f6_5b86_11ab_7585, + 0x3183_aaf5_ec27_9fdf, + 0xfa73_d7e1_8ac9_9df6, + 0x64e1_76a6_a64c_99b0, + 0x179f_a78c_5838_8f1f, + ]), + c1: Fp::from_raw_unchecked([ + 0x672a_0a11_ca2a_ef12, + 0x0d11_b9b5_2aa3_f16b, + 0xa444_12d0_699d_056e, + 0xc01d_0177_221a_5ba5, + 0x66e0_cede_6c73_5529, + 0x05f5_a71e_9fdd_c339, + ]) + } + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xd30a_88a1_b062_c679, + 0x5ac5_6a5d_35fc_8304, + 0xd0c8_34a6_a81f_290d, + 0xcd54_30c2_da37_07c7, + 0xf0c2_7ff7_8050_0af0, + 0x0924_5da6_e2d7_2eae, + ]), + c1: Fp::from_raw_unchecked([ + 0x9f2e_0676_791b_5156, + 0xe2d1_c823_4918_fe13, + 0x4c9e_459f_3c56_1bf4, + 0xa3e8_5e53_b9d3_e3c1, + 0x820a_121e_21a7_0020, + 0x15af_6183_41c5_9acc, + ]) + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7c95_658c_2499_3ab1, + 0x73eb_3872_1ca8_86b9, + 0x5256_d749_4774_34bc, + 0x8ba4_1902_ea50_4a8b, + 0x04a3_d3f8_0c86_ce6d, + 0x18a6_4a87_fb68_6eaa, + ]), + c1: Fp::from_raw_unchecked([ + 0xbb83_e71b_b920_cf26, + 0x2a52_77ac_92a7_3945, + 0xfc0e_e59f_94f0_46a0, + 0x7158_cdf3_7860_58f7, + 0x7cc1_061b_82f9_45f6, + 0x03f8_47aa_9fdb_e567, + ]) + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8078_dba5_6134_e657, + 0x1cd7_ec9a_4399_8a6e, + 0xb1aa_599a_1a99_3766, + 0xc9a0_f62f_0842_ee44, + 0x8e15_9be3_b605_dffa, + 0x0c86_ba0d_4af1_3fc2, + ]), + c1: Fp::from_raw_unchecked([ + 0xe80f_f2a0_6a52_ffb1, + 0x7694_ca48_721a_906c, + 0x7583_183e_03b0_8514, + 0xf567_afdd_40ce_e4e2, + 0x9a6d_96d2_e526_a5fc, + 0x197e_9f49_861f_2242, + ]) + } + } + } + ); +} diff --git a/src/lib.rs b/src/lib.rs index 030c11b2..a2c93b36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod msm; pub mod multicore; pub mod serde; +pub mod bls12_381; pub mod bn256; pub mod grumpkin; pub mod pasta;