use crate::{FieldBytes, NonZeroScalar, ORDER, ORDER_HEX, Secp256k1, WideBytes};
use core::iter::{Product, Sum};
use elliptic_curve::{
Curve, Error, Generate, ScalarValue,
bigint::{ArrayEncoding, Limb, U256, U512, Word, cpubits, modular::Retrieve},
ctutils,
ff::{self, Field, FromUniformBytes, PrimeField},
ops::{
Add, AddAssign, Invert, Mul, MulAssign, Neg, Reduce, ReduceNonZero, Shr, ShrAssign, Sub,
SubAssign,
},
rand_core::{CryptoRng, TryCryptoRng, TryRng},
scalar::{FromUintUnchecked, IsHigh},
subtle::{
Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess,
CtOption,
},
zeroize::DefaultIsZeroes,
};
use primeorder::{FieldExt, PrimeFieldExt};
cpubits! {
32 => {
#[path = "scalar/wide32.rs"]
mod wide;
}
64 => {
#[path = "scalar/wide64.rs"]
mod wide;
}
}
pub(crate) use self::wide::WideScalar;
#[cfg(feature = "serde")]
use serdect::serde::{Deserialize, Serialize, de, ser};
#[cfg(test)]
use num_bigint::{BigUint, ToBigUint};
const MODULUS: [Word; U256::LIMBS] = ORDER.as_ref().to_words();
const FRAC_MODULUS_2: U256 = ORDER.as_ref().shr_vartime(1);
#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord)]
pub struct Scalar(pub(crate) U256);
impl Scalar {
pub const ZERO: Self = Self(U256::ZERO);
pub const ONE: Self = Self(U256::ONE);
#[must_use]
pub fn is_zero(&self) -> Choice {
self.0.is_zero().into()
}
#[must_use]
pub fn to_bytes(&self) -> FieldBytes {
self.0.to_be_byte_array()
}
#[must_use]
pub const fn negate(&self) -> Self {
Self(self.0.neg_mod(ORDER.as_nz_ref()))
}
#[must_use]
pub const fn add(&self, rhs: &Self) -> Self {
Self(self.0.add_mod(&rhs.0, ORDER.as_nz_ref()))
}
#[must_use]
pub const fn sub(&self, rhs: &Self) -> Self {
Self(self.0.sub_mod(&rhs.0, ORDER.as_nz_ref()))
}
#[must_use]
pub fn mul(&self, rhs: &Scalar) -> Scalar {
WideScalar::mul_wide(self, rhs).reduce()
}
#[must_use]
pub fn square(&self) -> Self {
self.mul(self)
}
#[must_use]
pub fn shr_vartime(&self, shift: u32) -> Scalar {
Self(self.0.unbounded_shr_vartime(shift))
}
pub fn invert(&self) -> CtOption<Self> {
let inv = self.retrieve().invert_odd_mod(&ORDER);
CtOption::from(inv).map(Self::from_uint_unchecked)
}
pub fn invert_vartime(&self) -> CtOption<Self> {
let inv = self.retrieve().invert_odd_mod_vartime(&ORDER);
CtOption::from(inv).map(Self::from_uint_unchecked)
}
#[cfg(test)]
#[must_use]
#[allow(clippy::missing_panics_doc, reason = "test")]
pub fn modulus_as_biguint() -> BigUint {
Self::ONE.negate().to_biguint().unwrap() + 1.to_biguint().unwrap()
}
pub fn generate_biased_from_rng<R: CryptoRng + ?Sized>(rng: &mut R) -> Self {
let Ok(scalar) = Self::try_generate_biased_from_rng(rng);
scalar
}
pub fn try_generate_biased_from_rng<R: TryCryptoRng + ?Sized>(
rng: &mut R,
) -> Result<Self, R::Error> {
let mut buf = [0u8; 64];
rng.try_fill_bytes(&mut buf)?;
Ok(WideScalar::from_bytes(&buf).reduce())
}
pub(crate) const fn from_bytes_unchecked(bytes: &[u8; 32]) -> Self {
Self(U256::from_be_slice(bytes))
}
}
impl AsRef<Scalar> for Scalar {
fn as_ref(&self) -> &Scalar {
self
}
}
impl DefaultIsZeroes for Scalar {}
impl Field for Scalar {
const ZERO: Self = Self::ZERO;
const ONE: Self = Self::ONE;
fn try_random<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
let mut bytes = FieldBytes::default();
loop {
rng.try_fill_bytes(&mut bytes)?;
if let Some(scalar) = Scalar::from_repr(bytes).into() {
return Ok(scalar);
}
}
}
fn square(&self) -> Self {
Scalar::square(self)
}
fn double(&self) -> Self {
self.add(self)
}
fn invert(&self) -> CtOption<Self> {
Scalar::invert(self)
}
#[allow(clippy::many_single_char_names)]
fn sqrt(&self) -> CtOption<Self> {
let w = self.pow_vartime([
0x777fa4bd19a06c82,
0xfd755db9cd5e9140,
0xffffffffffffffff,
0x1ffffffffffffff,
]);
let mut v = Self::S;
let mut x = *self * w;
let mut b = x * w;
let mut z = Self::ROOT_OF_UNITY;
for max_v in (1..=Self::S).rev() {
let mut k = 1;
let mut tmp = b.square();
let mut j_less_than_v = Choice::from(1);
for j in 2..max_v {
let tmp_is_one = tmp.ct_eq(&Self::ONE);
let squared = Self::conditional_select(&tmp, &z, tmp_is_one).square();
tmp = Self::conditional_select(&squared, &tmp, tmp_is_one);
let new_z = Self::conditional_select(&z, &squared, tmp_is_one);
j_less_than_v &= !ConstantTimeEq::ct_eq(&j, &v);
k = u32::conditional_select(&j, &k, tmp_is_one);
z = Self::conditional_select(&z, &new_z, j_less_than_v);
}
let result = x * z;
x = Self::conditional_select(&result, &x, b.ct_eq(&Self::ONE));
z = z.square();
b *= z;
v = k;
}
CtOption::new(x, x.square().ct_eq(self))
}
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
ff::helpers::sqrt_ratio_generic(num, div)
}
}
impl Generate for Scalar {
fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
Self::try_random(rng)
}
}
impl PrimeField for Scalar {
type Repr = FieldBytes;
const MODULUS: &'static str = ORDER_HEX;
const NUM_BITS: u32 = 256;
const CAPACITY: u32 = 255;
const TWO_INV: Self = Self(U256::from_be_hex(
"7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1",
));
const MULTIPLICATIVE_GENERATOR: Self = Self(U256::from_u8(7));
const S: u32 = 6;
const ROOT_OF_UNITY: Self = Self(U256::from_be_hex(
"0c1dc060e7a91986df9879a3fbc483a898bdeab680756045992f4b5402b052f2",
));
const ROOT_OF_UNITY_INV: Self = Self(U256::from_be_hex(
"fd3ae181f12d7096efc7b0c75b8cbb7277a275910aa413c3b6fb30a0884f0d1c",
));
const DELTA: Self = Self(U256::from_be_hex(
"0000000000000000000cbc21fe4561c8d63b78e780e1341e199417c8c0bb7601",
));
fn from_repr(bytes: FieldBytes) -> CtOption<Self> {
let inner = U256::from_be_byte_array(bytes);
CtOption::new(
Self(inner),
ConstantTimeLess::ct_lt(&inner, &Secp256k1::ORDER),
)
}
fn to_repr(&self) -> FieldBytes {
self.to_bytes()
}
fn is_odd(&self) -> Choice {
self.0.is_odd().into()
}
}
impl FieldExt for Scalar {}
impl PrimeFieldExt for Scalar {}
impl From<u32> for Scalar {
fn from(k: u32) -> Self {
Self(k.into())
}
}
impl From<u64> for Scalar {
fn from(k: u64) -> Self {
Self(k.into())
}
}
impl From<u128> for Scalar {
fn from(k: u128) -> Self {
Self(k.into())
}
}
impl FromUniformBytes<64> for Scalar {
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
WideScalar::from_bytes(bytes).reduce()
}
}
impl From<NonZeroScalar> for Scalar {
fn from(scalar: NonZeroScalar) -> Self {
*scalar.as_ref()
}
}
impl From<&NonZeroScalar> for Scalar {
fn from(scalar: &NonZeroScalar) -> Self {
*scalar.as_ref()
}
}
impl From<ScalarValue<Secp256k1>> for Scalar {
fn from(scalar: ScalarValue<Secp256k1>) -> Scalar {
Scalar(*scalar.as_uint())
}
}
impl From<&ScalarValue<Secp256k1>> for Scalar {
fn from(scalar: &ScalarValue<Secp256k1>) -> Scalar {
Scalar(*scalar.as_uint())
}
}
impl From<Scalar> for ScalarValue<Secp256k1> {
fn from(scalar: Scalar) -> ScalarValue<Secp256k1> {
ScalarValue::from(&scalar)
}
}
impl From<&Scalar> for ScalarValue<Secp256k1> {
fn from(scalar: &Scalar) -> ScalarValue<Secp256k1> {
ScalarValue::new(scalar.0).unwrap()
}
}
impl TryFrom<Scalar> for NonZeroScalar {
type Error = Error;
fn try_from(scalar: Scalar) -> Result<Self, Error> {
NonZeroScalar::new(scalar).into_option().ok_or(Error)
}
}
impl FromUintUnchecked for Scalar {
type Uint = U256;
fn from_uint_unchecked(uint: Self::Uint) -> Self {
Self(uint)
}
}
impl Invert for Scalar {
type Output = CtOption<Self>;
fn invert(&self) -> CtOption<Self> {
Scalar::invert(self)
}
fn invert_vartime(&self) -> CtOption<Self> {
Scalar::invert_vartime(self)
}
}
impl IsHigh for Scalar {
fn is_high(&self) -> Choice {
ConstantTimeGreater::ct_gt(&self.0, &FRAC_MODULUS_2)
}
}
impl Shr<usize> for Scalar {
type Output = Self;
fn shr(self, rhs: usize) -> Self::Output {
#[allow(clippy::cast_possible_truncation)]
self.shr_vartime(rhs as u32)
}
}
impl Shr<usize> for &Scalar {
type Output = Scalar;
fn shr(self, rhs: usize) -> Self::Output {
#[allow(clippy::cast_possible_truncation)]
self.shr_vartime(rhs as u32)
}
}
impl ShrAssign<usize> for Scalar {
fn shr_assign(&mut self, rhs: usize) {
*self = *self >> rhs;
}
}
impl ConditionallySelectable for Scalar {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self(U256::conditional_select(&a.0, &b.0, choice))
}
}
impl ConstantTimeEq for Scalar {
fn ct_eq(&self, other: &Self) -> Choice {
ConstantTimeEq::ct_eq(&self.0, &other.0)
}
}
impl ctutils::CtEq for Scalar {
fn ct_eq(&self, other: &Self) -> ctutils::Choice {
ConstantTimeEq::ct_eq(self, other).into()
}
}
impl ctutils::CtSelect for Scalar {
fn ct_select(&self, other: &Self, choice: ctutils::Choice) -> Self {
ConditionallySelectable::conditional_select(self, other, choice.into())
}
}
impl PartialEq for Scalar {
fn eq(&self, other: &Self) -> bool {
ConstantTimeEq::ct_eq(self, other).into()
}
}
impl Eq for Scalar {}
impl Neg for Scalar {
type Output = Scalar;
fn neg(self) -> Scalar {
self.negate()
}
}
impl Neg for &Scalar {
type Output = Scalar;
fn neg(self) -> Scalar {
self.negate()
}
}
impl Add<Scalar> for Scalar {
type Output = Scalar;
fn add(self, other: Scalar) -> Scalar {
Scalar::add(&self, &other)
}
}
impl Add<&Scalar> for &Scalar {
type Output = Scalar;
fn add(self, other: &Scalar) -> Scalar {
Scalar::add(self, other)
}
}
impl Add<Scalar> for &Scalar {
type Output = Scalar;
fn add(self, other: Scalar) -> Scalar {
Scalar::add(self, &other)
}
}
impl Add<&Scalar> for Scalar {
type Output = Scalar;
fn add(self, other: &Scalar) -> Scalar {
Scalar::add(&self, other)
}
}
impl AddAssign<Scalar> for Scalar {
#[inline]
fn add_assign(&mut self, rhs: Scalar) {
*self = Scalar::add(self, &rhs);
}
}
impl AddAssign<&Scalar> for Scalar {
fn add_assign(&mut self, rhs: &Scalar) {
*self = Scalar::add(self, rhs);
}
}
impl Sub<Scalar> for Scalar {
type Output = Scalar;
fn sub(self, other: Scalar) -> Scalar {
Scalar::sub(&self, &other)
}
}
impl Sub<&Scalar> for &Scalar {
type Output = Scalar;
fn sub(self, other: &Scalar) -> Scalar {
Scalar::sub(self, other)
}
}
impl Sub<&Scalar> for Scalar {
type Output = Scalar;
fn sub(self, other: &Scalar) -> Scalar {
Scalar::sub(&self, other)
}
}
impl SubAssign<Scalar> for Scalar {
fn sub_assign(&mut self, rhs: Scalar) {
*self = Scalar::sub(self, &rhs);
}
}
impl SubAssign<&Scalar> for Scalar {
fn sub_assign(&mut self, rhs: &Scalar) {
*self = Scalar::sub(self, rhs);
}
}
impl Mul<Scalar> for Scalar {
type Output = Scalar;
fn mul(self, other: Scalar) -> Scalar {
Scalar::mul(&self, &other)
}
}
impl Mul<&Scalar> for &Scalar {
type Output = Scalar;
fn mul(self, other: &Scalar) -> Scalar {
Scalar::mul(self, other)
}
}
impl Mul<&Scalar> for Scalar {
type Output = Scalar;
fn mul(self, other: &Scalar) -> Scalar {
Scalar::mul(&self, other)
}
}
elliptic_curve::scalar_mul_impls!(Secp256k1, Scalar);
wnaf::impl_wnaf_size_for_scalar!(Scalar);
impl MulAssign<Scalar> for Scalar {
fn mul_assign(&mut self, rhs: Scalar) {
*self = Scalar::mul(self, &rhs);
}
}
impl MulAssign<&Scalar> for Scalar {
fn mul_assign(&mut self, rhs: &Scalar) {
*self = Scalar::mul(self, rhs);
}
}
impl Reduce<U256> for Scalar {
fn reduce(w: &U256) -> Self {
let (r, underflow) = w.borrowing_sub(&ORDER, Limb::ZERO);
let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8);
Self(U256::conditional_select(w, &r, !underflow))
}
}
impl Reduce<FieldBytes> for Scalar {
#[inline]
fn reduce(bytes: &FieldBytes) -> Self {
Self::reduce(&U256::from_be_byte_array(*bytes))
}
}
impl Reduce<U512> for Scalar {
fn reduce(w: &U512) -> Self {
WideScalar(*w).reduce()
}
}
impl Reduce<WideBytes> for Scalar {
fn reduce(bytes: &WideBytes) -> Self {
Self::reduce(&U512::from_be_byte_array(*bytes))
}
}
impl ReduceNonZero<U256> for Scalar {
fn reduce_nonzero(w: &U256) -> Self {
const ORDER_MINUS_ONE: U256 = ORDER.as_ref().wrapping_sub(&U256::ONE);
let (r, underflow) = w.borrowing_sub(&ORDER_MINUS_ONE, Limb::ZERO);
let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8);
Self(U256::conditional_select(w, &r, !underflow).wrapping_add(&U256::ONE))
}
}
impl ReduceNonZero<FieldBytes> for Scalar {
#[inline]
fn reduce_nonzero(bytes: &FieldBytes) -> Self {
Self::reduce_nonzero(&U256::from_be_byte_array(*bytes))
}
}
impl ReduceNonZero<U512> for Scalar {
fn reduce_nonzero(w: &U512) -> Self {
WideScalar(*w).reduce_nonzero()
}
}
impl ReduceNonZero<WideBytes> for Scalar {
#[inline]
fn reduce_nonzero(bytes: &WideBytes) -> Self {
Self::reduce_nonzero(&U512::from_be_byte_array(*bytes))
}
}
impl Retrieve for Scalar {
type Output = U256;
fn retrieve(&self) -> U256 {
self.0
}
}
impl Sum for Scalar {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(Add::add).unwrap_or(Self::ZERO)
}
}
impl<'a> Sum<&'a Scalar> for Scalar {
fn sum<I: Iterator<Item = &'a Scalar>>(iter: I) -> Self {
iter.copied().sum()
}
}
impl Product for Scalar {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(Mul::mul).unwrap_or(Self::ONE)
}
}
impl<'a> Product<&'a Scalar> for Scalar {
fn product<I: Iterator<Item = &'a Scalar>>(iter: I) -> Self {
iter.copied().product()
}
}
impl From<Scalar> for FieldBytes {
fn from(scalar: Scalar) -> Self {
scalar.to_bytes()
}
}
impl From<&Scalar> for FieldBytes {
fn from(scalar: &Scalar) -> Self {
scalar.to_bytes()
}
}
impl From<Scalar> for U256 {
fn from(scalar: Scalar) -> Self {
scalar.0
}
}
impl From<&Scalar> for U256 {
fn from(scalar: &Scalar) -> Self {
scalar.0
}
}
#[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;
use crate::{
FieldBytes, NonZeroScalar, ORDER, WideBytes,
arithmetic::dev::{biguint_to_bytes, bytes_to_biguint},
};
use elliptic_curve::{
array::Array,
bigint::{ArrayEncoding, U256, U512},
ff::{Field, PrimeField},
ops::Reduce,
scalar::IsHigh,
};
use num_bigint::{BigUint, ToBigUint};
use num_traits::Zero;
use proptest::prelude::*;
#[cfg(feature = "getrandom")]
use elliptic_curve::{Generate, common::getrandom::SysRng};
impl From<&BigUint> for Scalar {
fn from(x: &BigUint) -> Self {
debug_assert!(x < &Scalar::modulus_as_biguint());
let bytes = biguint_to_bytes(x);
Self::from_repr(bytes.into()).unwrap()
}
}
impl From<BigUint> for Scalar {
fn from(x: BigUint) -> Self {
Self::from(&x)
}
}
impl ToBigUint for Scalar {
fn to_biguint(&self) -> Option<BigUint> {
Some(bytes_to_biguint(self.to_bytes().as_ref()))
}
}
const T: [u64; 4] = [
0xeeff497a3340d905,
0xfaeabb739abd2280,
0xffffffffffffffff,
0x03ffffffffffffff,
];
#[test]
fn two_inv_constant() {
assert_eq!(Scalar::from(2u32) * Scalar::TWO_INV, Scalar::ONE);
}
#[test]
fn root_of_unity_constant() {
assert_eq!(
Scalar::ROOT_OF_UNITY.pow_vartime([1u64 << Scalar::S, 0, 0, 0]),
Scalar::ONE
);
assert_eq!(
Scalar::MULTIPLICATIVE_GENERATOR.pow_vartime(T),
Scalar::ROOT_OF_UNITY
);
}
#[test]
fn root_of_unity_inv_constant() {
assert_eq!(
Scalar::ROOT_OF_UNITY * Scalar::ROOT_OF_UNITY_INV,
Scalar::ONE
);
}
#[test]
fn delta_constant() {
assert_eq!(Scalar::DELTA.pow_vartime(T), Scalar::ONE);
}
#[test]
fn is_high() {
let high: bool = Scalar::ZERO.is_high().into();
assert!(!high);
let one = 1.to_biguint().unwrap();
let high: bool = Scalar::from(&one).is_high().into();
assert!(!high);
let m = Scalar::modulus_as_biguint();
let m_by_2 = &m >> 1;
let high: bool = Scalar::from(&m_by_2).is_high().into();
assert!(!high);
let high: bool = Scalar::from(&m_by_2 + &one).is_high().into();
assert!(high);
let high: bool = Scalar::from(&m - &one).is_high().into();
assert!(high);
}
#[test]
fn sqrt() {
for &n in &[1u64, 4, 9, 16, 25, 36, 49, 64] {
let scalar = Scalar::from(n);
let sqrt = scalar.sqrt().unwrap();
assert_eq!(sqrt.square(), scalar);
}
}
#[test]
fn invert() {
assert_eq!(Scalar::ONE, Scalar::ONE.invert().unwrap());
let three = Scalar::from(3u64);
let inv_three = three.invert().unwrap();
assert_eq!(three * inv_three, Scalar::ONE);
let minus_three = -three;
let inv_minus_three = minus_three.invert().unwrap();
assert_eq!(inv_minus_three, -inv_three);
assert_eq!(three * inv_minus_three, -Scalar::ONE);
assert!(bool::from(Scalar::ZERO.invert().is_none()));
assert_eq!(Scalar::from(2u64).invert().unwrap(), Scalar::TWO_INV);
assert_eq!(
Scalar::ROOT_OF_UNITY.invert_vartime().unwrap(),
Scalar::ROOT_OF_UNITY_INV
);
}
#[test]
fn invert_vartime() {
assert_eq!(Scalar::ONE, Scalar::ONE.invert_vartime().unwrap());
let three = Scalar::from(3u64);
let inv_three = three.invert_vartime().unwrap();
assert_eq!(three * inv_three, Scalar::ONE);
let minus_three = -three;
let inv_minus_three = minus_three.invert_vartime().unwrap();
assert_eq!(inv_minus_three, -inv_three);
assert_eq!(three * inv_minus_three, -Scalar::ONE);
assert!(bool::from(Scalar::ZERO.invert_vartime().is_none()));
assert_eq!(
Scalar::from(2u64).invert_vartime().unwrap(),
Scalar::TWO_INV
);
assert_eq!(
Scalar::ROOT_OF_UNITY.invert_vartime().unwrap(),
Scalar::ROOT_OF_UNITY_INV
);
}
#[test]
fn negate() {
let zero_neg = -Scalar::ZERO;
assert_eq!(zero_neg, Scalar::ZERO);
let m = Scalar::modulus_as_biguint();
let one = 1.to_biguint().unwrap();
let m_minus_one = &m - &one;
let m_by_2 = &m >> 1;
let one_neg = -Scalar::ONE;
assert_eq!(one_neg, Scalar::from(&m_minus_one));
let frac_modulus_2_neg = -Scalar::from(&m_by_2);
let frac_modulus_2_plus_one = Scalar::from(&m_by_2 + &one);
assert_eq!(frac_modulus_2_neg, frac_modulus_2_plus_one);
let modulus_minus_one_neg = -Scalar::from(&m - &one);
assert_eq!(modulus_minus_one_neg, Scalar::ONE);
}
#[test]
fn add_result_within_256_bits() {
let t = 1.to_biguint().unwrap() << 255;
let one = 1.to_biguint().unwrap();
let a = Scalar::from(&t - &one);
let b = Scalar::from(&t);
let res = a + b;
let m = Scalar::modulus_as_biguint();
let res_ref = Scalar::from((&t + &t - &one) % &m);
assert_eq!(res, res_ref);
}
#[cfg(feature = "getrandom")]
#[allow(clippy::op_ref)]
#[test]
fn try_generate_biased_from_rng() {
let a = Scalar::try_generate_biased_from_rng(&mut SysRng).unwrap();
assert_eq!((a - &a).is_zero().unwrap_u8(), 1);
}
#[cfg(feature = "getrandom")]
#[test]
fn try_generate_from_rng() {
let a = Scalar::try_generate_from_rng(&mut SysRng).unwrap();
assert_eq!((a - a).is_zero().unwrap_u8(), 1);
}
#[test]
fn from_bytes_reduced() {
let m = Scalar::modulus_as_biguint();
fn reduce<T: Reduce<FieldBytes>>(arr: &[u8]) -> T {
T::reduce(&Array::try_from(arr).unwrap())
}
let s = reduce::<Scalar>(&[0xffu8; 32]).to_biguint().unwrap();
assert!(s < m);
let s = reduce::<Scalar>(&[0u8; 32]).to_biguint().unwrap();
assert!(s.is_zero());
let s = reduce::<Scalar>(&ORDER.to_be_byte_array())
.to_biguint()
.unwrap();
assert!(s.is_zero());
let s = reduce::<NonZeroScalar>(&[0xffu8; 32]).to_biguint().unwrap();
assert!(s < m);
let s = reduce::<NonZeroScalar>(&[0u8; 32]).to_biguint().unwrap();
assert!(s < m);
assert!(!s.is_zero());
let s = reduce::<NonZeroScalar>(&ORDER.to_be_byte_array())
.to_biguint()
.unwrap();
assert!(s < m);
assert!(!s.is_zero());
let s = reduce::<NonZeroScalar>(&(ORDER.wrapping_sub(&U256::ONE)).to_be_byte_array())
.to_biguint()
.unwrap();
assert!(s < m);
assert!(!s.is_zero());
}
#[test]
fn from_wide_bytes_reduced() {
let m = Scalar::modulus_as_biguint();
fn reduce<T: Reduce<WideBytes>>(slice: &[u8]) -> T {
let mut bytes = WideBytes::default();
bytes[(64 - slice.len())..].copy_from_slice(slice);
T::reduce(&bytes)
}
let s = reduce::<Scalar>(&[0xffu8; 64]).to_biguint().unwrap();
assert!(s < m);
let s = reduce::<Scalar>(&[0u8; 64]).to_biguint().unwrap();
assert!(s.is_zero());
let s = reduce::<Scalar>(&ORDER.to_be_byte_array())
.to_biguint()
.unwrap();
assert!(s.is_zero());
let s = reduce::<NonZeroScalar>(&[0xffu8; 64]).to_biguint().unwrap();
assert!(s < m);
let s = reduce::<NonZeroScalar>(&[0u8; 64]).to_biguint().unwrap();
assert!(s < m);
assert!(!s.is_zero());
let s = reduce::<NonZeroScalar>(&ORDER.to_be_byte_array())
.to_biguint()
.unwrap();
assert!(s < m);
assert!(!s.is_zero());
let s = reduce::<NonZeroScalar>(&(ORDER.wrapping_sub(&U256::ONE)).to_be_byte_array())
.to_biguint()
.unwrap();
assert!(s < m);
assert!(!s.is_zero());
}
prop_compose! {
fn scalar()(bytes in any::<[u8; 32]>()) -> Scalar {
<Scalar as Reduce<FieldBytes>>::reduce(&bytes.into())
}
}
proptest! {
#[test]
fn fuzzy_roundtrip_to_bytes(a in scalar()) {
let a_back = Scalar::from_repr(a.to_bytes()).unwrap();
assert_eq!(a, a_back);
}
#[test]
fn fuzzy_roundtrip_to_bytes_unchecked(a in scalar()) {
let bytes = a.to_bytes();
let a_back = Scalar::from_bytes_unchecked(bytes.as_ref());
assert_eq!(a, a_back);
}
#[test]
fn fuzzy_add(a in scalar(), b in scalar()) {
let a_bi = a.to_biguint().unwrap();
let b_bi = b.to_biguint().unwrap();
let res_bi = (&a_bi + &b_bi) % &Scalar::modulus_as_biguint();
let res_ref = Scalar::from(&res_bi);
let res_test = a.add(&b);
assert_eq!(res_ref, res_test);
}
#[test]
fn fuzzy_sub(a in scalar(), b in scalar()) {
let a_bi = a.to_biguint().unwrap();
let b_bi = b.to_biguint().unwrap();
let m = Scalar::modulus_as_biguint();
let res_bi = (&m + &a_bi - &b_bi) % &m;
let res_ref = Scalar::from(&res_bi);
let res_test = a.sub(&b);
assert_eq!(res_ref, res_test);
}
#[test]
fn fuzzy_neg(a in scalar()) {
let a_bi = a.to_biguint().unwrap();
let m = Scalar::modulus_as_biguint();
let res_bi = (&m - &a_bi) % &m;
let res_ref = Scalar::from(&res_bi);
let res_test = -a;
assert_eq!(res_ref, res_test);
}
#[test]
fn fuzzy_mul(a in scalar(), b in scalar()) {
let a_bi = a.to_biguint().unwrap();
let b_bi = b.to_biguint().unwrap();
let res_bi = (&a_bi * &b_bi) % &Scalar::modulus_as_biguint();
let res_ref = Scalar::from(&res_bi);
let res_test = a.mul(&b);
assert_eq!(res_ref, res_test);
}
#[test]
fn fuzzy_rshift(a in scalar(), b in 0usize..512) {
let a_bi = a.to_biguint().unwrap();
let res_bi = &a_bi >> b;
let res_ref = Scalar::from(&res_bi);
let res_test = a >> b;
assert_eq!(res_ref, res_test);
}
#[test]
fn fuzzy_invert(
a in scalar()
) {
let a = if bool::from(a.is_zero()) { Scalar::ONE } else { a };
let a_bi = a.to_biguint().unwrap();
let inv = a.invert().unwrap();
let inv_bi = inv.to_biguint().unwrap();
let m = Scalar::modulus_as_biguint();
assert_eq!((&inv_bi * &a_bi) % &m, 1.to_biguint().unwrap());
}
#[test]
fn fuzzy_invert_vartime(w in scalar()) {
let inv: Option<Scalar> = w.invert().into();
let inv_vartime: Option<Scalar> = w.invert_vartime().into();
assert_eq!(inv, inv_vartime);
}
#[test]
fn fuzzy_from_wide_bytes_reduced(bytes_hi in any::<[u8; 32]>(), bytes_lo in any::<[u8; 32]>()) {
let m = Scalar::modulus_as_biguint();
let mut bytes = [0u8; 64];
bytes[0..32].clone_from_slice(&bytes_hi);
bytes[32..64].clone_from_slice(&bytes_lo);
let s = <Scalar as Reduce<U512>>::reduce(&U512::from_be_slice(&bytes));
let s_bu = s.to_biguint().unwrap();
assert!(s_bu < m);
}
}
}