From 9c6c5870372b1cc5576451073e28f8236fbeda2a Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 14 Jan 2023 18:55:51 -0700 Subject: [PATCH] elliptic-curve: add `BlindedScalar` Adds a scalar type with random blinding. It implements `Invert` using Stein's algorithm which runs in variable time, but masks the value first with multiplication. This is useful for embedded devices where it can save the full cost of an inversion. https://link.springer.com/article/10.1007/s13389-016-0135-4 --- elliptic-curve/src/arithmetic.rs | 3 +- elliptic-curve/src/dev.rs | 6 +++ elliptic-curve/src/scalar.rs | 4 +- elliptic-curve/src/scalar/blinded.rs | 73 ++++++++++++++++++++++++++++ elliptic-curve/src/scalar/invert.rs | 11 ++--- elliptic-curve/src/scalar/nonzero.rs | 2 +- 6 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 elliptic-curve/src/scalar/blinded.rs diff --git a/elliptic-curve/src/arithmetic.rs b/elliptic-curve/src/arithmetic.rs index 8113f5763..8153fb011 100644 --- a/elliptic-curve/src/arithmetic.rs +++ b/elliptic-curve/src/arithmetic.rs @@ -61,7 +61,8 @@ pub trait CurveArithmetic: Curve { /// - [`Default`] /// - [`Send`] /// - [`Sync`] - type Scalar: DefaultIsZeroes + type Scalar: AsRef + + DefaultIsZeroes + From> + FromUintUnchecked + Into> diff --git a/elliptic-curve/src/dev.rs b/elliptic-curve/src/dev.rs index c60d38b0e..ded320769 100644 --- a/elliptic-curve/src/dev.rs +++ b/elliptic-curve/src/dev.rs @@ -179,6 +179,12 @@ impl PrimeFieldBits for Scalar { } } +impl AsRef for Scalar { + fn as_ref(&self) -> &Scalar { + self + } +} + impl ConditionallySelectable for Scalar { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { Self(ScalarPrimitive::conditional_select(&a.0, &b.0, choice)) diff --git a/elliptic-curve/src/scalar.rs b/elliptic-curve/src/scalar.rs index d4d344423..6ffd63bea 100644 --- a/elliptic-curve/src/scalar.rs +++ b/elliptic-curve/src/scalar.rs @@ -1,5 +1,7 @@ //! Scalar types. +#[cfg(feature = "arithmetic")] +mod blinded; #[cfg(feature = "arithmetic")] mod invert; #[cfg(feature = "arithmetic")] @@ -8,7 +10,7 @@ mod primitive; pub use self::primitive::ScalarPrimitive; #[cfg(feature = "arithmetic")] -pub use self::{invert::invert_vartime, nonzero::NonZeroScalar}; +pub use self::{blinded::BlindedScalar, invert::invert_vartime, nonzero::NonZeroScalar}; use crypto_bigint::Integer; use subtle::Choice; diff --git a/elliptic-curve/src/scalar/blinded.rs b/elliptic-curve/src/scalar/blinded.rs new file mode 100644 index 000000000..33303aef1 --- /dev/null +++ b/elliptic-curve/src/scalar/blinded.rs @@ -0,0 +1,73 @@ +//! Random blinding support for [`Scalar`] + +use super::{invert_vartime, 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 +where + C: CurveArithmetic, +{ + /// Actual scalar value. + scalar: Scalar, + + /// Mask value. + mask: Scalar, +} + +impl BlindedScalar +where + C: CurveArithmetic, +{ + /// Create a new [`BlindedScalar`] from a scalar and a [`CryptoRngCore`]. + pub fn new(scalar: Scalar, rng: &mut impl CryptoRngCore) -> Self { + Self { + scalar, + mask: Scalar::::random(rng), + } + } +} + +impl AsRef> for BlindedScalar +where + C: CurveArithmetic, +{ + fn as_ref(&self) -> &Scalar { + &self.scalar + } +} + +impl Invert for BlindedScalar +where + C: CurveArithmetic, +{ + type Output = CtOption>; + + fn invert(&self) -> CtOption> { + // prevent side channel analysis of scalar inversion by pre-and-post-multiplying + // with the random masking scalar + let masked_scalar = self.scalar * self.mask; + invert_vartime::(&masked_scalar).map(|s| s * self.mask) + } +} + +impl Drop for BlindedScalar +where + C: CurveArithmetic, +{ + fn drop(&mut self) { + self.scalar.zeroize(); + self.mask.zeroize(); + } +} diff --git a/elliptic-curve/src/scalar/invert.rs b/elliptic-curve/src/scalar/invert.rs index d0b2bbedd..0b949718e 100644 --- a/elliptic-curve/src/scalar/invert.rs +++ b/elliptic-curve/src/scalar/invert.rs @@ -1,21 +1,18 @@ use super::FromUintUnchecked; use crate::{ops::Shr1, CurveArithmetic, Scalar}; use ff::{Field, PrimeField}; +use subtle::CtOption; /// Fast variable-time inversion using Stein's algorithm. /// -/// Returns `None` if the scalar is zero. +/// Returns none if the scalar is zero. /// /// #[allow(non_snake_case)] -pub fn invert_vartime(scalar: &Scalar) -> Option> +pub fn invert_vartime(scalar: &Scalar) -> CtOption> where C: CurveArithmetic, { - if scalar.is_zero().into() { - return None; - } - let order_div_2 = Scalar::::from_uint_unchecked(C::ORDER >> 1); let mut u = *scalar; @@ -60,5 +57,5 @@ where } } - Some(C) + CtOption::new(C, !scalar.is_zero()) } diff --git a/elliptic-curve/src/scalar/nonzero.rs b/elliptic-curve/src/scalar/nonzero.rs index c9bb6ff68..1d2a7fdae 100644 --- a/elliptic-curve/src/scalar/nonzero.rs +++ b/elliptic-curve/src/scalar/nonzero.rs @@ -76,7 +76,7 @@ where /// sidechannels. pub fn invert_vartime(&self) -> Self { Self { - scalar: super::invert_vartime::(&self.scalar).expect("nonzero input"), + scalar: super::invert_vartime::(&self.scalar).unwrap(), } } }