From c1bcc90ee4970d8b89610cbbe4ac7563462a21ab Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 31 Jul 2020 11:45:19 -0700 Subject: [PATCH] elliptic-curve: NonZeroScalar + Generator (replaces MulBase) Adds a `NonZeroScalar` type generic over curves along with a `Generator` trait which together with the `Mul` trait from `core` can replace the `MulBase` trait. `NonZeroScalar` is able to use the bounds on `Arithmetic::Scalar` and therefore usable anywhere a `Scalar` otherwise would, but with the guarantee that multiplication by an `AffinePoint` will not result in the point at infinity, for which no `AffinePoint` representation is possible given current trait bounds/implementations. --- elliptic-curve/src/lib.rs | 16 ++++-- elliptic-curve/src/ops.rs | 11 ----- elliptic-curve/src/point.rs | 7 +++ elliptic-curve/src/scalar.rs | 51 ++++++++++++++++++++ elliptic-curve/src/weierstrass/public_key.rs | 17 ++++--- 5 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 elliptic-curve/src/point.rs create mode 100644 elliptic-curve/src/scalar.rs diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index c5cf968db..c829740fd 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -24,6 +24,8 @@ extern crate std; pub mod error; pub mod oid; pub mod ops; +pub mod point; +pub mod scalar; pub mod secret_key; // TODO(tarcieri): other curve forms @@ -41,9 +43,12 @@ pub use rand_core; #[cfg(feature = "zeroize")] pub use zeroize; -use core::{fmt::Debug, ops::Add}; +use core::{ + fmt::Debug, + ops::{Add, Mul}, +}; use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; -use subtle::ConditionallySelectable; +use subtle::{ConditionallySelectable, ConstantTimeEq}; #[cfg(feature = "rand_core")] use rand_core::{CryptoRng, RngCore}; @@ -67,10 +72,13 @@ pub trait Curve: Clone + Debug + Default + Eq + Ord + Send + Sync { /// Elliptic curve with curve arithmetic support pub trait Arithmetic: Curve { /// Scalar type for a given curve - type Scalar: ConditionallySelectable + Default + secret_key::FromSecretKey; + type Scalar: ConditionallySelectable + + ConstantTimeEq + + Default + + secret_key::FromSecretKey; /// Affine point type for a given curve - type AffinePoint: ConditionallySelectable + ops::MulBase; + type AffinePoint: ConditionallySelectable + Mul> + point::Generator; } /// Associate an object identifier (OID) with a curve diff --git a/elliptic-curve/src/ops.rs b/elliptic-curve/src/ops.rs index 6e763c544..09f811d7e 100644 --- a/elliptic-curve/src/ops.rs +++ b/elliptic-curve/src/ops.rs @@ -10,14 +10,3 @@ pub trait Invert { /// Invert a field element. fn invert(&self) -> CtOption; } - -/// Fixed-base scalar multiplication. -/// -/// This trait is intended to be implemented on a point type. -pub trait MulBase: Sized { - /// Scalar type - type Scalar; - - /// Multiply scalar by the generator point for the elliptic curve - fn mul_base(scalar: &Self::Scalar) -> CtOption; -} diff --git a/elliptic-curve/src/point.rs b/elliptic-curve/src/point.rs new file mode 100644 index 000000000..67a810a93 --- /dev/null +++ b/elliptic-curve/src/point.rs @@ -0,0 +1,7 @@ +//! Traits for elliptic curve points + +/// Obtain the generator point. +pub trait Generator { + /// Get the generator point for this elliptic curve + fn generator() -> Self; +} diff --git a/elliptic-curve/src/scalar.rs b/elliptic-curve/src/scalar.rs new file mode 100644 index 000000000..0237e8cf3 --- /dev/null +++ b/elliptic-curve/src/scalar.rs @@ -0,0 +1,51 @@ +//! Scalar types + +use crate::{Arithmetic, Curve}; +use subtle::{ConstantTimeEq, CtOption}; + +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + +/// 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. +pub struct NonZeroScalar { + scalar: C::Scalar, +} + +impl NonZeroScalar +where + C: Curve + Arithmetic, +{ + /// Create a [`NonZeroScalar`] from a scalar, performing a constant-time + /// check that it's non-zero. + pub fn new(scalar: C::Scalar) -> CtOption { + let is_zero = scalar.ct_eq(&C::Scalar::default()); + CtOption::new(Self { scalar }, !is_zero) + } +} + +impl AsRef for NonZeroScalar +where + C: Curve + Arithmetic, +{ + fn as_ref(&self) -> &C::Scalar { + &self.scalar + } +} + +#[cfg(feature = "zeroize")] +impl Zeroize for NonZeroScalar +where + C: Curve + Arithmetic, + C::Scalar: Zeroize, +{ + fn zeroize(&mut self) { + self.scalar.zeroize(); + } +} diff --git a/elliptic-curve/src/weierstrass/public_key.rs b/elliptic-curve/src/weierstrass/public_key.rs index 1092912ef..796a0b866 100644 --- a/elliptic-curve/src/weierstrass/public_key.rs +++ b/elliptic-curve/src/weierstrass/public_key.rs @@ -5,9 +5,14 @@ use super::{ point::{CompressedPoint, CompressedPointSize, UncompressedPoint, UncompressedPointSize}, Curve, }; -use crate::{ops::MulBase, secret_key::FromSecretKey, Arithmetic, Error, SecretKey}; -use core::fmt::{self, Debug}; -use core::ops::Add; +use crate::{ + point::Generator, scalar::NonZeroScalar, secret_key::FromSecretKey, Arithmetic, Error, + SecretKey, +}; +use core::{ + fmt::{self, Debug}, + ops::{Add, Mul}, +}; use generic_array::{ typenum::{Unsigned, U1}, ArrayLength, GenericArray, @@ -107,6 +112,7 @@ where impl PublicKey where C: Curve + Arithmetic, + C::AffinePoint: Mul, Output = C::AffinePoint>, C::ElementSize: Add, ::Output: Add, CompressedPoint: From, @@ -118,14 +124,13 @@ where /// /// The `compress` flag requests point compression. pub fn from_secret_key(secret_key: &SecretKey, compress: bool) -> Result { - let ct_option = - C::Scalar::from_secret_key(&secret_key).and_then(|s| C::AffinePoint::mul_base(&s)); + let ct_option = C::Scalar::from_secret_key(&secret_key).and_then(NonZeroScalar::new); if ct_option.is_none().into() { return Err(Error); } - let affine_point = ct_option.unwrap(); + let affine_point = C::AffinePoint::generator() * ct_option.unwrap(); if compress { Ok(PublicKey::Compressed(affine_point.into()))