chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,74 @@
//! Random blinding support for [`Scalar`]
use super::Scalar;
use crate::{ops::Invert, CurveArithmetic};
use group::ff::Field;
use rand_core::CryptoRngCore;
use subtle::CtOption;
use zeroize::Zeroize;
/// Scalar blinded with a randomly generated masking value.
///
/// This provides a randomly blinded impl of [`Invert`] which is useful for
/// e.g. ECDSA ephemeral (`k`) scalars.
///
/// It implements masked variable-time inversions using Stein's algorithm, which
/// may be helpful for performance on embedded platforms.
#[derive(Clone)]
pub struct BlindedScalar<C>
where
C: CurveArithmetic,
{
/// Actual scalar value.
scalar: Scalar<C>,
/// Mask value.
mask: Scalar<C>,
}
impl<C> BlindedScalar<C>
where
C: CurveArithmetic,
{
/// Create a new [`BlindedScalar`] from a scalar and a [`CryptoRngCore`].
pub fn new(scalar: Scalar<C>, rng: &mut impl CryptoRngCore) -> Self {
Self {
scalar,
mask: Scalar::<C>::random(rng),
}
}
}
impl<C> AsRef<Scalar<C>> for BlindedScalar<C>
where
C: CurveArithmetic,
{
fn as_ref(&self) -> &Scalar<C> {
&self.scalar
}
}
impl<C> Invert for BlindedScalar<C>
where
C: CurveArithmetic,
{
type Output = CtOption<Scalar<C>>;
fn invert(&self) -> CtOption<Scalar<C>> {
// prevent side channel analysis of scalar inversion by pre-and-post-multiplying
// with the random masking scalar
(self.scalar * self.mask)
.invert_vartime()
.map(|s| s * self.mask)
}
}
impl<C> Drop for BlindedScalar<C>
where
C: CurveArithmetic,
{
fn drop(&mut self) {
self.scalar.zeroize();
self.mask.zeroize();
}
}

View File

@@ -0,0 +1,405 @@
//! Non-zero scalar type.
use crate::{
ops::{Invert, Reduce, ReduceNonZero},
scalar::IsHigh,
CurveArithmetic, Error, FieldBytes, PrimeCurve, Scalar, ScalarPrimitive, SecretKey,
};
use base16ct::HexDisplay;
use core::{
fmt,
ops::{Deref, Mul, Neg},
str,
};
use crypto_bigint::{ArrayEncoding, Integer};
use ff::{Field, PrimeField};
use generic_array::{typenum::Unsigned, GenericArray};
use rand_core::CryptoRngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use zeroize::Zeroize;
#[cfg(feature = "serde")]
use serdect::serde::{de, ser, Deserialize, Serialize};
/// Non-zero scalar type.
///
/// This type ensures that its value is not zero, ala `core::num::NonZero*`.
/// To do this, the generic `S` type must impl both `Default` and
/// `ConstantTimeEq`, with the requirement that `S::default()` returns 0.
///
/// In the context of ECC, it's useful for ensuring that scalar multiplication
/// cannot result in the point at infinity.
#[derive(Clone)]
pub struct NonZeroScalar<C>
where
C: CurveArithmetic,
{
scalar: Scalar<C>,
}
impl<C> NonZeroScalar<C>
where
C: CurveArithmetic,
{
/// Generate a random `NonZeroScalar`.
pub fn random(mut rng: &mut impl CryptoRngCore) -> Self {
// Use rejection sampling to eliminate zero values.
// While this method isn't constant-time, the attacker shouldn't learn
// anything about unrelated outputs so long as `rng` is a secure `CryptoRng`.
loop {
if let Some(result) = Self::new(Field::random(&mut rng)).into() {
break result;
}
}
}
/// Create a [`NonZeroScalar`] from a scalar.
pub fn new(scalar: Scalar<C>) -> CtOption<Self> {
CtOption::new(Self { scalar }, !scalar.is_zero())
}
/// Decode a [`NonZeroScalar`] from a big endian-serialized field element.
pub fn from_repr(repr: FieldBytes<C>) -> CtOption<Self> {
Scalar::<C>::from_repr(repr).and_then(Self::new)
}
/// Create a [`NonZeroScalar`] from a `C::Uint`.
pub fn from_uint(uint: C::Uint) -> CtOption<Self> {
ScalarPrimitive::new(uint).and_then(|scalar| Self::new(scalar.into()))
}
}
impl<C> AsRef<Scalar<C>> for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn as_ref(&self) -> &Scalar<C> {
&self.scalar
}
}
impl<C> ConditionallySelectable for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
scalar: Scalar::<C>::conditional_select(&a.scalar, &b.scalar, choice),
}
}
}
impl<C> ConstantTimeEq for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn ct_eq(&self, other: &Self) -> Choice {
self.scalar.ct_eq(&other.scalar)
}
}
impl<C> Copy for NonZeroScalar<C> where C: CurveArithmetic {}
impl<C> Deref for NonZeroScalar<C>
where
C: CurveArithmetic,
{
type Target = Scalar<C>;
fn deref(&self) -> &Scalar<C> {
&self.scalar
}
}
impl<C> From<NonZeroScalar<C>> for FieldBytes<C>
where
C: CurveArithmetic,
{
fn from(scalar: NonZeroScalar<C>) -> FieldBytes<C> {
Self::from(&scalar)
}
}
impl<C> From<&NonZeroScalar<C>> for FieldBytes<C>
where
C: CurveArithmetic,
{
fn from(scalar: &NonZeroScalar<C>) -> FieldBytes<C> {
scalar.to_repr()
}
}
impl<C> From<NonZeroScalar<C>> for ScalarPrimitive<C>
where
C: CurveArithmetic,
{
#[inline]
fn from(scalar: NonZeroScalar<C>) -> ScalarPrimitive<C> {
Self::from(&scalar)
}
}
impl<C> From<&NonZeroScalar<C>> for ScalarPrimitive<C>
where
C: CurveArithmetic,
{
fn from(scalar: &NonZeroScalar<C>) -> ScalarPrimitive<C> {
ScalarPrimitive::from_bytes(&scalar.to_repr()).unwrap()
}
}
impl<C> From<SecretKey<C>> for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn from(sk: SecretKey<C>) -> NonZeroScalar<C> {
Self::from(&sk)
}
}
impl<C> From<&SecretKey<C>> for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn from(sk: &SecretKey<C>) -> NonZeroScalar<C> {
let scalar = sk.as_scalar_primitive().to_scalar();
debug_assert!(!bool::from(scalar.is_zero()));
Self { scalar }
}
}
impl<C> Invert for NonZeroScalar<C>
where
C: CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
{
type Output = Self;
fn invert(&self) -> Self {
Self {
// This will always succeed since `scalar` will never be 0
scalar: Invert::invert(&self.scalar).unwrap(),
}
}
fn invert_vartime(&self) -> Self::Output {
Self {
// This will always succeed since `scalar` will never be 0
scalar: Invert::invert_vartime(&self.scalar).unwrap(),
}
}
}
impl<C> IsHigh for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn is_high(&self) -> Choice {
self.scalar.is_high()
}
}
impl<C> Neg for NonZeroScalar<C>
where
C: CurveArithmetic,
{
type Output = NonZeroScalar<C>;
fn neg(self) -> NonZeroScalar<C> {
let scalar = -self.scalar;
debug_assert!(!bool::from(scalar.is_zero()));
NonZeroScalar { scalar }
}
}
impl<C> Mul<NonZeroScalar<C>> for NonZeroScalar<C>
where
C: PrimeCurve + CurveArithmetic,
{
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
Self::mul(self, &other)
}
}
impl<C> Mul<&NonZeroScalar<C>> for NonZeroScalar<C>
where
C: PrimeCurve + CurveArithmetic,
{
type Output = Self;
fn mul(self, other: &Self) -> Self {
// Multiplication is modulo a prime, so the product of two non-zero
// scalars is also non-zero.
let scalar = self.scalar * other.scalar;
debug_assert!(!bool::from(scalar.is_zero()));
NonZeroScalar { scalar }
}
}
/// Note: this is a non-zero reduction, as it's impl'd for [`NonZeroScalar`].
impl<C, I> Reduce<I> for NonZeroScalar<C>
where
C: CurveArithmetic,
I: Integer + ArrayEncoding,
Scalar<C>: Reduce<I> + ReduceNonZero<I>,
{
type Bytes = <Scalar<C> as Reduce<I>>::Bytes;
fn reduce(n: I) -> Self {
let scalar = Scalar::<C>::reduce_nonzero(n);
debug_assert!(!bool::from(scalar.is_zero()));
Self { scalar }
}
fn reduce_bytes(bytes: &Self::Bytes) -> Self {
let scalar = Scalar::<C>::reduce_nonzero_bytes(bytes);
debug_assert!(!bool::from(scalar.is_zero()));
Self { scalar }
}
}
/// Note: forwards to the [`Reduce`] impl.
impl<C, I> ReduceNonZero<I> for NonZeroScalar<C>
where
Self: Reduce<I>,
C: CurveArithmetic,
I: Integer + ArrayEncoding,
Scalar<C>: Reduce<I, Bytes = Self::Bytes> + ReduceNonZero<I>,
{
fn reduce_nonzero(n: I) -> Self {
Self::reduce(n)
}
fn reduce_nonzero_bytes(bytes: &Self::Bytes) -> Self {
Self::reduce_bytes(bytes)
}
}
impl<C> TryFrom<&[u8]> for NonZeroScalar<C>
where
C: CurveArithmetic,
{
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() == C::FieldBytesSize::USIZE {
Option::from(NonZeroScalar::from_repr(GenericArray::clone_from_slice(
bytes,
)))
.ok_or(Error)
} else {
Err(Error)
}
}
}
impl<C> Zeroize for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn zeroize(&mut self) {
// Use zeroize's volatile writes to ensure value is cleared.
self.scalar.zeroize();
// Write a 1 instead of a 0 to ensure this type's non-zero invariant
// is upheld.
self.scalar = Scalar::<C>::ONE;
}
}
impl<C> fmt::Display for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:X}")
}
}
impl<C> fmt::LowerHex for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:x}", HexDisplay(&self.to_repr()))
}
}
impl<C> fmt::UpperHex for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:}", HexDisplay(&self.to_repr()))
}
}
impl<C> str::FromStr for NonZeroScalar<C>
where
C: CurveArithmetic,
{
type Err = Error;
fn from_str(hex: &str) -> Result<Self, Error> {
let mut bytes = FieldBytes::<C>::default();
if base16ct::mixed::decode(hex, &mut bytes)?.len() == bytes.len() {
Option::from(Self::from_repr(bytes)).ok_or(Error)
} else {
Err(Error)
}
}
}
#[cfg(feature = "serde")]
impl<C> Serialize for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
ScalarPrimitive::from(self).serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, C> Deserialize<'de> for NonZeroScalar<C>
where
C: CurveArithmetic,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let scalar = ScalarPrimitive::deserialize(deserializer)?;
Option::from(Self::new(scalar.into()))
.ok_or_else(|| de::Error::custom("expected non-zero scalar"))
}
}
#[cfg(all(test, feature = "dev"))]
mod tests {
use crate::dev::{NonZeroScalar, Scalar};
use ff::{Field, PrimeField};
use hex_literal::hex;
use zeroize::Zeroize;
#[test]
fn round_trip() {
let bytes = hex!("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
let scalar = NonZeroScalar::from_repr(bytes.into()).unwrap();
assert_eq!(&bytes, scalar.to_repr().as_slice());
}
#[test]
fn zeroize() {
let mut scalar = NonZeroScalar::new(Scalar::from(42u64)).unwrap();
scalar.zeroize();
assert_eq!(*scalar, Scalar::ONE);
}
}

View File

@@ -0,0 +1,434 @@
//! Generic scalar type with primitive functionality.
use crate::{
bigint::{prelude::*, Limb, NonZero},
scalar::FromUintUnchecked,
scalar::IsHigh,
Curve, Error, FieldBytes, FieldBytesEncoding, Result,
};
use base16ct::HexDisplay;
use core::{
cmp::Ordering,
fmt,
ops::{Add, AddAssign, Neg, ShrAssign, Sub, SubAssign},
str,
};
use generic_array::{typenum::Unsigned, GenericArray};
use rand_core::CryptoRngCore;
use subtle::{
Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess,
CtOption,
};
use zeroize::DefaultIsZeroes;
#[cfg(feature = "arithmetic")]
use super::{CurveArithmetic, Scalar};
#[cfg(feature = "serde")]
use serdect::serde::{de, ser, Deserialize, Serialize};
/// Generic scalar type with primitive functionality.
///
/// This type provides a baseline level of scalar arithmetic functionality
/// which is always available for all curves, regardless of if they implement
/// any arithmetic traits.
///
/// # `serde` support
///
/// When the optional `serde` feature of this create is enabled, [`Serialize`]
/// and [`Deserialize`] impls are provided for this type.
///
/// The serialization is a fixed-width big endian encoding. When used with
/// textual formats, the binary data is encoded as hexadecimal.
// TODO(tarcieri): use `crypto-bigint`'s `Residue` type, expose more functionality?
#[derive(Copy, Clone, Debug, Default)]
pub struct ScalarPrimitive<C: Curve> {
/// Inner unsigned integer type.
inner: C::Uint,
}
impl<C> ScalarPrimitive<C>
where
C: Curve,
{
/// Zero scalar.
pub const ZERO: Self = Self {
inner: C::Uint::ZERO,
};
/// Multiplicative identity.
pub const ONE: Self = Self {
inner: C::Uint::ONE,
};
/// Scalar modulus.
pub const MODULUS: C::Uint = C::ORDER;
/// Generate a random [`ScalarPrimitive`].
pub fn random(rng: &mut impl CryptoRngCore) -> Self {
Self {
inner: C::Uint::random_mod(rng, &NonZero::new(Self::MODULUS).unwrap()),
}
}
/// Create a new scalar from [`Curve::Uint`].
pub fn new(uint: C::Uint) -> CtOption<Self> {
CtOption::new(Self { inner: uint }, uint.ct_lt(&Self::MODULUS))
}
/// Decode [`ScalarPrimitive`] from a serialized field element
pub fn from_bytes(bytes: &FieldBytes<C>) -> CtOption<Self> {
Self::new(C::Uint::decode_field_bytes(bytes))
}
/// Decode [`ScalarPrimitive`] from a big endian byte slice.
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() == C::FieldBytesSize::USIZE {
Option::from(Self::from_bytes(GenericArray::from_slice(slice))).ok_or(Error)
} else {
Err(Error)
}
}
/// Borrow the inner `C::Uint`.
pub fn as_uint(&self) -> &C::Uint {
&self.inner
}
/// Borrow the inner limbs as a slice.
pub fn as_limbs(&self) -> &[Limb] {
self.inner.as_ref()
}
/// Is this [`ScalarPrimitive`] value equal to zero?
pub fn is_zero(&self) -> Choice {
self.inner.is_zero()
}
/// Is this [`ScalarPrimitive`] value even?
pub fn is_even(&self) -> Choice {
self.inner.is_even()
}
/// Is this [`ScalarPrimitive`] value odd?
pub fn is_odd(&self) -> Choice {
self.inner.is_odd()
}
/// Encode [`ScalarPrimitive`] as a serialized field element.
pub fn to_bytes(&self) -> FieldBytes<C> {
self.inner.encode_field_bytes()
}
/// Convert to a `C::Uint`.
pub fn to_uint(&self) -> C::Uint {
self.inner
}
}
impl<C> FromUintUnchecked for ScalarPrimitive<C>
where
C: Curve,
{
type Uint = C::Uint;
fn from_uint_unchecked(uint: C::Uint) -> Self {
Self { inner: uint }
}
}
#[cfg(feature = "arithmetic")]
impl<C> ScalarPrimitive<C>
where
C: CurveArithmetic,
{
/// Convert [`ScalarPrimitive`] into a given curve's scalar type.
pub(super) fn to_scalar(self) -> Scalar<C> {
Scalar::<C>::from_uint_unchecked(self.inner)
}
}
// TODO(tarcieri): better encapsulate this?
impl<C> AsRef<[Limb]> for ScalarPrimitive<C>
where
C: Curve,
{
fn as_ref(&self) -> &[Limb] {
self.as_limbs()
}
}
impl<C> ConditionallySelectable for ScalarPrimitive<C>
where
C: Curve,
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
inner: C::Uint::conditional_select(&a.inner, &b.inner, choice),
}
}
}
impl<C> ConstantTimeEq for ScalarPrimitive<C>
where
C: Curve,
{
fn ct_eq(&self, other: &Self) -> Choice {
self.inner.ct_eq(&other.inner)
}
}
impl<C> ConstantTimeLess for ScalarPrimitive<C>
where
C: Curve,
{
fn ct_lt(&self, other: &Self) -> Choice {
self.inner.ct_lt(&other.inner)
}
}
impl<C> ConstantTimeGreater for ScalarPrimitive<C>
where
C: Curve,
{
fn ct_gt(&self, other: &Self) -> Choice {
self.inner.ct_gt(&other.inner)
}
}
impl<C: Curve> DefaultIsZeroes for ScalarPrimitive<C> {}
impl<C: Curve> Eq for ScalarPrimitive<C> {}
impl<C> PartialEq for ScalarPrimitive<C>
where
C: Curve,
{
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl<C> PartialOrd for ScalarPrimitive<C>
where
C: Curve,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<C> Ord for ScalarPrimitive<C>
where
C: Curve,
{
fn cmp(&self, other: &Self) -> Ordering {
self.inner.cmp(&other.inner)
}
}
impl<C> From<u64> for ScalarPrimitive<C>
where
C: Curve,
{
fn from(n: u64) -> Self {
Self {
inner: C::Uint::from(n),
}
}
}
impl<C> Add<ScalarPrimitive<C>> for ScalarPrimitive<C>
where
C: Curve,
{
type Output = Self;
fn add(self, other: Self) -> Self {
self.add(&other)
}
}
impl<C> Add<&ScalarPrimitive<C>> for ScalarPrimitive<C>
where
C: Curve,
{
type Output = Self;
fn add(self, other: &Self) -> Self {
Self {
inner: self.inner.add_mod(&other.inner, &Self::MODULUS),
}
}
}
impl<C> AddAssign<ScalarPrimitive<C>> for ScalarPrimitive<C>
where
C: Curve,
{
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl<C> AddAssign<&ScalarPrimitive<C>> for ScalarPrimitive<C>
where
C: Curve,
{
fn add_assign(&mut self, other: &Self) {
*self = *self + other;
}
}
impl<C> Sub<ScalarPrimitive<C>> for ScalarPrimitive<C>
where
C: Curve,
{
type Output = Self;
fn sub(self, other: Self) -> Self {
self.sub(&other)
}
}
impl<C> Sub<&ScalarPrimitive<C>> for ScalarPrimitive<C>
where
C: Curve,
{
type Output = Self;
fn sub(self, other: &Self) -> Self {
Self {
inner: self.inner.sub_mod(&other.inner, &Self::MODULUS),
}
}
}
impl<C> SubAssign<ScalarPrimitive<C>> for ScalarPrimitive<C>
where
C: Curve,
{
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl<C> SubAssign<&ScalarPrimitive<C>> for ScalarPrimitive<C>
where
C: Curve,
{
fn sub_assign(&mut self, other: &Self) {
*self = *self - other;
}
}
impl<C> Neg for ScalarPrimitive<C>
where
C: Curve,
{
type Output = Self;
fn neg(self) -> Self {
Self {
inner: self.inner.neg_mod(&Self::MODULUS),
}
}
}
impl<C> Neg for &ScalarPrimitive<C>
where
C: Curve,
{
type Output = ScalarPrimitive<C>;
fn neg(self) -> ScalarPrimitive<C> {
-*self
}
}
impl<C> ShrAssign<usize> for ScalarPrimitive<C>
where
C: Curve,
{
fn shr_assign(&mut self, rhs: usize) {
self.inner >>= rhs;
}
}
impl<C> IsHigh for ScalarPrimitive<C>
where
C: Curve,
{
fn is_high(&self) -> Choice {
let n_2 = C::ORDER >> 1;
self.inner.ct_gt(&n_2)
}
}
impl<C> fmt::Display for ScalarPrimitive<C>
where
C: Curve,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:X}")
}
}
impl<C> fmt::LowerHex for ScalarPrimitive<C>
where
C: Curve,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:x}", HexDisplay(&self.to_bytes()))
}
}
impl<C> fmt::UpperHex for ScalarPrimitive<C>
where
C: Curve,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:X}", HexDisplay(&self.to_bytes()))
}
}
impl<C> str::FromStr for ScalarPrimitive<C>
where
C: Curve,
{
type Err = Error;
fn from_str(hex: &str) -> Result<Self> {
let mut bytes = FieldBytes::<C>::default();
base16ct::lower::decode(hex, &mut bytes)?;
Self::from_slice(&bytes)
}
}
#[cfg(feature = "serde")]
impl<C> Serialize for ScalarPrimitive<C>
where
C: Curve,
{
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, C> Deserialize<'de> for ScalarPrimitive<C>
where
C: Curve,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let mut bytes = FieldBytes::<C>::default();
serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?;
Self::from_slice(&bytes).map_err(|_| de::Error::custom("scalar out of range"))
}
}