#![allow(
clippy::should_implement_trait,
clippy::suspicious_op_assign_impl,
clippy::unused_unit,
clippy::unnecessary_cast,
clippy::too_many_arguments,
clippy::identity_op,
rustdoc::bare_urls
)]
#[path = "field/p521_64.rs"]
mod field_impl;
mod loose;
pub(crate) use self::loose::LooseFieldElement;
use self::field_impl::*;
use crate::{FieldBytes, NistP521, U576};
use core::{
fmt::{self, Debug},
iter::{Product, Sum},
ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
};
use elliptic_curve::{
ff::{self, Field, PrimeField},
generic_array::GenericArray,
rand_core::RngCore,
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeLess, CtOption},
zeroize::DefaultIsZeroes,
Error, FieldBytesEncoding,
};
use super::util::u576_to_le_bytes;
const MODULUS_HEX: &str = "00000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
pub(crate) const MODULUS: U576 = U576::from_be_hex(MODULUS_HEX);
#[derive(Clone, Copy)]
pub struct FieldElement(pub(crate) fiat_p521_tight_field_element);
impl FieldElement {
pub const ZERO: Self = Self::from_u64(0);
pub const ONE: Self = Self::from_u64(1);
const BYTES: usize = 66;
pub fn from_bytes(repr: &FieldBytes) -> CtOption<Self> {
let uint = <U576 as FieldBytesEncoding<NistP521>>::decode_field_bytes(repr);
Self::from_uint(uint)
}
pub fn from_slice(slice: &[u8]) -> elliptic_curve::Result<Self> {
if slice.len() != Self::BYTES {
return Err(Error);
}
Option::from(Self::from_bytes(GenericArray::from_slice(slice))).ok_or(Error)
}
pub fn from_uint(uint: U576) -> CtOption<Self> {
let is_some = uint.ct_lt(&MODULUS);
CtOption::new(Self::from_uint_unchecked(uint), is_some)
}
pub(crate) const fn from_hex(hex: &str) -> Self {
Self::from_uint_unchecked(U576::from_be_hex(hex))
}
pub const fn from_u64(w: u64) -> Self {
Self::from_uint_unchecked(U576::from_u64(w))
}
pub(crate) const fn from_uint_unchecked(w: U576) -> Self {
Self(fiat_p521_from_bytes(&u576_to_le_bytes(w)))
}
pub fn to_bytes(self) -> FieldBytes {
let mut ret = fiat_p521_to_bytes(&self.0);
ret.reverse();
GenericArray::clone_from_slice(&ret)
}
pub fn is_odd(&self) -> Choice {
Choice::from(self.0[0] as u8 & 1)
}
pub fn is_even(&self) -> Choice {
!self.is_odd()
}
pub fn is_zero(&self) -> Choice {
self.ct_eq(&Self::ZERO)
}
#[allow(dead_code)] pub(crate) const fn add_loose(&self, rhs: &Self) -> LooseFieldElement {
LooseFieldElement(fiat_p521_add(&self.0, &rhs.0))
}
#[allow(dead_code)] #[must_use]
pub(crate) const fn double_loose(&self) -> LooseFieldElement {
Self::add_loose(self, self)
}
#[allow(dead_code)] pub(crate) const fn sub_loose(&self, rhs: &Self) -> LooseFieldElement {
LooseFieldElement(fiat_p521_sub(&self.0, &rhs.0))
}
#[allow(dead_code)] pub(crate) const fn neg_loose(&self) -> LooseFieldElement {
LooseFieldElement(fiat_p521_opp(&self.0))
}
pub const fn add(&self, rhs: &Self) -> Self {
Self(fiat_p521_carry_add(&self.0, &rhs.0))
}
pub const fn sub(&self, rhs: &Self) -> Self {
Self(fiat_p521_carry_sub(&self.0, &rhs.0))
}
pub const fn neg(&self) -> Self {
Self(fiat_p521_carry_opp(&self.0))
}
#[must_use]
pub const fn double(&self) -> Self {
self.add(self)
}
pub const fn multiply(&self, rhs: &Self) -> Self {
LooseFieldElement::mul(&self.relax(), &rhs.relax())
}
pub const fn square(&self) -> Self {
self.relax().square()
}
const fn sqn(&self, n: usize) -> Self {
let mut x = self.square();
let mut i = 1;
while i < n {
x = x.square();
i += 1;
}
x
}
pub const fn pow_vartime(&self, exp: &[u64]) -> Self {
let mut res = Self::ONE;
let mut i = exp.len();
while i > 0 {
i -= 1;
let mut j = 64;
while j > 0 {
j -= 1;
res = res.square();
if ((exp[i] >> j) & 1) == 1 {
res = Self::multiply(&res, self);
}
}
}
res
}
pub fn invert(&self) -> CtOption<Self> {
CtOption::new(self.invert_unchecked(), !self.is_zero())
}
const fn invert_unchecked(&self) -> Self {
let z = self.square();
let z = self.multiply(&z);
let t0 = z.sqn(2);
let z = z.multiply(&t0);
let t0 = z.sqn(4);
let z = z.multiply(&t0);
let t0 = z.sqn(8);
let z = z.multiply(&t0);
let t0 = z.sqn(16);
let z = z.multiply(&t0);
let t0 = z.sqn(32);
let z = z.multiply(&t0);
let t0 = z.square();
let t0 = self.multiply(&t0);
let t0 = t0.sqn(64);
let z = z.multiply(&t0);
let t0 = z.square();
let t0 = self.multiply(&t0);
let t0 = t0.sqn(129);
let z = z.multiply(&t0);
let t0 = z.square();
let t0 = self.multiply(&t0);
let t0 = t0.sqn(259);
let z = z.multiply(&t0);
let z = z.sqn(2);
self.multiply(&z)
}
pub fn sqrt(&self) -> CtOption<Self> {
let sqrt = self.sqn(519);
CtOption::new(sqrt, sqrt.square().ct_eq(self))
}
pub(crate) const fn relax(&self) -> LooseFieldElement {
LooseFieldElement(fiat_p521_relax(&self.0))
}
}
impl AsRef<fiat_p521_tight_field_element> for FieldElement {
fn as_ref(&self) -> &fiat_p521_tight_field_element {
&self.0
}
}
impl Default for FieldElement {
fn default() -> Self {
Self::ZERO
}
}
impl Debug for FieldElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut bytes = fiat_p521_to_bytes(&self.0);
bytes.reverse();
let formatter = base16ct::HexDisplay(&bytes);
f.debug_tuple("FieldElement")
.field(&format_args!("0x{formatter:X}"))
.finish()
}
}
impl Eq for FieldElement {}
impl PartialEq for FieldElement {
fn eq(&self, rhs: &Self) -> bool {
self.ct_eq(rhs).into()
}
}
impl From<u32> for FieldElement {
fn from(n: u32) -> FieldElement {
Self::from_uint_unchecked(U576::from(n))
}
}
impl From<u64> for FieldElement {
fn from(n: u64) -> FieldElement {
Self::from_uint_unchecked(U576::from(n))
}
}
impl From<u128> for FieldElement {
fn from(n: u128) -> FieldElement {
Self::from_uint_unchecked(U576::from(n))
}
}
impl ConditionallySelectable for FieldElement {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut ret = Self::ZERO;
for i in 0..ret.0.len() {
ret.0[i] = u64::conditional_select(&a.0[i], &b.0[i], choice);
}
ret
}
}
impl ConstantTimeEq for FieldElement {
fn ct_eq(&self, other: &Self) -> Choice {
let a = fiat_p521_to_bytes(&self.0);
let b = fiat_p521_to_bytes(&other.0);
a.ct_eq(&b)
}
}
impl DefaultIsZeroes for FieldElement {}
impl Field for FieldElement {
const ZERO: Self = Self::ZERO;
const ONE: Self = Self::ONE;
fn random(mut rng: impl RngCore) -> Self {
let mut bytes = <FieldBytes>::default();
loop {
rng.fill_bytes(&mut bytes);
if let Some(fe) = Self::from_bytes(&bytes).into() {
return fe;
}
}
}
fn is_zero(&self) -> Choice {
Self::ZERO.ct_eq(self)
}
#[must_use]
fn square(&self) -> Self {
self.square()
}
#[must_use]
fn double(&self) -> Self {
self.double()
}
fn invert(&self) -> CtOption<Self> {
self.invert()
}
fn sqrt(&self) -> CtOption<Self> {
self.sqrt()
}
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
ff::helpers::sqrt_ratio_generic(num, div)
}
}
impl PrimeField for FieldElement {
type Repr = FieldBytes;
const MODULUS: &'static str = MODULUS_HEX;
const NUM_BITS: u32 = 521;
const CAPACITY: u32 = 520;
const TWO_INV: Self = Self::from_u64(2).invert_unchecked();
const MULTIPLICATIVE_GENERATOR: Self = Self::from_u64(3);
const S: u32 = 1;
const ROOT_OF_UNITY: Self = Self::from_hex("00000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
const ROOT_OF_UNITY_INV: Self = Self::ROOT_OF_UNITY.invert_unchecked();
const DELTA: Self = Self::from_u64(9);
#[inline]
fn from_repr(bytes: FieldBytes) -> CtOption<Self> {
Self::from_bytes(&bytes)
}
#[inline]
fn to_repr(&self) -> FieldBytes {
self.to_bytes()
}
#[inline]
fn is_odd(&self) -> Choice {
self.is_odd()
}
}
impl Add for FieldElement {
type Output = FieldElement;
#[inline]
fn add(self, rhs: FieldElement) -> FieldElement {
Self::add(&self, &rhs)
}
}
impl Add<&FieldElement> for FieldElement {
type Output = FieldElement;
#[inline]
fn add(self, rhs: &FieldElement) -> FieldElement {
Self::add(&self, rhs)
}
}
impl Add<&FieldElement> for &FieldElement {
type Output = FieldElement;
#[inline]
fn add(self, rhs: &FieldElement) -> FieldElement {
FieldElement::add(self, rhs)
}
}
impl AddAssign<FieldElement> for FieldElement {
#[inline]
fn add_assign(&mut self, other: FieldElement) {
*self = *self + other;
}
}
impl AddAssign<&FieldElement> for FieldElement {
#[inline]
fn add_assign(&mut self, other: &FieldElement) {
*self = *self + other;
}
}
impl Sub for FieldElement {
type Output = FieldElement;
#[inline]
fn sub(self, rhs: FieldElement) -> FieldElement {
Self::sub(&self, &rhs)
}
}
impl Sub<&FieldElement> for FieldElement {
type Output = FieldElement;
#[inline]
fn sub(self, rhs: &FieldElement) -> FieldElement {
Self::sub(&self, rhs)
}
}
impl Sub<&FieldElement> for &FieldElement {
type Output = FieldElement;
#[inline]
fn sub(self, rhs: &FieldElement) -> FieldElement {
FieldElement::sub(self, rhs)
}
}
impl SubAssign<FieldElement> for FieldElement {
#[inline]
fn sub_assign(&mut self, other: FieldElement) {
*self = *self - other;
}
}
impl SubAssign<&FieldElement> for FieldElement {
#[inline]
fn sub_assign(&mut self, other: &FieldElement) {
*self = *self - other;
}
}
impl Mul for FieldElement {
type Output = FieldElement;
#[inline]
fn mul(self, rhs: FieldElement) -> FieldElement {
self.relax().mul(&rhs.relax())
}
}
impl Mul<&FieldElement> for FieldElement {
type Output = FieldElement;
#[inline]
fn mul(self, rhs: &FieldElement) -> FieldElement {
self.relax().mul(&rhs.relax())
}
}
impl Mul<&FieldElement> for &FieldElement {
type Output = FieldElement;
#[inline]
fn mul(self, rhs: &FieldElement) -> FieldElement {
self.relax().mul(&rhs.relax())
}
}
impl MulAssign<&FieldElement> for FieldElement {
#[inline]
fn mul_assign(&mut self, other: &FieldElement) {
*self = *self * other;
}
}
impl MulAssign for FieldElement {
#[inline]
fn mul_assign(&mut self, other: FieldElement) {
*self = *self * other;
}
}
impl Neg for FieldElement {
type Output = FieldElement;
#[inline]
fn neg(self) -> FieldElement {
Self::neg(&self)
}
}
impl Sum for FieldElement {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(core::ops::Add::add).unwrap_or(Self::ZERO)
}
}
impl<'a> Sum<&'a FieldElement> for FieldElement {
fn sum<I: Iterator<Item = &'a FieldElement>>(iter: I) -> Self {
iter.copied().sum()
}
}
impl Product for FieldElement {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(core::ops::Mul::mul).unwrap_or(Self::ONE)
}
}
impl<'a> Product<&'a FieldElement> for FieldElement {
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.copied().product()
}
}
#[cfg(test)]
mod tests {
use super::FieldElement;
use elliptic_curve::ff::PrimeField;
use hex_literal::hex;
use primeorder::{
impl_field_identity_tests, impl_field_invert_tests, impl_field_sqrt_tests,
impl_primefield_tests,
};
const T: [u64; 9] = [
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0x00000000000000ff,
];
impl_field_identity_tests!(FieldElement);
impl_field_invert_tests!(FieldElement);
impl_field_sqrt_tests!(FieldElement);
impl_primefield_tests!(FieldElement, T);
#[test]
fn decode_invalid_field_element_returns_err() {
let overflowing_bytes = hex!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
let ct_option = FieldElement::from_bytes(overflowing_bytes.as_ref().into());
assert!(bool::from(ct_option.is_none()));
}
}