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()))