Skip to main content

elliptic_curve/scalar/
blinded.rs

1//! Random blinding support for [`Scalar`]
2
3use super::Scalar;
4use crate::{CurveArithmetic, ops::Invert};
5use common::Generate;
6use core::fmt;
7use rand_core::{CryptoRng, TryCryptoRng};
8use subtle::CtOption;
9use zeroize::Zeroize;
10
11#[cfg(feature = "getrandom")]
12use common::getrandom::{self, SysRng, rand_core::UnwrapErr};
13
14/// Scalar blinded with a randomly generated masking value.
15///
16/// This provides a randomly blinded impl of [`Invert`] which is useful for
17/// e.g. ECDSA ephemeral (`k`) scalars.
18///
19/// It implements masked variable-time inversions using Stein's algorithm, which
20/// may be helpful for performance on embedded platforms.
21#[derive(Clone)]
22pub struct BlindedScalar<C>
23where
24    C: CurveArithmetic,
25{
26    /// Actual scalar value.
27    scalar: Scalar<C>,
28
29    /// Mask value.
30    mask: Scalar<C>,
31}
32
33impl<C: CurveArithmetic> fmt::Debug for BlindedScalar<C> {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        f.debug_struct("BlindedScalar").finish_non_exhaustive()
36    }
37}
38
39impl<C> BlindedScalar<C>
40where
41    C: CurveArithmetic,
42{
43    /// Create a new [`BlindedScalar`] using the system's ambient secure RNG.
44    #[cfg(feature = "getrandom")]
45    pub fn new(scalar: Scalar<C>) -> Self {
46        Self::new_from_rng(scalar, &mut UnwrapErr(SysRng))
47    }
48
49    /// Create a new [`BlindedScalar`] using the system's ambient secure RNG.
50    ///
51    /// # Errors
52    /// Returns [`getrandom::Error`] if there is an internal failure in the system's secure RNG.
53    #[cfg(feature = "getrandom")]
54    pub fn try_new(scalar: Scalar<C>) -> Result<Self, getrandom::Error> {
55        Self::try_new_from_rng(scalar, &mut SysRng)
56    }
57
58    /// Create a new [`BlindedScalar`] from a scalar and a [`CryptoRng`].
59    pub fn new_from_rng<R: CryptoRng + ?Sized>(scalar: Scalar<C>, rng: &mut R) -> Self {
60        let Ok(ret) = Self::try_new_from_rng(scalar, rng);
61        ret
62    }
63
64    /// Create a new [`BlindedScalar`] from a scalar and a [`CryptoRng`].
65    ///
66    /// # Errors
67    /// Returns `R::Error` in the event of an RNG failure.
68    pub fn try_new_from_rng<R>(scalar: Scalar<C>, rng: &mut R) -> Result<Self, R::Error>
69    where
70        R: TryCryptoRng + ?Sized,
71    {
72        let mask = Scalar::<C>::try_generate_from_rng(rng)?;
73        Ok(Self { scalar, mask })
74    }
75}
76
77impl<C> AsRef<Scalar<C>> for BlindedScalar<C>
78where
79    C: CurveArithmetic,
80{
81    fn as_ref(&self) -> &Scalar<C> {
82        &self.scalar
83    }
84}
85
86impl<C> Invert for BlindedScalar<C>
87where
88    C: CurveArithmetic,
89{
90    type Output = CtOption<Scalar<C>>;
91
92    fn invert(&self) -> CtOption<Scalar<C>> {
93        // prevent side channel analysis of scalar inversion by pre-and-post-multiplying
94        // with the random masking scalar
95        (self.scalar * self.mask)
96            .invert_vartime()
97            .map(|s| s * self.mask)
98    }
99}
100
101impl<C> Drop for BlindedScalar<C>
102where
103    C: CurveArithmetic,
104{
105    fn drop(&mut self) {
106        self.scalar.zeroize();
107        self.mask.zeroize();
108    }
109}