Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CurveExt for G1Projective #4

Merged
merged 20 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ optional = true
version = "0.13"
default-features = false

[dependencies.pasta_curves]
version = "0.5"
default-features = false
optional = true

[dependencies.group]
version = "0.13"
default-features = false
Expand Down Expand Up @@ -68,7 +73,7 @@ default = ["groups", "pairings", "alloc", "bits"]
bits = ["ff/bits"]
groups = ["group"]
pairings = ["groups", "pairing"]
alloc = ["group/alloc"]
alloc = ["group/alloc", "pasta_curves/alloc"]
experimental = ["digest"]
nightly = ["subtle/nightly"]
basefield = []
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "1.61.0"
channel = "1.66.0"
components = [ "clippy", "rustfmt" ]
269 changes: 263 additions & 6 deletions src/fp.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//! This module provides an implementation of the BLS12-381 base field `GF(p)`
//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab`

#![allow(clippy::needless_borrow)]
use core::cmp::Ordering;
use core::fmt;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use ff::{Field, PrimeField, WithSmallOrderMulGroup};
use rand_core::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};

Expand Down Expand Up @@ -54,6 +57,27 @@ impl PartialEq for Fp {
}
}

impl Ord for Fp {
fn cmp(&self, other: &Self) -> Ordering {
let left = self.to_repr().0;
let right = other.to_repr().0;
left.iter()
.zip(right.iter())
.rev()
.find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) {
Ordering::Equal => None,
res => Some(res),
})
.unwrap_or(Ordering::Equal)
}
}

impl PartialOrd for Fp {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl ConditionallySelectable for Fp {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Fp([
Expand All @@ -67,6 +91,12 @@ impl ConditionallySelectable for Fp {
}
}

impl From<u64> for Fp {
fn from(value: u64) -> Self {
Self([value, 0, 0, 0, 0, 0]) * R2
}
}

/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787
const MODULUS: [u64; 6] = [
0xb9fe_ffff_ffff_aaab,
Expand Down Expand Up @@ -110,6 +140,85 @@ const R3: Fp = Fp([
0x0aa6_3460_9175_5d4d,
]);

/// Fp(1/2)
/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
/// sage: hex(((1 / 2) * (2^384)) % modulus)
/// '0x17fbb8571a006596d3916126f2d14ca26e22d1ec31ebb502633cb57c253c276f855000053ab000011804000000015554'
const TWO_INV: Fp = Fp([
0x1804000000015554,
0x855000053ab00001,
0x633cb57c253c276f,
0x6e22d1ec31ebb502,
0xd3916126f2d14ca2,
0x17fbb8571a006596,
]);

/// Generator of the field.
/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
/// sage: hex((2 * (2^384)) % modulus)
/// '0x11ebab9dbb81e28c6cf28d7901622c038b256521ed1f9bcb57605e0db0ddbb51b93c0018d6c40005321300000006554f'
const GENERATOR: Fp = Fp([
0x321300000006554f,
0xb93c0018d6c40005,
0x57605e0db0ddbb51,
0x8b256521ed1f9bcb,
0x6cf28d7901622c03,
0x11ebab9dbb81e28c,
]);

/// Primitive root of unity
/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
/// sage: hex((GF(modulus)(2) ^ ((modulus - 1) >> 1)) * (2^384))
/// '0x40ab3263eff0206ef148d1ea0f4c069eca8f3318332bb7a07e83a49a2e99d6932b7fff2ed47fffd43f5fffffffcaaae'
const ROOT_OF_UNITY: Fp = Fp([
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x40ab3263eff0206,
]);

/// Inverse of the primitive root of unity.
/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
/// sage: hex((1 / (GF(modulus)(2) ^ ((modulus - 1) >> 1))) * (2^384))
/// '0x40ab3263eff0206ef148d1ea0f4c069eca8f3318332bb7a07e83a49a2e99d6932b7fff2ed47fffd43f5fffffffcaaae'
const ROOT_OF_UNITY_INV: Fp = Fp([
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x40ab3263eff0206,
]);

/// DELTA
/// DELTA
/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
/// sage: hex((GF(modulus)(2) ^ (2 ^ 1)) * (2^384))
/// '0x9d645513d83de7e8ec9733bbf78ab2fb1d37ebee6ba24d7478fe97a6b0a807f53cc0032fc34000aaa270000000cfff3'
const DELTA: Fp = Fp([
0xaa270000000cfff3,
0x53cc0032fc34000a,
0x478fe97a6b0a807f,
0xb1d37ebee6ba24d7,
0x8ec9733bbf78ab2f,
0x9d645513d83de7e,
]);

/// ZETA
/// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
/// sage: hex((1 / (GF(modulus)(2) ^ ((modulus - 1) // 3))) * (2^384))
/// '0x18f020655463874103f97d6e83d050d28eb60ebe01bacb9e587042afd3851b955dab22461fcda5d2cd03c9e48671f071'
const ZETA: Fp = Fp([
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
]);

impl<'a> Neg for &'a Fp {
type Output = Fp;

Expand Down Expand Up @@ -158,6 +267,131 @@ impl<'a, 'b> Mul<&'b Fp> for &'a Fp {
impl_binops_additive!(Fp, Fp);
impl_binops_multiplicative!(Fp, Fp);

impl<T> core::iter::Sum<T> for Fp
where
T: core::borrow::Borrow<Fp>,
{
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = T>,
{
iter.fold(Self::zero(), |acc, item| acc + item.borrow())
}
}

impl<T> core::iter::Product<T> for Fp
where
T: core::borrow::Borrow<Fp>,
{
fn product<I>(iter: I) -> Self
where
I: Iterator<Item = T>,
{
iter.fold(Self::one(), |acc, item| acc * item.borrow())
}
}

impl Field for Fp {
const ZERO: Self = Fp::zero();
const ONE: Self = Fp::one();

fn random(rng: impl RngCore) -> Self {
Self::random(rng)
}

fn square(&self) -> Self {
self.square()
}

fn double(&self) -> Self {
self.add(self)
}

fn invert(&self) -> CtOption<Self> {
self.invert()
}

fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
ff::helpers::sqrt_ratio_generic(num, div)
}

fn sqrt(&self) -> CtOption<Self> {
self.sqrt()
}
}

/// This struct represents the [`Fp`] struct serialized as an array of 48 bytes
/// in Little Endian.
#[derive(Debug, Clone, Copy)]
pub struct ReprFp(pub(crate) [u8; 48]);

impl AsMut<[u8]> for ReprFp {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0[..]
}
}

impl AsRef<[u8]> for ReprFp {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}

impl AsRef<[u8; 48]> for ReprFp {
fn as_ref(&self) -> &[u8; 48] {
&self.0
}
}

impl From<[u8; 48]> for ReprFp {
fn from(value: [u8; 48]) -> Self {
// This uses little endian and so, assumes the array passed in is
// in that format.
Self(value)
}
}

impl Default for ReprFp {
fn default() -> Self {
Self([0u8; 48])
}
}

impl PrimeField for Fp {
type Repr = ReprFp;

fn from_repr(mut r: Self::Repr) -> CtOption<Self> {
// This uses little endian and so, assumes the array passed in is
// in that format.
r.0.reverse();
Self::from_bytes(&r.0)
han0110 marked this conversation as resolved.
Show resolved Hide resolved
}

fn to_repr(&self) -> Self::Repr {
let mut le_bytes = self.to_bytes();
le_bytes.reverse();
ReprFp(le_bytes)
}

fn is_odd(&self) -> Choice {
Choice::from(self.to_bytes()[0] & 1)
}

const MODULUS: &'static str = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab";
const NUM_BITS: u32 = 381;
const CAPACITY: u32 = Self::NUM_BITS - 1;
const TWO_INV: Self = TWO_INV;
const MULTIPLICATIVE_GENERATOR: Self = GENERATOR;
const S: u32 = 1;
const ROOT_OF_UNITY: Self = ROOT_OF_UNITY;
const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV;
const DELTA: Self = DELTA;
}

impl WithSmallOrderMulGroup<3> for Fp {
const ZETA: Self = ZETA;
}

impl Fp {
/// Returns zero, the additive identity.
#[inline]
Expand All @@ -171,11 +405,6 @@ impl Fp {
R
}

/// Checks if a value is equal to zero in constant time.
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<Fp> {
Expand Down Expand Up @@ -395,7 +624,7 @@ impl Fp {

// Attempt to subtract the modulus, to ensure the value
// is smaller than the modulus.
(&Fp([d0, d1, d2, d3, d4, d5])).subtract_p()
(Fp([d0, d1, d2, d3, d4, d5])).subtract_p()
}

#[inline]
Expand Down Expand Up @@ -669,6 +898,27 @@ impl Fp {
}
}

#[test]
fn test_constants() {
assert_eq!(Fp::ONE.double(), GENERATOR);
assert_eq!(Fp::ONE, Fp::ONE.double() * TWO_INV);
assert_eq!(Fp::ONE, ROOT_OF_UNITY.pow([1 << Fp::S]));
assert_eq!(Fp::ONE, ROOT_OF_UNITY * ROOT_OF_UNITY_INV);
// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
// sage: hex((modulus - 1) >> 1)
// '0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd555'
let t = [
0xdcff7fffffffd555,
0x0f55ffff58a9ffff,
0xb39869507b587b12,
0xb23ba5c279c2895f,
0x258dd3db21a5d66b,
0xd0088f51cbff34d,
];
assert_eq!(Fp::ONE, DELTA.pow(t));
assert_eq!(Fp::ONE, ZETA.pow([3]));
}

#[test]
fn test_conditional_selection() {
let a = Fp([1, 2, 3, 4, 5, 6]);
Expand Down Expand Up @@ -987,6 +1237,13 @@ fn test_lexicographic_largest() {
));
}

#[test]
fn test_repr() {
for value in [Fp::ZERO, Fp::ONE, Fp::ROOT_OF_UNITY] {
assert_eq!(Fp::from_repr(value.to_repr()).unwrap(), value);
}
}

#[cfg(feature = "zeroize")]
#[test]
fn test_zeroize() {
Expand Down
2 changes: 2 additions & 0 deletions src/fp2.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! This module implements arithmetic over the quadratic extension field Fp2.

#![allow(clippy::needless_borrow)]
use core::fmt;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use ff::Field;
use rand_core::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};

Expand Down
Loading
Loading