Skip to content
This repository was archived by the owner on Aug 17, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 16 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" ]
198 changes: 192 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])
Comment thread
CPerezz marked this conversation as resolved.
Outdated
}
}

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

/// Fp(1/2)
const TWO_INV: Fp = Fp([
0xdcff7fffffffd556,
0x0f55ffff58a9ffff,
0xb39869507b587b12,
0xb23ba5c279c2895f,
0x258dd3db21a5d66b,
0xd0088f51cbff34d,
]);
Comment thread
CPerezz marked this conversation as resolved.

/// Generator of the field.
const GENERATOR: Fp = Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x2]);
Comment thread
CPerezz marked this conversation as resolved.
Outdated

/// Primitive root of unity
const ROOT_OF_UNITY: Fp = Fp([
0x8527e005bf87356d,
0xd867814cd448c6d3,
0xae5a5b133eddd420,
0x356f43ccc999120f,
0xc4296fb7e96da4d6,
0x1938c01908424cd8,
]);
Comment thread
CPerezz marked this conversation as resolved.

/// Inverse of the primitive root of unity.
const ROOT_OF_UNITY_INV: Fp = Fp([
0xded7a779aa2898c8,
0x53f260542172180b,
0xf05b58c2034dddd6,
0x4a7cdeacf037ae72,
0x7aa8223a111aeea8,
0x5046e6d0c9ac33e,
]);
Comment thread
CPerezz marked this conversation as resolved.

/// DELTA
const DELTA: Fp = Fp([
0xca172967c7a255d7,
0xa5d11a97b6038a99,
0x19e243df219f0178,
0x74bc071476d308b7,
0x73a18c6550d55c00,
0x1536fbfcdcf7f2f5,
]);
Comment thread
CPerezz marked this conversation as resolved.

/// ZETA
// sage: modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
// sage: GF(modulus).primitive_element() ^ ((modulus - 1) // 3)
// 793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350
// 5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe
const ZETA: Fp = Fp([
0x2e01fffffffefffe,
0xde17d813620a0002,
0xddb3a93be6f89688,
0xba69c6076a0f77ea,
0x5f19672fdf76ce51,
0x0000000000000000,
]);
Comment thread
CPerezz marked this conversation as resolved.
Outdated

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

Expand Down Expand Up @@ -158,6 +245,110 @@ 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()
}
}

#[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 Default for ReprFp {
fn default() -> Self {
Self([0u8; 48])
}
}

impl PrimeField for Fp {
type Repr = ReprFp;

fn from_repr(r: Self::Repr) -> CtOption<Self> {
Self::from_bytes(&r.0)
Comment thread
han0110 marked this conversation as resolved.
}

fn to_repr(&self) -> Self::Repr {
ReprFp(self.to_bytes())
}

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

const MODULUS: &'static str = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab";
const NUM_BITS: u32 = 381;
const CAPACITY: u32 = Self::NUM_BITS - 1;
const TWO_INV: Self = TWO_INV;
const MULTIPLICATIVE_GENERATOR: Self = GENERATOR;
const S: u32 = 32;
Comment thread
CPerezz marked this conversation as resolved.
Outdated
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 +362,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 +581,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
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