use crate::{FieldBytes, NistP384, ORDER_HEX, U384};
use elliptic_curve::{
Curve as _,
bigint::{ArrayEncoding, Limb, cpubits},
ff::PrimeField,
ops::{Reduce, ReduceNonZero},
scalar::{FromUintUnchecked, IsHigh},
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, CtOption},
};
cpubits! {
32 => {
#[cfg(not(p384_backend = "bignum"))]
use fiat_crypto::p384_scalar_32::*;
}
64 => {
#[cfg(not(p384_backend = "bignum"))]
use fiat_crypto::p384_scalar_64::*;
}
}
#[cfg(feature = "serde")]
use {
elliptic_curve::ScalarValue,
serdect::serde::{Deserialize, Serialize, de, ser},
};
#[cfg(doc)]
use core::ops::{Add, Mul, Neg, Sub};
primefield::monty_field_params! {
name: ScalarParams,
modulus: ORDER_HEX,
uint: U384,
byte_order: primefield::ByteOrder::BigEndian,
multiplicative_generator: 2,
doc: "Montgomery parameters for the NIST P-384 scalar modulus `n`."
}
primefield::monty_field_element! {
name: Scalar,
params: ScalarParams,
uint: U384,
doc: "Element in the NIST P-384 scalar field modulo `n`."
}
#[cfg(p384_backend = "bignum")]
primefield::monty_field_arithmetic! {
name: Scalar,
params: ScalarParams,
uint: U384
}
#[cfg(not(p384_backend = "bignum"))]
primefield::fiat_monty_field_arithmetic! {
name: Scalar,
params: ScalarParams,
uint: U384,
non_mont: fiat_p384_scalar_non_montgomery_domain_field_element,
mont: fiat_p384_scalar_montgomery_domain_field_element,
from_mont: fiat_p384_scalar_from_montgomery,
to_mont: fiat_p384_scalar_to_montgomery,
add: fiat_p384_scalar_add,
sub: fiat_p384_scalar_sub,
mul: fiat_p384_scalar_mul,
neg: fiat_p384_scalar_opp,
square: fiat_p384_scalar_square,
divstep_precomp: fiat_p384_scalar_divstep_precomp,
divstep: fiat_p384_scalar_divstep,
msat: fiat_p384_scalar_msat,
selectnz: fiat_p384_scalar_selectznz
}
elliptic_curve::scalar_impls!(NistP384, Scalar);
impl AsRef<Scalar> for Scalar {
fn as_ref(&self) -> &Scalar {
self
}
}
impl FromUintUnchecked for Scalar {
type Uint = U384;
fn from_uint_unchecked(uint: Self::Uint) -> Self {
Self::from_uint_unchecked(uint)
}
}
impl IsHigh for Scalar {
fn is_high(&self) -> Choice {
const MODULUS_SHR1: U384 = NistP384::ORDER.as_ref().shr_vartime(1);
self.to_canonical().ct_gt(&MODULUS_SHR1)
}
}
impl Reduce<U384> for Scalar {
fn reduce(w: &U384) -> Self {
let (r, underflow) = w.borrowing_sub(&NistP384::ORDER, Limb::ZERO);
let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8);
Self::from_uint_unchecked(U384::conditional_select(w, &r, !underflow))
}
}
impl Reduce<FieldBytes> for Scalar {
#[inline]
fn reduce(bytes: &FieldBytes) -> Self {
Self::reduce(&U384::from_be_byte_array(*bytes))
}
}
impl ReduceNonZero<U384> for Scalar {
fn reduce_nonzero(w: &U384) -> Self {
const ORDER_MINUS_ONE: U384 = NistP384::ORDER.as_ref().wrapping_sub(&U384::ONE);
let (r, underflow) = w.borrowing_sub(&ORDER_MINUS_ONE, Limb::ZERO);
let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8);
Self::from_uint_unchecked(
U384::conditional_select(w, &r, !underflow).wrapping_add(&U384::ONE),
)
}
}
impl ReduceNonZero<FieldBytes> for Scalar {
#[inline]
fn reduce_nonzero(bytes: &FieldBytes) -> Self {
Self::reduce_nonzero(&U384::from_be_byte_array(*bytes))
}
}
#[cfg(feature = "serde")]
impl Serialize for Scalar {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
ScalarValue::from(self).serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Scalar {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
Ok(ScalarValue::deserialize(deserializer)?.into())
}
}
#[cfg(test)]
mod tests {
use super::{Scalar, U384};
#[cfg(not(p384_backend = "bignum"))]
use super::{
ScalarParams, fiat_p384_scalar_montgomery_domain_field_element, fiat_p384_scalar_msat,
fiat_p384_scalar_non_montgomery_domain_field_element, fiat_p384_scalar_to_montgomery,
};
use crate::{FieldBytes, NistP384, NonZeroScalar};
use elliptic_curve::{
Curve,
array::Array,
ff::PrimeField,
ops::{BatchInvert, ReduceNonZero},
};
use proptest::{prelude::any, prop_compose, proptest};
primefield::test_primefield!(Scalar, U384);
#[cfg(not(p384_backend = "bignum"))]
primefield::test_fiat_monty_field_arithmetic!(
name: Scalar,
params: ScalarParams,
uint: U384,
non_mont: fiat_p384_scalar_non_montgomery_domain_field_element,
mont: fiat_p384_scalar_montgomery_domain_field_element,
to_mont: fiat_p384_scalar_to_montgomery,
msat: fiat_p384_scalar_msat
);
#[test]
fn from_to_bytes_roundtrip() {
let k: u64 = 42;
let mut bytes = FieldBytes::default();
bytes[40..].copy_from_slice(k.to_be_bytes().as_ref());
let scalar = Scalar::from_repr(bytes).unwrap();
assert_eq!(bytes, scalar.to_bytes());
}
#[test]
fn multiply() {
let one = Scalar::ONE;
let two = one + one;
let three = two + one;
let six = three + three;
assert_eq!(six, two * three);
let minus_two = -two;
let minus_three = -three;
assert_eq!(two, -minus_two);
assert_eq!(minus_three * minus_two, minus_two * minus_three);
assert_eq!(six, minus_two * minus_three);
}
#[test]
fn reduce_nonzero() {
assert_eq!(
Scalar::reduce_nonzero(&Array::default()).to_canonical(),
U384::ONE
);
assert_eq!(
Scalar::reduce_nonzero(&U384::ONE).to_canonical(),
U384::from_u8(2)
);
assert_eq!(
Scalar::reduce_nonzero(&U384::from_u8(2)).to_canonical(),
U384::from_u8(3),
);
assert_eq!(
Scalar::reduce_nonzero(NistP384::ORDER.as_ref()).to_canonical(),
U384::from_u8(2),
);
assert_eq!(
Scalar::reduce_nonzero(&NistP384::ORDER.wrapping_sub(&U384::from_u8(1))).to_canonical(),
U384::ONE,
);
assert_eq!(
Scalar::reduce_nonzero(&NistP384::ORDER.wrapping_sub(&U384::from_u8(2))).to_canonical(),
NistP384::ORDER.wrapping_sub(&U384::ONE),
);
assert_eq!(
Scalar::reduce_nonzero(&NistP384::ORDER.wrapping_sub(&U384::from_u8(3))).to_canonical(),
NistP384::ORDER.wrapping_sub(&U384::from_u8(2)),
);
assert_eq!(
Scalar::reduce_nonzero(&NistP384::ORDER.wrapping_add(&U384::ONE)).to_canonical(),
U384::from_u8(3),
);
assert_eq!(
Scalar::reduce_nonzero(&NistP384::ORDER.wrapping_add(&U384::from_u8(2))).to_canonical(),
U384::from_u8(4),
);
}
prop_compose! {
fn non_zero_scalar()(bytes in any::<[u8; 48]>()) -> NonZeroScalar {
NonZeroScalar::reduce_nonzero(&FieldBytes::from(bytes))
}
}
proptest! {
#[test]
fn batch_invert(
a in non_zero_scalar(),
b in non_zero_scalar(),
c in non_zero_scalar(),
d in non_zero_scalar(),
e in non_zero_scalar(),
) {
let scalars: [Scalar; 5] = [*a, *b, *c, *d, *e];
let inverted_scalars = Scalar::batch_invert(scalars).unwrap();
for (scalar, inverted_scalar) in scalars.into_iter().zip(inverted_scalars) {
assert_eq!(inverted_scalar, scalar.invert().unwrap());
}
}
}
}