#![allow(clippy::assign_op_pattern, clippy::op_ref)]
use elliptic_curve::{bigint::cpubits, ops::BatchInvert};
cpubits! {
32 => {
#[path = "field/field32.rs"]
mod field_impl;
}
64 => {
#[path = "field/field64.rs"]
mod field_impl;
}
}
use core::ops::Mul;
use elliptic_curve::{
bigint::U256,
ff::PrimeField,
subtle::{Choice, ConstantTimeEq, CtOption},
};
#[cfg(doc)]
use {
core::ops::{Add, Neg, Sub},
elliptic_curve::{
ff::{self, Field},
subtle::ConditionallySelectable,
},
};
const MODULUS_HEX: &str = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff";
primefield::monty_field_params!(
name: FieldParams,
modulus: MODULUS_HEX,
uint: U256,
byte_order: primefield::ByteOrder::BigEndian,
multiplicative_generator: 6,
doc: "Montgomery parameters for the NIST P-256 field modulus: p = 2^{224}(2^{32} − 1) + 2^{192} + 2^{96} − 1."
);
primefield::monty_field_element!(
name: FieldElement,
params: FieldParams,
uint: U256,
doc: "Element in the finite field modulo p = 2^{224}(2^{32} − 1) + 2^{192} + 2^{96} − 1."
);
impl FieldElement {
pub(crate) const fn from_uint_unchecked(w: U256) -> Self {
Self::multiply(
&Self::from_montgomery(w),
&Self::from_montgomery(*FieldParams::PARAMS.r2()),
)
}
pub const fn add(&self, rhs: &Self) -> Self {
Self::from_montgomery(field_impl::add(
self.0.as_montgomery(),
rhs.0.as_montgomery(),
))
}
pub const fn double(&self) -> Self {
self.add(self)
}
pub const fn sub(&self, rhs: &Self) -> Self {
Self::from_montgomery(field_impl::sub(
self.0.as_montgomery(),
rhs.0.as_montgomery(),
))
}
pub const fn neg(&self) -> Self {
Self::sub(&Self::ZERO, self)
}
#[inline]
pub(crate) const fn to_canonical(self) -> U256 {
field_impl::to_canonical(self.0.as_montgomery())
}
pub const fn multiply(&self, rhs: &Self) -> Self {
let (lo, hi): (U256, U256) = self.0.as_montgomery().widening_mul(rhs.0.as_montgomery());
Self::from_montgomery(field_impl::montgomery_reduce(&lo, &hi))
}
pub const fn square(&self) -> Self {
self.multiply(self)
}
pub fn invert(&self) -> CtOption<Self> {
self.0.invert().map(Self)
}
pub fn invert_vartime(&self) -> CtOption<Self> {
self.0.invert_vartime().map(Self)
}
pub fn sqrt(&self) -> CtOption<Self> {
let t11 = self.mul(&self.square());
let t1111 = t11.mul(&t11.sqn(2));
let t11111111 = t1111.mul(t1111.sqn(4));
let x16 = t11111111.sqn(8).mul(t11111111);
let sqrt = x16
.sqn(16)
.mul(x16)
.sqn(32)
.mul(self)
.sqn(96)
.mul(self)
.sqn(94);
CtOption::new(
sqrt,
(&sqrt * &sqrt).ct_eq(self), )
}
const fn sqn(&self, n: usize) -> Self {
Self(self.0.sqn_vartime(n))
}
#[inline]
pub(crate) const fn from_montgomery(uint: U256) -> Self {
Self(primefield::MontyFieldElement::<
FieldParams,
{ FieldParams::LIMBS },
>::from_montgomery(uint))
}
}
impl BatchInvert for FieldElement {}
#[cfg(test)]
mod tests {
use super::{FieldElement, FieldParams, cpubits};
use crate::{FieldBytes, U256, test_vectors::field::DBL_TEST_VECTORS};
use elliptic_curve::{array::Array, bigint::modular::ConstMontyParams};
cpubits! {
64 => { use proptest::{num::u64::ANY, prelude::*}; }
}
primefield::test_primefield!(FieldElement, U256);
#[test]
fn r2() {
let expected_r2 =
U256::from_be_hex("00000004fffffffdfffffffffffffffefffffffbffffffff0000000000000003");
assert_eq!(FieldParams::PARAMS.r2(), &expected_r2);
}
#[test]
fn from_bytes() {
assert_eq!(
FieldElement::from_bytes(&FieldBytes::default()).unwrap(),
FieldElement::ZERO
);
assert_eq!(
FieldElement::from_bytes(&Array([
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, 1
]))
.unwrap(),
FieldElement::ONE
);
assert!(bool::from(
FieldElement::from_bytes(&Array([0xff; 32])).is_none()
));
}
#[test]
fn to_bytes() {
assert_eq!(FieldElement::ZERO.to_bytes(), FieldBytes::default());
assert_eq!(
FieldElement::ONE.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, 1
]
);
}
#[test]
fn repeated_add() {
let mut r = FieldElement::ONE;
for item in DBL_TEST_VECTORS {
assert_eq!(r.to_bytes().as_slice(), item);
r = r + &r;
}
}
#[test]
fn repeated_double() {
let mut r = FieldElement::ONE;
for item in DBL_TEST_VECTORS {
assert_eq!(r.to_bytes().as_slice(), item);
r = r.double();
}
}
#[test]
fn repeated_mul() {
let mut r = FieldElement::ONE;
let two = r + &r;
for item in DBL_TEST_VECTORS {
assert_eq!(r.to_bytes().as_slice(), item);
r = r * &two;
}
}
#[test]
fn negation() {
let two = FieldElement::ONE.double();
let neg_two = -two;
assert_eq!(two + &neg_two, FieldElement::ZERO);
assert_eq!(-neg_two, two);
}
#[test]
fn pow_vartime() {
let one = FieldElement::ONE;
let two = one + &one;
let four = two.square();
assert_eq!(two.pow_vartime(&U256::from_u64(2)), four);
}
cpubits! {
64 => {
proptest! {
#[test]
fn add_then_sub(
a0 in ANY,
a1 in ANY,
a2 in ANY,
b0 in ANY,
b1 in ANY,
b2 in ANY,
) {
let a = FieldElement::from_montgomery(U256::from_words([a0, a1, a2, 0]));
let b = FieldElement::from_montgomery(U256::from_words([b0, b1, b2, 0]));
assert_eq!(a.add(&b).sub(&a), b);
}
}
}
}
}