diff --git a/Cargo.toml b/Cargo.toml index a8632560..fc5be68f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ ark-std = { version = "0.3" } bincode = "1.3.3" [dependencies] +bitvec = "1.0.1" subtle = "2.4" ff = "0.13.0" group = "0.13.0" diff --git a/src/bls12_381/fp.rs b/src/bls12_381/fp.rs new file mode 100644 index 00000000..675cb570 --- /dev/null +++ b/src/bls12_381/fp.rs @@ -0,0 +1,1233 @@ +//! This module provides an implementation of the BLS12-381 base field `GF(p)` +//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` + +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; +use ff::{Field, PrimeField, WithSmallOrderMulGroup}; +use rand_core::RngCore; +use std::convert::{TryFrom, TryInto}; +use std::io::Write; +use std::ops::Deref; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + +use crate::util::{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, +}; + +// 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)] +#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] +pub struct Fp(pub(crate) [u64; 6]); + +impl Ord for Fp { + fn cmp(&self, other: &Self) -> core::cmp::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) { + core::cmp::Ordering::Equal => None, + res => Some(res), + }) + .unwrap_or(core::cmp::Ordering::Equal) + } +} + +impl PartialOrd for Fp { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// Wrapper needed, because we don't have Default implemented for [0u8; 48] +#[derive(Copy, Clone)] +pub struct FpRepr([u8; 48]); + +impl Default for FpRepr { + fn default() -> Self { + Self([0u8; 48]) + } +} + +impl Deref for FpRepr { + type Target = [u8; 48]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl AsRef<[u8]> for FpRepr { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for FpRepr { + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut_slice() + } +} + +impl WithSmallOrderMulGroup<3> for Fp { + const ZETA: Self = ZETA; +} + +use crate::impl_sum_prod; +impl_sum_prod!(Fp); + +/// 2^-1 +const TWO_INV: Fp = Fp([ + 0x1804_0000_0001_5554, + 0x8550_0005_3ab0_0001, + 0x633c_b57c_253c_276f, + 0x6e22_d1ec_31eb_b502, + 0xd391_6126_f2d1_4ca2, + 0x17fb_b857_1a00_6596, +]); + +/// Computed using sage, GF(0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab).primitive_element() +const MULTIPLICATIVE_GENERATOR: Fp = Fp::from_raw_unchecked([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]); + +// TODO: Can we simply put 0 here:: +const ROOT_OF_UNITY: Fp = Fp::zero(); + +// Unused constant for base field +const ROOT_OF_UNITY_INV: Fp = Fp::zero(); + +// Unused constant for base field +const DELTA: Fp = Fp::zero(); + +/// `ZETA^3 = 1 mod r` where `ZETA^2 != 1 mod r` +// computed with sage math: +// R.=PolynomialRing(GF(0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab)) +// L=(k^3-1).roots();L +const ZETA: Fp = Fp::from_raw_unchecked([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, +]); + +impl From for Fp { + fn from(val: u64) -> Fp { + Fp([val, 0, 0, 0, 0, 0]) + } +} + +impl Field for Fp { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + + fn random(mut rng: impl RngCore) -> Self { + Self::random(&mut rng) + } + + #[inline(always)] + fn square(&self) -> Self { + self.square() + } + + fn double(&self) -> Self { + self + 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() + } + + fn is_zero_vartime(&self) -> bool { + self.0 == Self::zero().0 + } +} + +impl PrimeField for Fp { + type Repr = FpRepr; + + fn from_repr(repr: Self::Repr) -> CtOption { + Self::from_bytes(&repr) + } + + fn to_repr(&self) -> Self::Repr { + FpRepr(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; // todo: check + const TWO_INV: Self = TWO_INV; + const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; + const S: u32 = 1; // MODULUS - 1 / 2 is odd. + const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; + const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; + const DELTA: Self = DELTA; +} + +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, "{:02x}", b)?; + } + 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 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), + ]) + } +} + +/// 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, +]); + +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 Fp { + pub const fn size() -> usize { + 48 + } + + /// 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 + } + + pub fn is_zero(&self) -> Choice { + self.ct_eq(&Fp::zero()) + } + + /// 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(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)) + } + + /// Converts an element of `Fp` into a byte representation in + /// big-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 + } + + pub(crate) 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] + pub fn sqrt(&self) -> CtOption { + // 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. + + 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] + 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] + 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] + 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)] + #[allow(clippy::too_many_arguments)] + 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] + 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) + } + + /// Lexicographic comparison of Montgomery forms. + #[inline(always)] + const fn is_less_than(x: &[u64; 6], y: &[u64; 6]) -> bool { + let (_, borrow) = sbb(x[0], y[0], 0); + let (_, borrow) = sbb(x[1], y[1], borrow); + let (_, borrow) = sbb(x[2], y[2], borrow); + let (_, borrow) = sbb(x[3], y[3], borrow); + let (_, borrow) = sbb(x[4], y[4], borrow); + let (_, borrow) = sbb(x[5], y[5], borrow); + borrow >> 63 == 1 + } +} + +impl crate::serde::SerdeObject for Fp { + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { + debug_assert_eq!(bytes.len(), 48); + let inner = + [0, 8, 16, 24, 32, 40].map(|i| u64::from_le_bytes(bytes[i..i + 8].try_into().unwrap())); + + Self(inner) + } + fn from_raw_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 48 { + return None; + } + let elt = Self::from_raw_bytes_unchecked(bytes); + Self::is_less_than(&elt.0, &MODULUS).then(|| elt) + } + fn to_raw_bytes(&self) -> Vec { + let mut res = Vec::with_capacity(48); + for limb in self.0.iter() { + res.extend_from_slice(&limb.to_le_bytes()); + } + res + } + fn read_raw_unchecked(reader: &mut R) -> Self { + let inner = [(); 6].map(|_| { + let mut buf = [0; 8]; + reader.read_exact(&mut buf).unwrap(); + u64::from_le_bytes(buf) + }); + Self(inner) + } + fn read_raw(reader: &mut R) -> std::io::Result { + let mut inner = [0u64; 6]; + for limb in inner.iter_mut() { + let mut buf = [0; 8]; + reader.read_exact(&mut buf)?; + *limb = u64::from_le_bytes(buf); + } + let elt = Self(inner); + Self::is_less_than(&elt.0, &MODULUS) + .then(|| elt) + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "input number is not less than field modulus", + ) + }) + } + + fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { + for limb in self.0.iter() { + writer.write_all(&limb.to_le_bytes())?; + } + Ok(()) + } +} + +#[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_debug() { + assert_eq!( + format!( + "{:?}", + 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, + ]) + ), + "0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704" + ); +} + +#[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(); + let b = Fp::from_bytes(&tmp).unwrap(); + + assert_eq!(a, b); + } + + assert_eq!( + -Fp::one(), + Fp::from_bytes(&[ + 170, 170, 255, 255, 255, 255, 254, 185, 255, 255, 83, 177, 254, 255, 171, 30, 36, 246, + 176, 246, 160, 210, 48, 103, 191, 18, 133, 243, 132, 75, 119, 100, 215, 172, 75, 67, + 182, 167, 27, 75, 154, 230, 127, 57, 234, 17, 1, 26 + ]) + .unwrap() + ); + + assert!(bool::from( + Fp::from_bytes(&[ + 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(&[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())); +} diff --git a/src/bls12_381/fp12.rs b/src/bls12_381/fp12.rs new file mode 100644 index 00000000..b71e8b47 --- /dev/null +++ b/src/bls12_381/fp12.rs @@ -0,0 +1,648 @@ +use crate::bls12_381::fp::*; +use crate::bls12_381::fp2::*; +use crate::bls12_381::fp6::*; + +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; +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 rand_core::RngCore; + +/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / 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 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() + } +} + +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 fn zero() -> Self { + Fp12 { + c0: Fp6::zero(), + c1: Fp6::zero(), + } + } + + #[inline] + pub 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 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<'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); + +#[test] +fn test_arithmetic() { + use crate::bls12_381::fp::*; + use crate::bls12_381::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() + ); +} diff --git a/src/bls12_381/fp2.rs b/src/bls12_381/fp2.rs new file mode 100644 index 00000000..04efb875 --- /dev/null +++ b/src/bls12_381/fp2.rs @@ -0,0 +1,1147 @@ +//! This module implements arithmetic over the quadratic extension field Fp2. + +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; +use ff::{Field, PrimeField, WithSmallOrderMulGroup}; +use rand_core::RngCore; +use std::convert::TryInto; +use std::ops::{BitAnd, Deref}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + +use crate::bls12_381::fp::Fp; +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, +}; + +#[derive(Copy, Clone)] +#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] +pub struct Fp2 { + pub c0: Fp, + pub c1: Fp, +} + +impl Ord for Fp2 { + fn cmp(&self, other: &Self) -> core::cmp::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) { + core::cmp::Ordering::Equal => None, + res => Some(res), + }) + .unwrap_or(core::cmp::Ordering::Equal) + } +} + +impl PartialOrd for Fp2 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl WithSmallOrderMulGroup<3> for Fp2 { + const ZETA: Self = ZETA; +} + +// Wrapper needed, because we don't have Default implemented for [0u8; 48] +#[derive(Copy, Clone)] +pub struct Fp2Repr([u8; 96]); + +impl Default for Fp2Repr { + fn default() -> Self { + Self([0u8; 96]) + } +} + +impl Deref for Fp2Repr { + type Target = [u8; 96]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl AsRef<[u8]> for Fp2Repr { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for Fp2Repr { + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut_slice() + } +} + +use crate::impl_sum_prod; +impl_sum_prod!(Fp2); + +/// 2^-1 +const TWO_INV: Fp2 = Fp2 { + c0: Fp([ + 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([0, 0, 0, 0, 0, 0]), +}; + +/// Computed using sage, GF(0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab).primitive_element() +const MULTIPLICATIVE_GENERATOR: Fp2 = Fp2 { + c0: Fp([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]), + c1: Fp([0, 0, 0, 0, 0, 0]), +}; + +// TODO: Can we simply put 0 here:: +const ROOT_OF_UNITY: Fp2 = Fp2::zero(); + +// Unused constant for base field +const ROOT_OF_UNITY_INV: Fp2 = Fp2::zero(); + +// Unused constant for base field +const DELTA: Fp2 = Fp2::zero(); + +impl From for Fp2 { + fn from(val: u64) -> Fp2 { + Fp2 { + c0: Fp([val, 0, 0, 0, 0, 0]), + c1: Fp([0, 0, 0, 0, 0, 0]), + } + } +} + +// Fp::ZETA^2 +const ZETA: Fp2 = Fp2 { + c0: Fp([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fp([0, 0, 0, 0, 0, 0]), +}; + +impl Field for Fp2 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + + fn random(mut rng: impl RngCore) -> Self { + Self::random(&mut rng) + } + + #[must_use] + fn square(&self) -> Self { + self.square() + } + + #[must_use] + fn double(&self) -> Self { + self + 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() + } + + fn is_zero(&self) -> Choice { + self.c0.is_zero().bitand(self.c1.is_zero()) + } +} + +impl PrimeField for Fp2 { + type Repr = Fp2Repr; + + fn from_repr(repr: Self::Repr) -> CtOption { + let c0 = Fp::from_bytes(&repr[..48].try_into().unwrap()).unwrap(); + let c1 = Fp::from_bytes(&repr[48..].try_into().unwrap()).unwrap(); + + CtOption::new(Self { c0, c1 }, Choice::from(1)) + } + + fn to_repr(&self) -> Self::Repr { + let mut bytes = [0u8; 96]; + bytes[..48].copy_from_slice(&self.c0.to_bytes()); + bytes[48..].copy_from_slice(&self.c1.to_bytes()); + Fp2Repr(bytes) + } + + fn is_odd(&self) -> Choice { + Choice::from(self.to_repr().as_ref()[0] & 1) + } + + const MODULUS: &'static str = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"; + const NUM_BITS: u32 = 381; + const CAPACITY: u32 = Self::NUM_BITS - 1; // todo: check + const TWO_INV: Self = TWO_INV; + const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; + const S: u32 = 1; // MODULUS - 1 / 2 is odd. + const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; + const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; + const DELTA: Self = DELTA; +} + +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 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 { + pub const fn size() -> usize { + 96 + } + + #[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), + } + } + + /// 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; 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 + } + + /// Vartime exponentiation for larger exponents, only + /// used in testing and not exposed through the public API. + #[cfg(all(test, feature = "experimental"))] + pub(crate) 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 `Fq`, 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(), + ) + } + + /// Converts an element of `Fq` into a byte representation in + /// little-endian byte order. + 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 + } +} + +impl crate::serde::SerdeObject for Fp2 { + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { + debug_assert_eq!(bytes.len(), 96); + let [c0, c1] = [0, 48].map(|i| Fp::from_raw_bytes_unchecked(&bytes[i..i + 48])); + Self { c0, c1 } + } + fn from_raw_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 96 { + return None; + } + let [c0, c1] = [0, 48].map(|i| Fp::from_raw_bytes(&bytes[i..i + 48])); + c0.zip(c1).map(|(c0, c1)| Self { c0, c1 }) + } + fn to_raw_bytes(&self) -> Vec { + let mut res = Vec::with_capacity(96); + for limb in self.c0.0.iter().chain(self.c1.0.iter()) { + res.extend_from_slice(&limb.to_le_bytes()); + } + res + } + fn read_raw_unchecked(reader: &mut R) -> Self { + let [c0, c1] = [(); 2].map(|_| Fp::read_raw_unchecked(reader)); + Self { c0, c1 } + } + fn read_raw(reader: &mut R) -> std::io::Result { + let c0 = Fp::read_raw(reader)?; + let c1 = Fp::read_raw(reader)?; + Ok(Self { c0, c1 }) + } + fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { + self.c0.write_raw(writer)?; + self.c1.write_raw(writer) + } +} + +#[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())); +} diff --git a/src/bls12_381/fp6.rs b/src/bls12_381/fp6.rs new file mode 100644 index 00000000..41162fca --- /dev/null +++ b/src/bls12_381/fp6.rs @@ -0,0 +1,560 @@ +use crate::bls12_381::fp::*; +use crate::bls12_381::fp2::*; + +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; +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 rand_core::RngCore; + +/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / 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 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() + } +} + +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 fn zero() -> Self { + Fp6 { + c0: Fp2::zero(), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } + + #[inline] + pub 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 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 crate::bls12_381::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()); +} diff --git a/src/bls12_381/g1.rs b/src/bls12_381/g1.rs new file mode 100644 index 00000000..08d986d2 --- /dev/null +++ b/src/bls12_381/g1.rs @@ -0,0 +1,154 @@ +//! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. + +use core::cmp; +use core::iter::Sum; +use core::ops::{Add, Mul, Neg, Sub}; +use group::{prime::PrimeCurveAffine, Curve, Group, GroupEncoding}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use ff::Field; +use ff::PrimeField; +use ff::WithSmallOrderMulGroup; +use group::cofactor::CofactorGroup; + +use pasta_curves::arithmetic::{Coordinates, CurveAffine, CurveExt}; +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + +use crate::bls12_381::fp::Fp; +use crate::bls12_381::Scalar; +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, + new_curve_impl, +}; + +new_curve_impl!( + (pub), + G1Projective, + G1Affine, + false, + Fp, + Scalar, + (G1_GENERATOR_X, G1_GENERATOR_Y), + G1_B, + "bls12_381_g1", +); + +const G1_GENERATOR_X: Fp = 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, +]); + +const G1_GENERATOR_Y: Fp = 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, +]); + +const G1_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, +]); + +/// 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, +]); + +const BLS_X: u64 = 0xd201_0000_0001_0000; +const BLS_X_IS_NEGATIVE: bool = true; + +impl G1Projective { + /// 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 = 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 BLS_X_IS_NEGATIVE { + xself = -xself; + } + xself + } +} + +impl CofactorGroup for G1Projective { + type Subgroup = G1Projective; + + /// 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$. + fn clear_cofactor(&self) -> G1Projective { + self - self.mul_by_x() + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, self.is_torsion_free()) + } + + 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 = self.mul_by_x().mul_by_x().neg(); + let endomorphism_p = endomorphism(&self.to_affine()); + minus_x_squared_times_p.ct_eq(&G1Projective::from(endomorphism_p)) + } +} + +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 +} + +#[cfg(test)] +mod tests { + use crate::bls12_381::G1Projective; + + #[test] + fn test_curve() { + crate::tests::curve::curve_tests::(); + } + + #[test] + fn test_serialization() { + crate::tests::curve::random_serialization_test::(); + #[cfg(feature = "derive_serde")] + { + crate::tests::curve::random_serde_test::(); + } + } +} diff --git a/src/bls12_381/g2.rs b/src/bls12_381/g2.rs new file mode 100644 index 00000000..200867e5 --- /dev/null +++ b/src/bls12_381/g2.rs @@ -0,0 +1,228 @@ +//! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. + +use core::cmp; +use core::iter::Sum; +use core::ops::{Add, Mul, Neg, Sub}; +use group::{prime::PrimeCurveAffine, Curve, Group, GroupEncoding}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use pasta_curves::arithmetic::{Coordinates, CurveAffine, CurveExt}; +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + +use ff::WithSmallOrderMulGroup; +use ff::{Field, PrimeField}; +use group::cofactor::CofactorGroup; + +use crate::bls12_381::fp::Fp; +use crate::bls12_381::fp2::Fp2; +use crate::bls12_381::Scalar; +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, + new_curve_impl, +}; + +new_curve_impl!( + (pub), + G2Projective, + G2Affine, + false, + Fp2, + Scalar, + (G2_GENERATOR_X, G2_GENERATOR_Y), + G2_B, + "bls12_381_g2", +); + +const G2_GENERATOR_X: Fp2 = 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, + ]), +}; + +const G2_GENERATOR_Y: Fp2 = 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, + ]), +}; + +const G2_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, + ]), +}; + +impl G2Projective { + 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 `crate::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 = crate::bls12_381::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 crate::bls12_381::BLS_X_IS_NEGATIVE { + xself = -xself; + } + xself + } +} + +impl CofactorGroup for G2Projective { + type Subgroup = G2Projective; + + fn clear_cofactor(&self) -> Self { + 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) + } + + fn into_subgroup(self) -> CtOption { + unimplemented!(); + } + + 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 + self.psi().ct_eq(&self.mul_by_x()) + } +} + +#[cfg(test)] +mod tests { + use crate::bls12_381::G2Projective; + + #[test] + fn test_curve() { + crate::tests::curve::curve_tests::(); + } + + #[test] + fn test_serialization() { + crate::tests::curve::random_serialization_test::(); + #[cfg(feature = "derive_serde")] + { + crate::tests::curve::random_serde_test::(); + } + } +} diff --git a/src/bls12_381/mod.rs b/src/bls12_381/mod.rs new file mode 100644 index 00000000..55b7f7d0 --- /dev/null +++ b/src/bls12_381/mod.rs @@ -0,0 +1,17 @@ +mod fp; +mod fp12; +mod fp2; +mod fp6; +mod g1; +mod g2; +mod pairings; +mod scalar; + +pub use fp::*; +pub use fp12::*; +pub use fp2::*; +pub use fp6::*; +pub use g1::*; +pub use g2::*; +pub use pairings::*; +pub use scalar::*; diff --git a/src/bls12_381/pairings.rs b/src/bls12_381/pairings.rs new file mode 100644 index 00000000..b0bfd800 --- /dev/null +++ b/src/bls12_381/pairings.rs @@ -0,0 +1,942 @@ +use crate::bls12_381::fp::Fp; +use crate::bls12_381::fp12::Fp12; +use crate::bls12_381::fp2::Fp2; +use crate::bls12_381::fp6::Fp6; +use crate::bls12_381::{G1Affine, G1Projective, G2Affine, G2Projective, Scalar}; + +use crate::pairing::{Engine, PairingCurveAffine}; +use core::borrow::Borrow; +use core::fmt; +use core::iter::Sum; +use core::ops::{Add, AddAssign, Mul, Neg, Sub}; +use group::cofactor::CofactorCurveAffine; +use group::Group; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +use crate::pairing::MultiMillerLoop; +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, +}; + +pub(crate) const BLS_X: u64 = 0xd201_0000_0001_0000; +pub(crate) const BLS_X_IS_NEGATIVE: bool = true; + +/// 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. +#[derive(Copy, Clone, Debug)] +pub struct MillerLoopResult(pub(crate) Fp12); + +impl Default for MillerLoopResult { + fn default() -> Self { + MillerLoopResult(Fp12::one()) + } +} + +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] + #[allow(clippy::suspicious_arithmetic_impl)] + 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. +#[derive(Copy, Clone, Debug)] +pub struct Gt(pub(crate) Fp12); + +impl Default for Gt { + fn default() -> Self { + Self::identity() + } +} + +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] + #[allow(clippy::suspicious_arithmetic_impl)] + 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; + + #[allow(clippy::suspicious_arithmetic_impl)] + 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, + } + } +} + +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: Fp12 = miller_loop(&mut adder); + + MillerLoopResult(tmp) +} + +/// Invoke the pairing function without the use of precomputation and other optimizations. +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. +#[derive(Clone, Debug)] +pub struct Bls12; + +impl Engine for Bls12 { + type Scalar = 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 crate::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 crate::bls12_381::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(), + ); +} + +#[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..247f95ea --- /dev/null +++ b/src/bls12_381/scalar.rs @@ -0,0 +1,1278 @@ +//! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ +//! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` + +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub}; +use rand_core::RngCore; +use std::convert::TryFrom; + +use ff::{Field, FieldBits, FromUniformBytes, PrimeField, PrimeFieldBits, WithSmallOrderMulGroup}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use std::convert::TryInto; + +use crate::util::{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, Eq)] +pub struct Scalar(pub(crate) [u64; 4]); + +impl Ord for Scalar { + fn cmp(&self, other: &Self) -> core::cmp::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) { + core::cmp::Ordering::Equal => None, + res => Some(res), + }) + .unwrap_or(core::cmp::Ordering::Equal) + } +} + +impl PartialOrd for Scalar { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +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, "{:02x}", b)?; + } + 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 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 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, +]); + +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)] + #[allow(clippy::too_many_arguments)] + 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 FromUniformBytes<64> for Scalar { + /// Converts a 512-bit little endian integer into + /// a `Scalar` by reducing by the modulus. + fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { + Self::from_bytes_wide(bytes) + } +} + +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() + } +} + +// GF(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001).primitive_element() ^ ((0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 - 1) // 3) +const ZETA: Scalar = Scalar::from_raw([0x00000000ffffffff, 0xac45a4010001a402, 0x00, 0x00]); + +impl WithSmallOrderMulGroup<3> for Scalar { + const ZETA: Self = ZETA; +} + +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 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 PrimeFieldBits for Scalar { + type ReprBits = [u64; 4]; + + fn to_le_bits(&self) -> FieldBits { + let bytes = self.to_repr(); + + 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 { + FieldBits::new(MODULUS.0) + } +} + +#[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/jubjub/curve.rs b/src/jubjub/curve.rs new file mode 100644 index 00000000..078d5164 --- /dev/null +++ b/src/jubjub/curve.rs @@ -0,0 +1,1984 @@ +//! This crate provides an implementation of the **Jubjub** elliptic curve and its associated +//! field arithmetic. See [`README.md`](https://github.com/zkcrypto/jubjub/blob/master/README.md) for more details about Jubjub. +//! +//! # API +//! +//! * `AffinePoint` / `ExtendedPoint` which are implementations of Jubjub group arithmetic +//! * `AffineNielsPoint` / `ExtendedNielsPoint` which are pre-processed Jubjub points +//! * `Fq`, which is the base field of Jubjub +//! * `Fr`, which is the scalar field of Jubjub +//! * `batch_normalize` for converting many `ExtendedPoint`s into `AffinePoint`s efficiently. +//! +//! # Constant Time +//! +//! All operations are constant time unless explicitly noted; these functions will contain +//! "vartime" in their name and they will be documented as variable time. +//! +//! This crate uses the `subtle` crate to perform constant-time operations. + +// Catch documentation errors caused by code changes. +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +#![deny(unsafe_code)] +// 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 will necessarily involve various binary +// operators, and so this lint is triggered unnecessarily. +#![allow(clippy::suspicious_arithmetic_impl)] + +use bitvec::{order::Lsb0, view::AsBits}; +use core::borrow::Borrow; +use core::fmt; +use core::iter::Sum; +use core::ops::{Add, Mul, Neg, Sub}; +use ff::{BatchInverter, Field}; +use group::{ + prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, + Curve, Group, GroupEncoding, +}; +use pasta_curves::arithmetic::{Coordinates, CurveAffine, CurveExt}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +pub use crate::bls12_381::Scalar as Fq; + +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, +}; + +pub use super::fr::Fr; + +/// Represents an element of the base field $\mathbb{F}_q$ of the Jubjub elliptic curve +/// construction. +pub type Base = Fq; + +/// Represents an element of the scalar field $\mathbb{F}_r$ of the Jubjub elliptic curve +/// construction. +pub type Scalar = Fr; + +const FR_MODULUS_BYTES: [u8; 32] = [ + 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, + 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, +]; + +/// This represents a Jubjub point in the affine `(u, v)` +/// coordinates. +#[derive(Clone, Copy, Debug, Eq)] +pub struct AffinePoint { + u: Fq, + v: Fq, +} + +impl fmt::Display for AffinePoint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl Neg for AffinePoint { + type Output = AffinePoint; + + /// This computes the negation of a point `P = (u, v)` + /// as `-P = (-u, v)`. + #[inline] + fn neg(self) -> AffinePoint { + AffinePoint { + u: -self.u, + v: self.v, + } + } +} + +impl ConstantTimeEq for AffinePoint { + fn ct_eq(&self, other: &Self) -> Choice { + self.u.ct_eq(&other.u) & self.v.ct_eq(&other.v) + } +} + +impl PartialEq for AffinePoint { + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl ConditionallySelectable for AffinePoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + AffinePoint { + u: Fq::conditional_select(&a.u, &b.u, choice), + v: Fq::conditional_select(&a.v, &b.v, choice), + } + } +} + +/// This represents an extended point `(U, V, Z, T1, T2)` +/// with `Z` nonzero, corresponding to the affine point +/// `(U/Z, V/Z)`. We always have `T1 * T2 = UV/Z`. +/// +/// You can do the following things with a point in this +/// form: +/// +/// * Convert it into a point in the affine form. +/// * Add it to an `ExtendedPoint`, `AffineNielsPoint` or `ExtendedNielsPoint`. +/// * Double it using `double()`. +/// * Compare it with another extended point using `PartialEq` or `ct_eq()`. +#[derive(Clone, Copy, Debug, Eq)] +pub struct ExtendedPoint { + u: Fq, + v: Fq, + z: Fq, + t1: Fq, + t2: Fq, +} + +impl fmt::Display for ExtendedPoint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl ConstantTimeEq for ExtendedPoint { + fn ct_eq(&self, other: &Self) -> Choice { + // (u/z, v/z) = (u'/z', v'/z') is implied by + // (uz'z = u'z'z) and + // (vz'z = v'z'z) + // as z and z' are always nonzero. + + (self.u * other.z).ct_eq(&(other.u * self.z)) + & (self.v * other.z).ct_eq(&(other.v * self.z)) + } +} + +impl ConditionallySelectable for ExtendedPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ExtendedPoint { + u: Fq::conditional_select(&a.u, &b.u, choice), + v: Fq::conditional_select(&a.v, &b.v, choice), + z: Fq::conditional_select(&a.z, &b.z, choice), + t1: Fq::conditional_select(&a.t1, &b.t1, choice), + t2: Fq::conditional_select(&a.t2, &b.t2, choice), + } + } +} + +impl PartialEq for ExtendedPoint { + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl Sum for ExtendedPoint +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + +impl Neg for ExtendedPoint { + type Output = ExtendedPoint; + + /// Computes the negation of a point `P = (U, V, Z, T)` + /// as `-P = (-U, V, Z, -T1, T2)`. The choice of `T1` + /// is made without loss of generality. + #[inline] + fn neg(self) -> ExtendedPoint { + ExtendedPoint { + u: -self.u, + v: self.v, + z: self.z, + t1: -self.t1, + t2: self.t2, + } + } +} + +impl From for ExtendedPoint { + /// Constructs an extended point (with `Z = 1`) from + /// an affine point using the map `(u, v) => (u, v, 1, u, v)`. + fn from(affine: AffinePoint) -> ExtendedPoint { + ExtendedPoint { + u: affine.u, + v: affine.v, + z: Fq::one(), + t1: affine.u, + t2: affine.v, + } + } +} + +impl<'a> From<&'a ExtendedPoint> for AffinePoint { + /// Constructs an affine point from an extended point + /// using the map `(U, V, Z, T1, T2) => (U/Z, V/Z)` + /// as Z is always nonzero. **This requires a field inversion + /// and so it is recommended to perform these in a batch + /// using [`batch_normalize`](crate::batch_normalize) instead.** + fn from(extended: &'a ExtendedPoint) -> AffinePoint { + // Z coordinate is always nonzero, so this is + // its inverse. + let zinv = extended.z.invert().unwrap(); + + AffinePoint { + u: extended.u * zinv, + v: extended.v * zinv, + } + } +} + +impl From for AffinePoint { + fn from(extended: ExtendedPoint) -> AffinePoint { + AffinePoint::from(&extended) + } +} + +/// This is a pre-processed version of an affine point `(u, v)` +/// in the form `(v + u, v - u, u * v * 2d)`. This can be added to an +/// [`ExtendedPoint`](crate::ExtendedPoint). +#[derive(Clone, Copy, Debug)] +pub struct AffineNielsPoint { + v_plus_u: Fq, + v_minus_u: Fq, + t2d: Fq, +} + +impl AffineNielsPoint { + /// Constructs this point from the neutral element `(0, 1)`. + pub const fn identity() -> Self { + AffineNielsPoint { + v_plus_u: Fq::one(), + v_minus_u: Fq::one(), + t2d: Fq::zero(), + } + } + + #[inline] + fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { + let zero = AffineNielsPoint::identity(); + + let mut acc = ExtendedPoint::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 four bits because they're always + // unset for Fr. + for bit in by + .as_bits::() + .iter() + .rev() + .skip(4) + .map(|bit| Choice::from(if *bit { 1 } else { 0 })) + { + acc = acc.double(); + acc += AffineNielsPoint::conditional_select(&zero, self, bit); + } + + acc + } + + /// Multiplies this point by the specific little-endian bit pattern in the + /// given byte array, ignoring the highest four bits. + pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { + self.multiply(by) + } +} + +impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint { + type Output = ExtendedPoint; + + fn mul(self, other: &'b Fr) -> ExtendedPoint { + self.multiply(&other.to_bytes()) + } +} + +impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, ExtendedPoint); + +impl ConditionallySelectable for AffineNielsPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + AffineNielsPoint { + v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), + v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), + t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), + } + } +} + +/// This is a pre-processed version of an extended point `(U, V, Z, T1, T2)` +/// in the form `(V + U, V - U, Z, T1 * T2 * 2d)`. +#[derive(Clone, Copy, Debug)] +pub struct ExtendedNielsPoint { + v_plus_u: Fq, + v_minus_u: Fq, + z: Fq, + t2d: Fq, +} + +impl ConditionallySelectable for ExtendedNielsPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ExtendedNielsPoint { + v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), + v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), + z: Fq::conditional_select(&a.z, &b.z, choice), + t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), + } + } +} + +impl ExtendedNielsPoint { + /// Constructs this point from the neutral element `(0, 1)`. + pub const fn identity() -> Self { + ExtendedNielsPoint { + v_plus_u: Fq::one(), + v_minus_u: Fq::one(), + z: Fq::one(), + t2d: Fq::zero(), + } + } + + #[inline] + fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { + let zero = ExtendedNielsPoint::identity(); + + let mut acc = ExtendedPoint::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 four bits because they're always + // unset for Fr. + for bit in by + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(4) + { + acc = acc.double(); + acc += ExtendedNielsPoint::conditional_select(&zero, self, bit); + } + + acc + } + + /// Multiplies this point by the specific little-endian bit pattern in the + /// given byte array, ignoring the highest four bits. + pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { + self.multiply(by) + } +} + +impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint { + type Output = ExtendedPoint; + + fn mul(self, other: &'b Fr) -> ExtendedPoint { + self.multiply(&other.to_bytes()) + } +} + +impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint); + +// `d = -(10240/10241)` +const EDWARDS_D: Fq = Fq::from_raw([ + 0x0106_5fd6_d634_3eb1, + 0x292d_7f6d_3757_9d26, + 0xf5fd_9207_e6bd_7fd4, + 0x2a93_18e7_4bfa_2b48, +]); + +// `2*d` +const EDWARDS_D2: Fq = Fq::from_raw([ + 0x020c_bfad_ac68_7d62, + 0x525a_feda_6eaf_3a4c, + 0xebfb_240f_cd7a_ffa8, + 0x5526_31ce_97f4_5691, +]); + +impl AffinePoint { + /// Constructs the neutral element `(0, 1)`. + pub const fn identity() -> Self { + AffinePoint { + u: Fq::zero(), + v: Fq::one(), + } + } + + /// Determines if this point is the identity. + pub fn is_identity(&self) -> Choice { + ExtendedPoint::from(*self).is_identity() + } + + /// Multiplies this point by the cofactor, producing an + /// `ExtendedPoint` + pub fn mul_by_cofactor(&self) -> ExtendedPoint { + ExtendedPoint::from(*self).mul_by_cofactor() + } + + /// Determines if this point is of small order. + pub fn is_small_order(&self) -> Choice { + ExtendedPoint::from(*self).is_small_order() + } + + /// Determines if this point is torsion free and so is + /// in the prime order subgroup. + pub fn is_torsion_free(&self) -> Choice { + ExtendedPoint::from(*self).is_torsion_free() + } + + /// Determines if this point is prime order, or in other words that + /// the smallest scalar multiplied by this point that produces the + /// identity is `r`. This is equivalent to checking that the point + /// is both torsion free and not the identity. + pub fn is_prime_order(&self) -> Choice { + let extended = ExtendedPoint::from(*self); + extended.is_torsion_free() & (!extended.is_identity()) + } + + /// Converts this element into its byte representation. + pub fn to_bytes(&self) -> [u8; 32] { + let mut tmp = self.v.to_bytes(); + let u = self.u.to_bytes(); + + // Encode the sign of the u-coordinate in the most + // significant bit. + tmp[31] |= u[0] << 7; + + tmp + } + + /// Attempts to interpret a byte representation of an + /// affine point, failing if the element is not on + /// the curve or non-canonical. + pub fn from_bytes(b: [u8; 32]) -> CtOption { + Self::from_bytes_inner(b, 1.into()) + } + + /// Attempts to interpret a byte representation of an affine point, failing if the + /// element is not on the curve. + /// + /// Most non-canonical encodings will also cause a failure. However, this API + /// preserves (for use in consensus-critical protocols) a bug in the parsing code that + /// caused two non-canonical encodings to be **silently** accepted: + /// + /// - (0, 1), which is the identity; + /// - (0, -1), which is a point of order two. + /// + /// Each of these has a single non-canonical encoding in which the value of the sign + /// bit is 1. + /// + /// See [ZIP 216](https://zips.z.cash/zip-0216) for a more detailed description of the + /// bug, as well as its fix. + pub fn from_bytes_pre_zip216_compatibility(b: [u8; 32]) -> CtOption { + Self::from_bytes_inner(b, 0.into()) + } + + fn from_bytes_inner(mut b: [u8; 32], zip_216_enabled: Choice) -> CtOption { + // Grab the sign bit from the representation + let sign = b[31] >> 7; + + // Mask away the sign bit + b[31] &= 0b0111_1111; + + // Interpret what remains as the v-coordinate + Fq::from_bytes(&b).and_then(|v| { + // -u^2 + v^2 = 1 + d.u^2.v^2 + // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) + // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) + // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) + // u^2 (1 + d.v^2) = v^2 - 1 (factor) + // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) + // We know that (1 + d.v^2) is nonzero for all v: + // (1 + d.v^2) = 0 + // d.v^2 = -1 + // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square + + let v2 = v.square(); + + ((v2 - Fq::one()) * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) + .sqrt() + .and_then(|u| { + // Fix the sign of `u` if necessary + let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); + let u_negated = -u; + let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); + + // If u == 0, flip_sign == sign_bit. We therefore want to reject the + // encoding as non-canonical if all of the following occur: + // - ZIP 216 is enabled + // - u == 0 + // - flip_sign == true + let u_is_zero = u.ct_eq(&Fq::zero()); + CtOption::new( + AffinePoint { u: final_u, v }, + !(zip_216_enabled & u_is_zero & flip_sign), + ) + }) + }) + } + + /// Attempts to interpret a batch of byte representations of affine points. + /// + /// Returns None for each element if it is not on the curve, or is non-canonical + /// according to ZIP 216. + pub fn batch_from_bytes(items: impl Iterator) -> Vec> { + use ff::BatchInvert; + + #[derive(Clone, Copy, Default)] + struct Item { + sign: u8, + v: Fq, + numerator: Fq, + denominator: Fq, + } + + impl ConditionallySelectable for Item { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Item { + sign: u8::conditional_select(&a.sign, &b.sign, choice), + v: Fq::conditional_select(&a.v, &b.v, choice), + numerator: Fq::conditional_select(&a.numerator, &b.numerator, choice), + denominator: Fq::conditional_select(&a.denominator, &b.denominator, choice), + } + } + } + + let items: Vec<_> = items + .map(|mut b| { + // Grab the sign bit from the representation + let sign = b[31] >> 7; + + // Mask away the sign bit + b[31] &= 0b0111_1111; + + // Interpret what remains as the v-coordinate + Fq::from_bytes(&b).map(|v| { + // -u^2 + v^2 = 1 + d.u^2.v^2 + // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) + // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) + // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) + // u^2 (1 + d.v^2) = v^2 - 1 (factor) + // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) + // We know that (1 + d.v^2) is nonzero for all v: + // (1 + d.v^2) = 0 + // d.v^2 = -1 + // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square + + let v2 = v.square(); + + Item { + v, + sign, + numerator: (v2 - Fq::one()), + denominator: Fq::one() + EDWARDS_D * v2, + } + }) + }) + .collect(); + + let mut denominators: Vec<_> = items + .iter() + .map(|item| item.map(|item| item.denominator).unwrap_or(Fq::zero())) + .collect(); + denominators.iter_mut().batch_invert(); + + items + .into_iter() + .zip(denominators.into_iter()) + .map(|(item, inv_denominator)| { + item.and_then( + |Item { + v, sign, numerator, .. + }| { + (numerator * inv_denominator).sqrt().and_then(|u| { + // Fix the sign of `u` if necessary + let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); + let u_negated = -u; + let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); + + // If u == 0, flip_sign == sign_bit. We therefore want to reject the + // encoding as non-canonical if all of the following occur: + // - u == 0 + // - flip_sign == true + let u_is_zero = u.ct_eq(&Fq::zero()); + CtOption::new(AffinePoint { u: final_u, v }, !(u_is_zero & flip_sign)) + }) + }, + ) + }) + .collect() + } + + /// Returns the `u`-coordinate of this point. + pub fn get_u(&self) -> Fq { + self.u + } + + /// Returns the `v`-coordinate of this point. + pub fn get_v(&self) -> Fq { + self.v + } + + /// Returns an `ExtendedPoint` for use in arithmetic operations. + pub const fn to_extended(&self) -> ExtendedPoint { + ExtendedPoint { + u: self.u, + v: self.v, + z: Fq::one(), + t1: self.u, + t2: self.v, + } + } + + /// Performs a pre-processing step that produces an `AffineNielsPoint` + /// for use in multiple additions. + pub const fn to_niels(&self) -> AffineNielsPoint { + AffineNielsPoint { + v_plus_u: Fq::add(&self.v, &self.u), + v_minus_u: Fq::sub(&self.v, &self.u), + t2d: Fq::mul(&Fq::mul(&self.u, &self.v), &EDWARDS_D2), + } + } + + /// Constructs an AffinePoint given `u` and `v` without checking + /// that the point is on the curve. + pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint { + AffinePoint { u, v } + } + + /// This is only for debugging purposes and not + /// exposed in the public API. Checks that this + /// point is on the curve. + #[cfg(test)] + fn is_on_curve_vartime(&self) -> bool { + let u2 = self.u.square(); + let v2 = self.v.square(); + + v2 - u2 == Fq::one() + EDWARDS_D * u2 * v2 + } +} + +impl ExtendedPoint { + /// Constructs an extended point from the neutral element `(0, 1)`. + pub const fn identity() -> Self { + ExtendedPoint { + u: Fq::zero(), + v: Fq::one(), + z: Fq::one(), + t1: Fq::zero(), + t2: Fq::zero(), + } + } + + /// Determines if this point is the identity. + pub fn is_identity(&self) -> Choice { + // If this point is the identity, then + // u = 0 * z = 0 + // and v = 1 * z = z + self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z) + } + + /// Determines if this point is of small order. + pub fn is_small_order(&self) -> Choice { + // We only need to perform two doublings, since the 2-torsion + // points are (0, 1) and (0, -1), and so we only need to check + // that the u-coordinate of the result is zero to see if the + // point is small order. + self.double().double().u.ct_eq(&Fq::zero()) + } + + /// Determines if this point is torsion free and so is contained + /// in the prime order subgroup. + pub fn is_torsion_free(&self) -> Choice { + self.multiply(&FR_MODULUS_BYTES).is_identity() + } + + /// Determines if this point is prime order, or in other words that + /// the smallest scalar multiplied by this point that produces the + /// identity is `r`. This is equivalent to checking that the point + /// is both torsion free and not the identity. + pub fn is_prime_order(&self) -> Choice { + self.is_torsion_free() & (!self.is_identity()) + } + + /// Multiplies this element by the cofactor `8`. + pub fn mul_by_cofactor(&self) -> ExtendedPoint { + self.double().double().double() + } + + /// Performs a pre-processing step that produces an `ExtendedNielsPoint` + /// for use in multiple additions. + pub fn to_niels(&self) -> ExtendedNielsPoint { + ExtendedNielsPoint { + v_plus_u: self.v + self.u, + v_minus_u: self.v - self.u, + z: self.z, + t2d: self.t1 * self.t2 * EDWARDS_D2, + } + } + + /// Computes the doubling of a point more efficiently than a point can + /// be added to itself. + pub fn double(&self) -> ExtendedPoint { + // Doubling is more efficient (three multiplications, four squarings) + // when we work within the projective coordinate space (U:Z, V:Z). We + // rely on the most efficient formula, "dbl-2008-bbjlp", as described + // in Section 6 of "Twisted Edwards Curves" by Bernstein et al. + // + // See + // for more information. + // + // We differ from the literature in that we use (u, v) rather than + // (x, y) coordinates. We also have the constant `a = -1` implied. Let + // us rewrite the procedure of doubling (u, v, z) to produce (U, V, Z) + // as follows: + // + // B = (u + v)^2 + // C = u^2 + // D = v^2 + // F = D - C + // H = 2 * z^2 + // J = F - H + // U = (B - C - D) * J + // V = F * (- C - D) + // Z = F * J + // + // If we compute K = D + C, we can rewrite this: + // + // B = (u + v)^2 + // C = u^2 + // D = v^2 + // F = D - C + // K = D + C + // H = 2 * z^2 + // J = F - H + // U = (B - K) * J + // V = F * (-K) + // Z = F * J + // + // In order to avoid the unnecessary negation of K, + // we will negate J, transforming the result into + // an equivalent point with a negated z-coordinate. + // + // B = (u + v)^2 + // C = u^2 + // D = v^2 + // F = D - C + // K = D + C + // H = 2 * z^2 + // J = H - F + // U = (B - K) * J + // V = F * K + // Z = F * J + // + // Let us rename some variables to simplify: + // + // UV2 = (u + v)^2 + // UU = u^2 + // VV = v^2 + // VVmUU = VV - UU + // VVpUU = VV + UU + // ZZ2 = 2 * z^2 + // J = ZZ2 - VVmUU + // U = (UV2 - VVpUU) * J + // V = VVmUU * VVpUU + // Z = VVmUU * J + // + // We wish to obtain two factors of T = UV/Z. + // + // UV/Z = (UV2 - VVpUU) * (ZZ2 - VVmUU) * VVmUU * VVpUU / VVmUU / (ZZ2 - VVmUU) + // = (UV2 - VVpUU) * VVmUU * VVpUU / VVmUU + // = (UV2 - VVpUU) * VVpUU + // + // and so we have that T1 = (UV2 - VVpUU) and T2 = VVpUU. + + let uu = self.u.square(); + let vv = self.v.square(); + let zz2 = self.z.square().double(); + let uv2 = (self.u + self.v).square(); + let vv_plus_uu = vv + uu; + let vv_minus_uu = vv - uu; + + // The remaining arithmetic is exactly the process of converting + // from a completed point to an extended point. + CompletedPoint { + u: uv2 - vv_plus_uu, + v: vv_plus_uu, + z: vv_minus_uu, + t: zz2 - vv_minus_uu, + } + .into_extended() + } + + #[inline] + fn multiply(self, by: &[u8; 32]) -> Self { + self.to_niels().multiply(by) + } + + /// Converts a batch of projective elements into affine elements. + /// + /// This function will panic if `p.len() != q.len()`. + /// + /// This costs 5 multiplications per element, and a field inversion. + fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) { + assert_eq!(p.len(), q.len()); + + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `u` field of `AffinePoint` to store the z-coordinate being + // inverted, and the `v` field for scratch space. + q.u = p.z; + } + + BatchInverter::invert_with_internal_scratch(q, |q| &mut q.u, |q| &mut q.v); + + for (p, q) in p.iter().zip(q.iter_mut()).rev() { + let tmp = q.u; + + // Set the coordinates to the correct value + q.u = p.u * tmp; // Multiply by 1/z + q.v = p.v * tmp; // Multiply by 1/z + } + } + + /// This is only for debugging purposes and not + /// exposed in the public API. Checks that this + /// point is on the curve. + #[cfg(test)] + fn is_on_curve_vartime(&self) -> bool { + let affine = AffinePoint::from(*self); + + self.z != Fq::zero() + && affine.is_on_curve_vartime() + && (affine.u * affine.v * self.z == self.t1 * self.t2) + } + + /// Return a Subgroup element, if the point is torsion free + fn into_subgroup(self) -> CtOption { + CtOption::new(SubgroupPoint(self), self.is_torsion_free()) + } + + /// Clear cofactor and return Subgroup point + fn clear_cofactor(&self) -> SubgroupPoint { + SubgroupPoint(self.mul_by_cofactor()) + } +} + +impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + fn mul(self, other: &'b Fr) -> ExtendedPoint { + self.multiply(&other.to_bytes()) + } +} + +impl_binops_multiplicative!(ExtendedPoint, Fr); + +impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { + // We perform addition in the extended coordinates. Here we use + // a formula presented by Hisil, Wong, Carter and Dawson in + // "Twisted Edward Curves Revisited" which only requires 8M. + // + // A = (V1 - U1) * (V2 - U2) + // B = (V1 + U1) * (V2 + U2) + // C = 2d * T1 * T2 + // D = 2 * Z1 * Z2 + // E = B - A + // F = D - C + // G = D + C + // H = B + A + // U3 = E * F + // Y3 = G * H + // Z3 = F * G + // T3 = E * H + + let a = (self.v - self.u) * other.v_minus_u; + let b = (self.v + self.u) * other.v_plus_u; + let c = self.t1 * self.t2 * other.t2d; + let d = (self.z * other.z).double(); + + // The remaining arithmetic is exactly the process of converting + // from a completed point to an extended point. + CompletedPoint { + u: b - a, + v: b + a, + z: d + c, + t: d - c, + } + .into_extended() + } +} + +impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { + let a = (self.v - self.u) * other.v_plus_u; + let b = (self.v + self.u) * other.v_minus_u; + let c = self.t1 * self.t2 * other.t2d; + let d = (self.z * other.z).double(); + + CompletedPoint { + u: b - a, + v: b + a, + z: d - c, + t: d + c, + } + .into_extended() + } +} + +impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint); + +impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint { + // This is identical to the addition formula for `ExtendedNielsPoint`, + // except we can assume that `other.z` is one, so that we perform + // 7 multiplications. + + let a = (self.v - self.u) * other.v_minus_u; + let b = (self.v + self.u) * other.v_plus_u; + let c = self.t1 * self.t2 * other.t2d; + let d = self.z.double(); + + // The remaining arithmetic is exactly the process of converting + // from a completed point to an extended point. + CompletedPoint { + u: b - a, + v: b + a, + z: d + c, + t: d - c, + } + .into_extended() + } +} + +impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint { + let a = (self.v - self.u) * other.v_plus_u; + let b = (self.v + self.u) * other.v_minus_u; + let c = self.t1 * self.t2 * other.t2d; + let d = self.z.double(); + + CompletedPoint { + u: b - a, + v: b + a, + z: d - c, + t: d + c, + } + .into_extended() + } +} + +impl_binops_additive!(ExtendedPoint, AffineNielsPoint); + +impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint { + self + other.to_niels() + } +} + +impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint { + self - other.to_niels() + } +} + +impl_binops_additive!(ExtendedPoint, ExtendedPoint); + +impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn add(self, other: &'b AffinePoint) -> ExtendedPoint { + self + other.to_niels() + } +} + +impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn sub(self, other: &'b AffinePoint) -> ExtendedPoint { + self - other.to_niels() + } +} + +impl_binops_additive!(ExtendedPoint, AffinePoint); + +impl<'a, 'b> Add<&'b AffinePoint> for &'a AffinePoint { + type Output = ExtendedPoint; + + #[inline] + fn add(self, other: &'b AffinePoint) -> ExtendedPoint { + ExtendedPoint::from(*other) + self + } +} + +impl<'a, 'b> Sub<&'b AffinePoint> for &'a AffinePoint { + type Output = ExtendedPoint; + + #[inline] + fn sub(self, other: &'b AffinePoint) -> ExtendedPoint { + -ExtendedPoint::from(*other) + self + } +} + +impl_binops_additive_specify_output!(AffinePoint, AffinePoint, ExtendedPoint); + +/// This is a "completed" point produced during a point doubling or +/// addition routine. These points exist in the `(U:Z, V:T)` model +/// of the curve. This is not exposed in the API because it is +/// an implementation detail. +struct CompletedPoint { + u: Fq, + v: Fq, + z: Fq, + t: Fq, +} + +impl CompletedPoint { + /// This converts a completed point into an extended point by + /// homogenizing: + /// + /// (u/z, v/t) = (u/z * t/t, v/t * z/z) = (ut/zt, vz/zt) + /// + /// The resulting T coordinate is utvz/zt = uv, and so + /// T1 = u, T2 = v, without loss of generality. + #[inline] + fn into_extended(self) -> ExtendedPoint { + ExtendedPoint { + u: self.u * self.t, + v: self.v * self.z, + z: self.z * self.t, + t1: self.u, + t2: self.v, + } + } +} + +impl Default for AffinePoint { + /// Returns the identity. + fn default() -> AffinePoint { + AffinePoint::identity() + } +} + +impl Default for ExtendedPoint { + /// Returns the identity. + fn default() -> ExtendedPoint { + ExtendedPoint::identity() + } +} + +/// This takes a mutable slice of `ExtendedPoint`s and "normalizes" them using +/// only a single inversion for the entire batch. This normalization results in +/// all of the points having a Z-coordinate of one. Further, an iterator is +/// returned which can be used to obtain `AffinePoint`s for each element in the +/// slice. +/// +/// This costs 5 multiplications per element, and a field inversion. +pub fn batch_normalize(v: &mut [ExtendedPoint]) -> impl Iterator + '_ { + // We use the `t1` field of `ExtendedPoint` for scratch space. + BatchInverter::invert_with_internal_scratch(v, |p| &mut p.z, |p| &mut p.t1); + + for p in v.iter_mut() { + let mut q = *p; + let tmp = q.z; + + // Set the coordinates to the correct value + q.u *= &tmp; // Multiply by 1/z + q.v *= &tmp; // Multiply by 1/z + q.z = Fq::one(); // z-coordinate is now one + q.t1 = q.u; + q.t2 = q.v; + + *p = q; + } + + // All extended points are now normalized, but the type + // doesn't encode this fact. Let us offer affine points + // to the caller. + + v.iter().map(|p| AffinePoint { u: p.u, v: p.v }) +} + +impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint { + type Output = ExtendedPoint; + + fn mul(self, other: &'b Fr) -> ExtendedPoint { + self.to_niels().multiply(&other.to_bytes()) + } +} + +impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint); + +/// This represents a point in the prime-order subgroup of Jubjub, in extended +/// coordinates. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct SubgroupPoint(ExtendedPoint); + +impl From for ExtendedPoint { + fn from(val: SubgroupPoint) -> ExtendedPoint { + val.0 + } +} + +impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint { + fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint { + &val.0 + } +} + +impl fmt::Display for SubgroupPoint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl ConditionallySelectable for SubgroupPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice)) + } +} + +impl SubgroupPoint { + /// Constructs an AffinePoint given `u` and `v` without checking that the point is on + /// the curve or in the prime-order subgroup. + /// + /// This should only be used for hard-coding constants (e.g. fixed generators); in all + /// other cases, use [`SubgroupPoint::from_bytes`] instead. + /// + /// [`SubgroupPoint::from_bytes`]: SubgroupPoint#impl-GroupEncoding + pub const fn from_raw_unchecked(u: Fq, v: Fq) -> Self { + SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended()) + } +} + +impl Sum for SubgroupPoint +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + +impl Neg for SubgroupPoint { + type Output = SubgroupPoint; + + #[inline] + fn neg(self) -> SubgroupPoint { + SubgroupPoint(-self.0) + } +} + +impl Neg for &SubgroupPoint { + type Output = SubgroupPoint; + + #[inline] + fn neg(self) -> SubgroupPoint { + SubgroupPoint(-self.0) + } +} + +impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint { + self + other.0 + } +} + +impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint { + self - other.0 + } +} + +impl_binops_additive!(ExtendedPoint, SubgroupPoint); + +impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint { + type Output = SubgroupPoint; + + #[inline] + fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint { + SubgroupPoint(self.0 + other.0) + } +} + +impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint { + type Output = SubgroupPoint; + + #[inline] + fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint { + SubgroupPoint(self.0 - other.0) + } +} + +impl_binops_additive!(SubgroupPoint, SubgroupPoint); + +impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint { + type Output = SubgroupPoint; + + fn mul(self, other: &'b Fr) -> SubgroupPoint { + SubgroupPoint(self.0.multiply(&other.to_bytes())) + } +} + +impl_binops_multiplicative!(SubgroupPoint, Fr); + +impl Group for ExtendedPoint { + type Scalar = Fr; + + fn random(mut rng: impl RngCore) -> Self { + loop { + let v = Fq::random(&mut rng); + let flip_sign = rng.next_u32() % 2 != 0; + + // See AffinePoint::from_bytes for details. + let v2 = v.square(); + let p = ((v2 - Fq::one()) + * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) + .sqrt() + .map(|u| AffinePoint { + u: if flip_sign { -u } else { u }, + v, + }); + + if p.is_some().into() { + let p = p.unwrap().to_curve(); + + if bool::from(!p.is_identity()) { + return p; + } + } + } + } + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + AffinePoint::generator().into() + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } +} + +impl Group for SubgroupPoint { + type Scalar = Fr; + + fn random(mut rng: impl RngCore) -> Self { + loop { + let p = ExtendedPoint::random(&mut rng).clear_cofactor(); + + if bool::from(!p.is_identity()) { + return p; + } + } + } + + fn identity() -> Self { + SubgroupPoint(ExtendedPoint::identity()) + } + + fn generator() -> Self { + ExtendedPoint::generator().clear_cofactor() + } + + fn is_identity(&self) -> Choice { + self.0.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + SubgroupPoint(self.0.double()) + } +} + +impl PrimeGroup for SubgroupPoint {} + +impl PrimeCurveAffine for AffinePoint { + type Scalar = Scalar; + type Curve = ExtendedPoint; + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + // The point with the lowest positive v-coordinate and positive u-coordinate. + AffinePoint { + u: Fq::from_raw([ + 0xe4b3_d35d_f1a7_adfe, + 0xcaf5_5d1b_29bf_81af, + 0x8b0f_03dd_d60a_8187, + 0x62ed_cbb8_bf37_87c8, + ]), + v: Fq::from_raw([ + 0x0000_0000_0000_000b, + 0x0000_0000_0000_0000, + 0x0000_0000_0000_0000, + 0x0000_0000_0000_0000, + ]), + } + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + fn to_curve(&self) -> Self::Curve { + (*self).into() + } +} + +impl CurveAffine for AffinePoint { + type ScalarExt = Scalar; + type Base = Fq; + type CurveExt = ExtendedPoint; + + fn is_on_curve(&self) -> Choice { + // v^2 - u^2 ?= 1 + d * u^2 * v^2 + let u2 = self.u.square(); + let v2 = self.v.square(); + (v2 - u2).ct_eq(&(Fq::one() + EDWARDS_D * u2 * v2)) | self.is_identity() + } + + fn coordinates(&self) -> CtOption> { + Coordinates::from_xy(self.u, self.v) + } + + fn from_xy(u: Self::Base, v: Self::Base) -> CtOption { + let p = AffinePoint { u, v }; + CtOption::new(p, p.is_on_curve()) + } + + fn a() -> Self::Base { + unimplemented!() + } + + fn b() -> Self::Base { + unimplemented!() + } +} + +impl Curve for ExtendedPoint { + type AffineRepr = AffinePoint; + + fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { + Self::batch_normalize(p, q); + } + + fn to_affine(&self) -> Self::AffineRepr { + self.into() + } +} + +impl PrimeGroup for ExtendedPoint {} + +impl PrimeCurve for ExtendedPoint { + type Affine = AffinePoint; +} + +impl CurveExt for ExtendedPoint { + type ScalarExt = Scalar; + type Base = Fq; + type AffineExt = AffinePoint; + + const CURVE_ID: &'static str = "jubjub"; + + fn endo(&self) -> Self { + unimplemented!(); + } + + fn jacobian_coordinates(&self) -> (Fq, Fq, Fq) { + unimplemented!(); + } + + fn hash_to_curve<'a>(_: &'a str) -> Box Self + 'a> { + unimplemented!(); + } + + fn is_on_curve(&self) -> Choice { + unimplemented!(); + } + + fn b() -> Self::Base { + unimplemented!(); + } + + fn a() -> Self::Base { + unimplemented!(); + } + + fn new_jacobian(_x: Self::Base, _y: Self::Base, _z: Self::Base) -> CtOption { + unimplemented!(); + } +} + +impl GroupEncoding for ExtendedPoint { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + AffinePoint::from_bytes(*bytes).map(Self::from) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + // We can't avoid curve checks when parsing a compressed encoding. + AffinePoint::from_bytes(*bytes).map(Self::from) + } + + fn to_bytes(&self) -> Self::Repr { + AffinePoint::from(self).to_bytes() + } +} + +impl GroupEncoding for SubgroupPoint { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup()) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint) + } + + fn to_bytes(&self) -> Self::Repr { + self.0.to_bytes() + } +} + +impl GroupEncoding for AffinePoint { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_bytes(*bytes) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + Self::from_bytes(*bytes) + } + + fn to_bytes(&self) -> Self::Repr { + self.to_bytes() + } +} + +#[test] +fn test_is_on_curve_var() { + assert!(AffinePoint::identity().is_on_curve_vartime()); +} + +#[test] +fn test_d_is_non_quadratic_residue() { + assert!(bool::from(EDWARDS_D.sqrt().is_none())); + assert!(bool::from((-EDWARDS_D).sqrt().is_none())); + assert!(bool::from((-EDWARDS_D).invert().unwrap().sqrt().is_none())); +} + +#[test] +fn test_affine_niels_point_identity() { + assert_eq!( + AffineNielsPoint::identity().v_plus_u, + AffinePoint::identity().to_niels().v_plus_u + ); + assert_eq!( + AffineNielsPoint::identity().v_minus_u, + AffinePoint::identity().to_niels().v_minus_u + ); + assert_eq!( + AffineNielsPoint::identity().t2d, + AffinePoint::identity().to_niels().t2d + ); +} + +#[test] +fn test_extended_niels_point_identity() { + assert_eq!( + ExtendedNielsPoint::identity().v_plus_u, + ExtendedPoint::identity().to_niels().v_plus_u + ); + assert_eq!( + ExtendedNielsPoint::identity().v_minus_u, + ExtendedPoint::identity().to_niels().v_minus_u + ); + assert_eq!( + ExtendedNielsPoint::identity().z, + ExtendedPoint::identity().to_niels().z + ); + assert_eq!( + ExtendedNielsPoint::identity().t2d, + ExtendedPoint::identity().to_niels().t2d + ); +} + +#[test] +fn test_assoc() { + let p = ExtendedPoint::from(AffinePoint { + u: Fq::from_raw([ + 0x81c5_71e5_d883_cfb0, + 0x049f_7a68_6f14_7029, + 0xf539_c860_bc3e_a21f, + 0x4284_715b_7ccc_8162, + ]), + v: Fq::from_raw([ + 0xbf09_6275_684b_b8ca, + 0xc7ba_2458_90af_256d, + 0x5911_9f3e_8638_0eb0, + 0x3793_de18_2f9f_b1d2, + ]), + }) + .mul_by_cofactor(); + assert!(p.is_on_curve_vartime()); + + assert_eq!( + (p * Fr::from(1000u64)) * Fr::from(3938u64), + p * (Fr::from(1000u64) * Fr::from(3938u64)), + ); +} + +#[test] +fn test_batch_normalize() { + let mut p = ExtendedPoint::from(AffinePoint { + u: Fq::from_raw([ + 0x81c5_71e5_d883_cfb0, + 0x049f_7a68_6f14_7029, + 0xf539_c860_bc3e_a21f, + 0x4284_715b_7ccc_8162, + ]), + v: Fq::from_raw([ + 0xbf09_6275_684b_b8ca, + 0xc7ba_2458_90af_256d, + 0x5911_9f3e_8638_0eb0, + 0x3793_de18_2f9f_b1d2, + ]), + }) + .mul_by_cofactor(); + + let mut v = vec![]; + for _ in 0..10 { + v.push(p); + p = p.double(); + } + + for p in &v { + assert!(p.is_on_curve_vartime()); + } + + let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect(); + let mut result0 = vec![AffinePoint::identity(); v.len()]; + ExtendedPoint::batch_normalize(&v, &mut result0); + for i in 0..10 { + assert!(expected[i] == result0[i]); + } + let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect(); + for i in 0..10 { + assert!(expected[i] == result1[i]); + assert!(v[i].is_on_curve_vartime()); + assert!(AffinePoint::from(v[i]) == expected[i]); + } + let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect(); + for i in 0..10 { + assert!(expected[i] == result2[i]); + assert!(v[i].is_on_curve_vartime()); + assert!(AffinePoint::from(v[i]) == expected[i]); + } +} + +#[cfg(test)] +const FULL_GENERATOR: AffinePoint = AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0xe4b3_d35d_f1a7_adfe, + 0xcaf5_5d1b_29bf_81af, + 0x8b0f_03dd_d60a_8187, + 0x62ed_cbb8_bf37_87c8, + ]), + Fq::from_raw([0xb, 0x0, 0x0, 0x0]), +); + +#[cfg(test)] +const EIGHT_TORSION: [AffinePoint; 8] = [ + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0xd92e_6a79_2720_0d43, + 0x7aa4_1ac4_3dae_8582, + 0xeaaa_e086_a166_18d1, + 0x71d4_df38_ba9e_7973, + ]), + Fq::from_raw([ + 0xff0d_2068_eff4_96dd, + 0x9106_ee90_f384_a4a1, + 0x16a1_3035_ad4d_7266, + 0x4958_bdb2_1966_982e, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0xfffe_ffff_0000_0001, + 0x67ba_a400_89fb_5bfe, + 0xa5e8_0b39_939e_d334, + 0x73ed_a753_299d_7d47, + ]), + Fq::from_raw([0x0, 0x0, 0x0, 0x0]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0xd92e_6a79_2720_0d43, + 0x7aa4_1ac4_3dae_8582, + 0xeaaa_e086_a166_18d1, + 0x71d4_df38_ba9e_7973, + ]), + Fq::from_raw([ + 0x00f2_df96_100b_6924, + 0xc2b6_b572_0c79_b75d, + 0x1c98_a7d2_5c54_659e, + 0x2a94_e9a1_1036_e51a, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([0x0, 0x0, 0x0, 0x0]), + Fq::from_raw([ + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0x26d1_9585_d8df_f2be, + 0xd919_893e_c24f_d67c, + 0x488e_f781_683b_bf33, + 0x0218_c81a_6eff_03d4, + ]), + Fq::from_raw([ + 0x00f2_df96_100b_6924, + 0xc2b6_b572_0c79_b75d, + 0x1c98_a7d2_5c54_659e, + 0x2a94_e9a1_1036_e51a, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0x0001_0000_0000_0000, + 0xec03_0002_7603_0000, + 0x8d51_ccce_7603_04d0, + 0x0, + ]), + Fq::from_raw([0x0, 0x0, 0x0, 0x0]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0x26d1_9585_d8df_f2be, + 0xd919_893e_c24f_d67c, + 0x488e_f781_683b_bf33, + 0x0218_c81a_6eff_03d4, + ]), + Fq::from_raw([ + 0xff0d_2068_eff4_96dd, + 0x9106_ee90_f384_a4a1, + 0x16a1_3035_ad4d_7266, + 0x4958_bdb2_1966_982e, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([0x0, 0x0, 0x0, 0x0]), + Fq::from_raw([0x1, 0x0, 0x0, 0x0]), + ), +]; + +#[test] +fn find_eight_torsion() { + let g = ExtendedPoint::from(FULL_GENERATOR); + assert!(!bool::from(g.is_small_order())); + let g = g.multiply(&FR_MODULUS_BYTES); + assert!(bool::from(g.is_small_order())); + + let mut cur = g; + + for (i, point) in EIGHT_TORSION.iter().enumerate() { + let tmp = AffinePoint::from(cur); + if &tmp != point { + panic!("{}th torsion point should be {:?}", i, tmp); + } + + cur += &g; + } +} + +#[test] +fn find_curve_generator() { + let mut trial_bytes = [0; 32]; + for _ in 0..255 { + let a = AffinePoint::from_bytes(trial_bytes); + if bool::from(a.is_some()) { + let a = a.unwrap(); + assert!(a.is_on_curve_vartime()); + let b = ExtendedPoint::from(a); + let b = b.multiply(&FR_MODULUS_BYTES); + assert!(bool::from(b.is_small_order())); + let b = b.double(); + assert!(bool::from(b.is_small_order())); + let b = b.double(); + assert!(bool::from(b.is_small_order())); + if !bool::from(b.is_identity()) { + let b = b.double(); + assert!(bool::from(b.is_small_order())); + assert!(bool::from(b.is_identity())); + assert_eq!(FULL_GENERATOR, a); + assert_eq!(AffinePoint::generator(), a); + assert!(bool::from(a.mul_by_cofactor().is_torsion_free())); + return; + } + } + + trial_bytes[0] += 1; + } + + panic!("should have found a generator of the curve"); +} + +#[test] +fn test_small_order() { + for point in EIGHT_TORSION.iter() { + assert!(bool::from(point.is_small_order())); + } +} + +#[test] +fn test_is_identity() { + let a = EIGHT_TORSION[0].mul_by_cofactor(); + let b = EIGHT_TORSION[1].mul_by_cofactor(); + + assert_eq!(a.u, b.u); + assert_eq!(a.v, a.z); + assert_eq!(b.v, b.z); + assert!(a.v != b.v); + assert!(a.z != b.z); + + assert!(bool::from(a.is_identity())); + assert!(bool::from(b.is_identity())); + + for point in EIGHT_TORSION.iter() { + assert!(bool::from(point.mul_by_cofactor().is_identity())); + } +} + +#[test] +fn test_mul_consistency() { + let a = Fr([ + 0x21e6_1211_d993_4f2e, + 0xa52c_058a_693c_3e07, + 0x9ccb_77bf_b12d_6360, + 0x07df_2470_ec94_398e, + ]); + let b = Fr([ + 0x0333_6d1c_be19_dbe0, + 0x0153_618f_6156_a536, + 0x2604_c9e1_fc3c_6b15, + 0x04ae_581c_eb02_8720, + ]); + let c = Fr([ + 0xd7ab_f5bb_2468_3f4c, + 0x9d77_12cc_274b_7c03, + 0x9732_93db_9683_789f, + 0x0b67_7e29_380a_97a7, + ]); + assert_eq!(a * b, c); + let p = ExtendedPoint::from(AffinePoint { + u: Fq::from_raw([ + 0x81c5_71e5_d883_cfb0, + 0x049f_7a68_6f14_7029, + 0xf539_c860_bc3e_a21f, + 0x4284_715b_7ccc_8162, + ]), + v: Fq::from_raw([ + 0xbf09_6275_684b_b8ca, + 0xc7ba_2458_90af_256d, + 0x5911_9f3e_8638_0eb0, + 0x3793_de18_2f9f_b1d2, + ]), + }) + .mul_by_cofactor(); + assert_eq!(p * c, (p * a) * b); + + // Test Mul implemented on ExtendedNielsPoint + assert_eq!(p * c, (p.to_niels() * a) * b); + assert_eq!(p.to_niels() * c, (p * a) * b); + assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b); + + // Test Mul implemented on AffineNielsPoint + let p_affine_niels = AffinePoint::from(p).to_niels(); + assert_eq!(p * c, (p_affine_niels * a) * b); + assert_eq!(p_affine_niels * c, (p * a) * b); + assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b); +} + +#[test] +fn test_serialization_consistency() { + let gen = FULL_GENERATOR.mul_by_cofactor(); + let mut p = gen; + + let v = vec![ + [ + 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97, + 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215, + ], + [ + 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195, + 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177, + ], + [ + 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82, + 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199, + ], + [ + 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83, + 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0, + ], + [ + 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4, + 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185, + ], + [ + 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166, + 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140, + ], + [ + 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33, + 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24, + ], + [ + 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211, + 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138, + ], + [ + 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93, + 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203, + ], + [ + 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14, + 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68, + ], + [ + 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122, + 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97, + ], + [ + 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150, + 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67, + ], + [ + 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166, + 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10, + ], + [ + 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161, + 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178, + ], + [ + 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73, + 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159, + ], + [ + 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0, + 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88, + ], + ]; + + let batched = AffinePoint::batch_from_bytes(v.iter().cloned()); + + for (expected_serialized, batch_deserialized) in v.into_iter().zip(batched.into_iter()) { + assert!(p.is_on_curve_vartime()); + let affine = AffinePoint::from(p); + let serialized = affine.to_bytes(); + let deserialized = AffinePoint::from_bytes(serialized).unwrap(); + assert_eq!(affine, deserialized); + assert_eq!(affine, batch_deserialized.unwrap()); + assert_eq!(expected_serialized, serialized); + p += gen; + } +} + +#[test] +fn test_zip_216() { + const NON_CANONICAL_ENCODINGS: [[u8; 32]; 2] = [ + // (0, 1) with sign bit set to 1. + [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, + ], + // (0, -1) with sign bit set to 1. + [ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, + 0xbd, 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, + 0x53, 0xa7, 0xed, 0xf3, + ], + ]; + + for b in &NON_CANONICAL_ENCODINGS { + { + let mut encoding = *b; + + // The normal API should reject the non-canonical encoding. + assert!(bool::from(AffinePoint::from_bytes(encoding).is_none())); + + // If we clear the sign bit of the non-canonical encoding, it should be + // accepted by the normal API. + encoding[31] &= 0b0111_1111; + assert!(bool::from(AffinePoint::from_bytes(encoding).is_some())); + } + + { + // The bug-preserving API should accept the non-canonical encoding, and the + // resulting point should serialize to a different (canonical) encoding. + let parsed = AffinePoint::from_bytes_pre_zip216_compatibility(*b).unwrap(); + let mut encoded = parsed.to_bytes(); + assert_ne!(b, &encoded); + + // If we set the sign bit of the serialized encoding, it should match the + // non-canonical encoding. + encoded[31] |= 0b1000_0000; + assert_eq!(b, &encoded); + } + } +} diff --git a/src/jubjub/fr.rs b/src/jubjub/fr.rs new file mode 100644 index 00000000..05ecbdb6 --- /dev/null +++ b/src/jubjub/fr.rs @@ -0,0 +1,1232 @@ +//! This module provides an implementation of the Jubjub scalar field $\mathbb{F}_r$ +//! where `r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` + +use core::convert::TryInto; +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub}; + +use ff::{Field, FieldBits, PrimeField, PrimeFieldBits, WithSmallOrderMulGroup}; +use rand_core::RngCore; +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 crate::util::{adc, mac, sbb}; + +/// Represents an element of the scalar field $\mathbb{F}_r$ of the Jubjub elliptic +/// curve construction. +// The internal representation of this type is four 64-bit unsigned +// integers in little-endian order. Elements of Fr are always in +// Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. +#[derive(Clone, Copy, Eq)] +pub struct Fr(pub(crate) [u64; 4]); + +impl fmt::Debug for Fr { + 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, "{:02x}", b)?; + } + Ok(()) + } +} + +impl fmt::Display for Fr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for Fr { + fn from(val: u64) -> Fr { + Fr([val, 0, 0, 0]) * R2 + } +} + +impl ConstantTimeEq for Fr { + 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 Fr { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl ConditionallySelectable for Fr { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fr([ + 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 +/// r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 +pub const MODULUS: Fr = Fr([ + 0xd097_0e5e_d6f7_2cb7, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, +]); + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 252; + +/// 2^-1 +const TWO_INV: Fr = Fr([ + 0x7b47_8d09_4846_9a48, + 0xccbe_fb61_99bf_7be9, + 0xccc6_27f7_f65e_27fa, + 0x0c12_58ac_d662_82b7, +]); + +// GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) +const GENERATOR: Fr = Fr([ + 0x720b_1b19_d49e_a8f1, + 0xbf4a_a361_01f1_3a58, + 0x5fa8_cc96_8193_ccbb, + 0x0e70_cbdc_7dcc_f3ac, +]); + +// 2^S * t = MODULUS - 1 with t odd +const S: u32 = 1; + +// 2^S root of unity computed by GENERATOR^t +const ROOT_OF_UNITY: Fr = Fr([ + 0xaa9f_02ab_1d61_24de, + 0xb352_4a64_6611_2932, + 0x7342_2612_15ac_260b, + 0x04d6_b87b_1da2_59e2, +]); + +/// ROOT_OF_UNITY^-1 (which is equal to ROOT_OF_UNITY because S = 1). +const ROOT_OF_UNITY_INV: Fr = ROOT_OF_UNITY; + +/// GENERATOR^{2^s} where t * 2^s + 1 = q with t odd. +/// In other words, this is a t root of unity. +const DELTA: Fr = Fr([ + 0x994f_5ac0_c8e4_1613, + 0x3bb7_3163_0bbf_0b84, + 0x1df0_a482_0371_a563, + 0x0e30_3e96_f8cb_47bd, +]); + +impl<'a> Neg for &'a Fr { + type Output = Fr; + + #[inline] + fn neg(self) -> Fr { + self.neg() + } +} + +impl Neg for Fr { + type Output = Fr; + + #[inline] + fn neg(self) -> Fr { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fr> for &'a Fr { + type Output = Fr; + + #[inline] + fn sub(self, rhs: &'b Fr) -> Fr { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Fr> for &'a Fr { + type Output = Fr; + + #[inline] + fn add(self, rhs: &'b Fr) -> Fr { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Fr> for &'a Fr { + type Output = Fr; + + #[inline] + fn mul(self, rhs: &'b Fr) -> Fr { + // Schoolbook multiplication + + self.mul(rhs) + } +} + +impl_binops_additive!(Fr, Fr); +impl_binops_multiplicative!(Fr, Fr); + +impl core::iter::Sum for Fr +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 Fr +where + T: core::borrow::Borrow, +{ + fn product(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::one(), |acc, item| acc * item.borrow()) + } +} + +/// INV = -(r^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0x1ba3_a358_ef78_8ef9; + +/// R = 2^256 mod r +const R: Fr = Fr([ + 0x25f8_0bb3_b996_07d9, + 0xf315_d62f_66b6_e750, + 0x9325_14ee_eb88_14f4, + 0x09a6_fc6f_4791_55c6, +]); + +/// R^2 = 2^512 mod r +const R2: Fr = Fr([ + 0x6771_9aa4_95e5_7731, + 0x51b0_cef0_9ce3_fc26, + 0x69da_b7fa_c026_e9a5, + 0x04f6_547b_8d12_7688, +]); + +/// R^3 = 2^768 mod r +const R3: Fr = Fr([ + 0xe0d6_c656_3d83_0544, + 0x323e_3883_598d_0f85, + 0xf0fe_a300_4c2e_2ba8, + 0x0587_4f84_9467_37ec, +]); + +impl Default for Fr { + fn default() -> Self { + Self::zero() + } +} + +impl Fr { + /// Returns zero, the additive identity. + #[inline] + pub const fn zero() -> Fr { + Fr([0, 0, 0, 0]) + } + + /// Returns one, the multiplicative identity. + #[inline] + pub const fn one() -> Fr { + R + } + + /// Doubles this field element. + #[inline] + pub const fn double(&self) -> Fr { + self.add(self) + } + + /// Attempts to convert a little-endian byte representation of + /// a field element into an element of `Fr`, failing if the input + /// is not canonical (is not smaller than r). + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { + let mut tmp = Fr([0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); + tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); + tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); + tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().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 `Fr` 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 = Fr::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 + /// an element of Fr by reducing modulo r. + pub fn from_bytes_wide(bytes: &[u8; 64]) -> Fr { + Fr::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()), + ]) + } + + fn from_u512(limbs: [u64; 8]) -> Fr { + // 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 = Fr([limbs[0], limbs[1], limbs[2], limbs[3]]); + let d1 = Fr([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) `Fr` representation. + pub const fn from_raw(val: [u64; 4]) -> Self { + (&Fr(val)).mul(&R2) + } + + /// Squares this element. + #[inline] + pub const fn square(&self) -> Fr { + 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); + + Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Computes the square root of this element, if it exists. + pub fn sqrt(&self) -> CtOption { + // Because r = 3 (mod 4) + // sqrt can be done with only one exponentiation, + // via the computation of self^((r + 1) // 4) (mod r) + let sqrt = self.pow_vartime(&[ + 0xb425_c397_b5bd_cb2e, + 0x299a_0824_f332_0420, + 0x4199_cec0_404d_0ec0, + 0x039f_6d3a_994c_ebea, + ]); + + CtOption::new( + sqrt, + (sqrt * sqrt).ct_eq(self), // Only return Some if it's the square root. + ) + } + + /// 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.mul_assign(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 Fr, num_times: usize) { + for _ in 0..num_times { + *n = n.square(); + } + } + // found using https://github.com/kwantam/addchain + let mut t1 = self.square(); + let mut t0 = t1.square(); + let mut t3 = t0 * t1; + let t6 = t3 * self; + let t7 = t6 * t1; + let t12 = t7 * t3; + let t13 = t12 * t0; + let t16 = t12 * t3; + let t2 = t13 * t3; + let t15 = t16 * t3; + let t19 = t2 * t0; + let t9 = t15 * t3; + let t18 = t9 * t3; + let t14 = t18 * t1; + let t4 = t18 * t0; + let t8 = t18 * t3; + let t17 = t14 * t3; + let t11 = t8 * t3; + t1 = t17 * t3; + let t5 = t11 * t3; + t3 = t5 * t0; + t0 = t5.square(); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t3); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t8); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t19); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t13); + square_assign_multi(&mut t0, 8); + t0.mul_assign(&t14); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t18); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t17); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t16); + square_assign_multi(&mut t0, 3); + t0.mul_assign(self); + square_assign_multi(&mut t0, 11); + t0.mul_assign(&t11); + square_assign_multi(&mut t0, 8); + t0.mul_assign(&t5); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t15); + square_assign_multi(&mut t0, 8); + t0.mul_assign(self); + square_assign_multi(&mut t0, 12); + t0.mul_assign(&t13); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t9); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t15); + square_assign_multi(&mut t0, 14); + t0.mul_assign(&t14); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t13); + square_assign_multi(&mut t0, 2); + t0.mul_assign(self); + square_assign_multi(&mut t0, 6); + t0.mul_assign(self); + square_assign_multi(&mut t0, 9); + t0.mul_assign(&t7); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t12); + square_assign_multi(&mut t0, 8); + t0.mul_assign(&t11); + square_assign_multi(&mut t0, 3); + t0.mul_assign(self); + square_assign_multi(&mut t0, 12); + t0.mul_assign(&t9); + square_assign_multi(&mut t0, 11); + t0.mul_assign(&t8); + square_assign_multi(&mut t0, 8); + t0.mul_assign(&t7); + square_assign_multi(&mut t0, 4); + t0.mul_assign(&t6); + square_assign_multi(&mut t0, 10); + t0.mul_assign(&t5); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t3); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t4); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t3); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t2); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t2); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t1); + + CtOption::new(t0, !self.ct_eq(&Self::zero())) + } + + #[inline] + #[allow(clippy::too_many_arguments)] + 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 + (&Fr([r4, r5, r6, r7])).sub(&MODULUS) + } + + /// Multiplies this element by another element + #[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); + + Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Subtracts another element from this element. + #[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); + + Fr([d0, d1, d2, d3]) + } + + /// Adds this element to another element. + #[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. + (&Fr([d0, d1, d2, d3])).sub(&MODULUS) + } + + /// Negates this element. + #[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); + + Fr([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) + } +} + +impl From for [u8; 32] { + fn from(value: Fr) -> [u8; 32] { + value.to_bytes() + } +} + +impl<'a> From<&'a Fr> for [u8; 32] { + fn from(value: &'a Fr) -> [u8; 32] { + value.to_bytes() + } +} + +impl Field for Fr { + 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 { + self.sqrt() + } +} + +impl PrimeField for Fr { + 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 = + "0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7"; + 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 Ord for Fr { + fn cmp(&self, other: &Self) -> core::cmp::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) { + core::cmp::Ordering::Equal => None, + res => Some(res), + }) + .unwrap_or(core::cmp::Ordering::Equal) + } +} + +impl PartialOrd for Fr { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl WithSmallOrderMulGroup<3> for Fr { + const ZETA: Self = unimplemented!(); +} + +impl PrimeFieldBits for Fr { + type ReprBits = [u64; 4]; + + fn to_le_bits(&self) -> FieldBits { + let bytes = self.to_repr(); + + 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 { + FieldBits::new(MODULUS.0) + } +} + +#[test] +fn test_constants() { + assert_eq!( + Fr::MODULUS, + "0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7", + ); + + assert_eq!(Fr::from(2) * Fr::TWO_INV, Fr::ONE); + + assert_eq!(Fr::ROOT_OF_UNITY * Fr::ROOT_OF_UNITY_INV, Fr::ONE); + + // ROOT_OF_UNITY^{2^s} mod m == 1 + assert_eq!(Fr::ROOT_OF_UNITY.pow(&[1u64 << Fr::S, 0, 0, 0]), Fr::ONE); + + // DELTA^{t} mod m == 1 + assert_eq!( + Fr::DELTA.pow(&[ + 0x684b_872f_6b7b_965b, + 0x5334_1049_e664_0841, + 0x8333_9d80_809a_1d80, + 0x073e_da75_3299_d7d4, + ]), + Fr::ONE, + ); +} + +#[test] +fn test_inv() { + // Compute -(r^{-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); +} + +#[test] +fn test_debug() { + assert_eq!( + format!("{:?}", Fr::zero()), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:?}", Fr::one()), + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + assert_eq!( + format!("{:?}", R2), + "0x09a6fc6f479155c6932514eeeb8814f4f315d62f66b6e75025f80bb3b99607d9" + ); +} + +#[allow(clippy::eq_op)] +#[test] +fn test_equality() { + assert_eq!(Fr::zero(), Fr::zero()); + assert_eq!(Fr::one(), Fr::one()); + assert_eq!(R2, R2); + + assert!(Fr::zero() != Fr::one()); + assert!(Fr::one() != R2); +} + +#[test] +fn test_to_bytes() { + assert_eq!( + Fr::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!( + Fr::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(), + [ + 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, + 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 + ] + ); + + assert_eq!( + (-&Fr::one()).to_bytes(), + [ + 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ] + ); +} + +#[test] +fn test_from_bytes() { + assert_eq!( + Fr::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(), + Fr::zero() + ); + + assert_eq!( + Fr::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(), + Fr::one() + ); + + assert_eq!( + Fr::from_bytes(&[ + 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, + 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 + ]) + .unwrap(), + R2 + ); + + // -1 should work + assert!(bool::from( + Fr::from_bytes(&[ + 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ]) + .is_some() + )); + + // modulus is invalid + assert!(bool::from( + Fr::from_bytes(&[ + 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ]) + .is_none() + )); + + // Anything larger than the modulus is invalid + assert!(bool::from( + Fr::from_bytes(&[ + 184, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ]) + .is_none() + )); + + assert!(bool::from( + Fr::from_bytes(&[ + 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 104, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ]) + .is_none() + )); + + assert!(bool::from( + Fr::from_bytes(&[ + 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 15 + ]) + .is_none() + )); +} + +#[test] +fn test_from_u512_zero() { + assert_eq!( + Fr::zero(), + Fr::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, Fr::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); +} + +#[test] +fn test_from_u512_r2() { + assert_eq!(R2, Fr::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, + Fr::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, + Fr::from_bytes_wide(&[ + 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, + 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9, 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!( + -&Fr::one(), + Fr::from_bytes_wide(&[ + 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, 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!( + Fr([ + 0x8b75_c901_5ae4_2a22, + 0xe590_82e7_bf9e_38b8, + 0x6440_c912_61da_51b3, + 0x0a5e_07ff_b209_91cf, + ]), + Fr::from_bytes_wide(&[0xff; 64]) + ); +} + +#[test] +fn test_zero() { + assert_eq!(Fr::zero(), -&Fr::zero()); + assert_eq!(Fr::zero(), Fr::zero() + Fr::zero()); + assert_eq!(Fr::zero(), Fr::zero() - Fr::zero()); + assert_eq!(Fr::zero(), Fr::zero() * Fr::zero()); +} + +#[cfg(test)] +const LARGEST: Fr = Fr([ + 0xd097_0e5e_d6f7_2cb6, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, +]); + +#[test] +fn test_addition() { + let mut tmp = LARGEST; + tmp += &LARGEST; + + assert_eq!( + tmp, + Fr([ + 0xd097_0e5e_d6f7_2cb5, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9 + ]) + ); + + let mut tmp = LARGEST; + tmp += &Fr([1, 0, 0, 0]); + + assert_eq!(tmp, Fr::zero()); +} + +#[test] +fn test_negation() { + let tmp = -&LARGEST; + + assert_eq!(tmp, Fr([1, 0, 0, 0])); + + let tmp = -&Fr::zero(); + assert_eq!(tmp, Fr::zero()); + let tmp = -&Fr([1, 0, 0, 0]); + assert_eq!(tmp, LARGEST); +} + +#[test] +fn test_subtraction() { + let mut tmp = LARGEST; + tmp -= &LARGEST; + + assert_eq!(tmp, Fr::zero()); + + let mut tmp = Fr::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 = Fr::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 = Fr::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(Fr::zero().invert().is_none())); + assert_eq!(Fr::one().invert().unwrap(), Fr::one()); + assert_eq!((-&Fr::one()).invert().unwrap(), -&Fr::one()); + + let mut tmp = R2; + + for _ in 0..100 { + let mut tmp2 = tmp.invert().unwrap(); + tmp2.mul_assign(&tmp); + + assert_eq!(tmp2, Fr::one()); + + tmp.add_assign(&R2); + } +} + +#[test] +fn test_invert_is_pow() { + let r_minus_2 = [ + 0xd097_0e5e_d6f7_2cb5, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, + ]; + + let mut r1 = R; + let mut r2 = R; + let mut r3 = R; + + for _ in 0..100 { + r1 = r1.invert().unwrap(); + r2 = r2.pow_vartime(&r_minus_2); + r3 = r3.pow(&r_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() { + let mut square = Fr([ + // r - 2 + 0xd097_0e5e_d6f7_2cb5, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, + ]); + + 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 -= Fr::one(); + } + + assert_eq!(47, none_count); +} + +#[test] +fn test_from_raw() { + assert_eq!( + Fr::from_raw([ + 0x25f8_0bb3_b996_07d8, + 0xf315_d62f_66b6_e750, + 0x9325_14ee_eb88_14f4, + 0x09a6_fc6f_4791_55c6, + ]), + Fr::from_raw([0xffff_ffff_ffff_ffff; 4]) + ); + + assert_eq!(Fr::from_raw(MODULUS.0), Fr::zero()); + + assert_eq!(Fr::from_raw([1, 0, 0, 0]), R); +} diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs new file mode 100644 index 00000000..fdb6a8c7 --- /dev/null +++ b/src/jubjub/mod.rs @@ -0,0 +1,5 @@ +mod curve; +mod fr; + +pub use curve::*; +pub use fr::*; diff --git a/src/lib.rs b/src/lib.rs index 634efa69..60aa1be6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,9 @@ mod arithmetic; +pub mod bls12_381; pub mod bn256; +pub mod jubjub; pub mod pairing; pub mod pasta; pub mod secp256k1; @@ -25,6 +27,7 @@ use group; #[cfg(test)] pub mod tests; +mod util; #[cfg(all(feature = "prefetch", target_arch = "x86_64"))] #[inline(always)] diff --git a/src/tests/curve.rs b/src/tests/curve.rs index 3a22c588..d3784014 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -179,12 +179,12 @@ fn projective_addition() { let a = G::identity(); let a = a.double(); - assert!(bool::from(c.is_on_curve())); + assert!(bool::from(a.is_on_curve())); assert!(bool::from(a.is_identity())); let a = G::generator(); let a = a.double(); - assert!(bool::from(c.is_on_curve())); + assert!(bool::from(a.is_on_curve())); assert_eq!(a, G::generator() + G::generator()); let a = G::random(OsRng); diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 00000000..28746c2d --- /dev/null +++ b/src/util.rs @@ -0,0 +1,20 @@ +/// Compute a + b + carry, returning the result and the new carry over. +#[inline(always)] +pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + (b as u128) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a - (b + borrow), returning the result and the new borrow. +#[inline(always)] +pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { + let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a + (b * c) + carry, returning the result and the new carry over. +#[inline(always)] +pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +}