diff --git a/elliptic-curve/src/jwk.rs b/elliptic-curve/src/jwk.rs index 55ea1e8f2..c2b33c5ea 100644 --- a/elliptic-curve/src/jwk.rs +++ b/elliptic-curve/src/jwk.rs @@ -7,7 +7,7 @@ use crate::{ sec1::{ Coordinates, EncodedPoint, UncompressedPointSize, UntaggedPointSize, ValidatePublicKey, }, - secret_key::{SecretKey, SecretValue}, + secret_key::SecretKey, weierstrass::Curve, Error, FieldBytes, }; @@ -135,9 +135,7 @@ impl JwkEcKey { #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] pub fn to_secret_key(&self) -> Result, Error> where - C: Curve + JwkParameters + ValidatePublicKey + SecretValue, - C::Secret: Clone + Zeroize, - FieldBytes: From, + C: Curve + JwkParameters + ValidatePublicKey, UntaggedPointSize: Add + ArrayLength, UncompressedPointSize: ArrayLength, { @@ -232,9 +230,7 @@ where #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] impl TryFrom for SecretKey where - C: Curve + JwkParameters + ValidatePublicKey + SecretValue, - C::Secret: Clone + Zeroize, - FieldBytes: From, + C: Curve + JwkParameters + ValidatePublicKey, UntaggedPointSize: Add + ArrayLength, UncompressedPointSize: ArrayLength, { @@ -248,9 +244,7 @@ where #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] impl TryFrom<&JwkEcKey> for SecretKey where - C: Curve + JwkParameters + ValidatePublicKey + SecretValue, - C::Secret: Clone + Zeroize, - FieldBytes: From, + C: Curve + JwkParameters + ValidatePublicKey, UntaggedPointSize: Add + ArrayLength, UncompressedPointSize: ArrayLength, { diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index 8e8054d45..7de7e38db 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -74,9 +74,6 @@ pub use { #[cfg(feature = "bits")] pub use crate::scalar::ScalarBits; -#[cfg(all(feature = "hazmat", feature = "zeroize"))] -pub use secret_key::{SecretBytes, SecretValue}; - #[cfg(feature = "jwk")] pub use crate::jwk::{JwkEcKey, JwkParameters}; diff --git a/elliptic-curve/src/scalar/bytes.rs b/elliptic-curve/src/scalar/bytes.rs index a57f5f49d..16230bda1 100644 --- a/elliptic-curve/src/scalar/bytes.rs +++ b/elliptic-curve/src/scalar/bytes.rs @@ -9,7 +9,10 @@ use generic_array::GenericArray; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeLess, CtOption}; #[cfg(feature = "arithmetic")] -use crate::{group::ff::PrimeField, ProjectiveArithmetic, Scalar}; +use crate::{group::ff::PrimeField, NonZeroScalar, ProjectiveArithmetic, Scalar}; + +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; /// Scalar bytes: wrapper for [`FieldBytes`] which guarantees that the the /// inner byte value is within range of the [`Curve::ORDER`]. @@ -161,6 +164,32 @@ where } } +#[cfg(feature = "arithmetic")] +impl From> for ScalarBytes +where + C: Curve + ProjectiveArithmetic, + Scalar: PrimeField>, +{ + fn from(scalar: NonZeroScalar) -> ScalarBytes { + ScalarBytes { + inner: scalar.into(), + } + } +} + +#[cfg(feature = "arithmetic")] +impl TryFrom> for NonZeroScalar +where + C: Curve + ProjectiveArithmetic, + Scalar: PrimeField>, +{ + type Error = Error; + + fn try_from(bytes: ScalarBytes) -> Result> { + NonZeroScalar::::from_repr(bytes.inner).ok_or(Error) + } +} + impl PartialEq for ScalarBytes where C: Curve, @@ -185,6 +214,16 @@ where } } +#[cfg(feature = "zeroize")] +impl Zeroize for ScalarBytes +where + C: Curve, +{ + fn zeroize(&mut self) { + self.inner.zeroize(); + } +} + #[cfg(all(test, feature = "dev"))] mod tests { use crate::dev::MockCurve; diff --git a/elliptic-curve/src/scalar/non_zero.rs b/elliptic-curve/src/scalar/non_zero.rs index ad76e86b8..3d9818833 100644 --- a/elliptic-curve/src/scalar/non_zero.rs +++ b/elliptic-curve/src/scalar/non_zero.rs @@ -12,7 +12,7 @@ use generic_array::GenericArray; use subtle::{Choice, ConditionallySelectable, CtOption}; #[cfg(feature = "zeroize")] -use zeroize::Zeroize; +use {crate::SecretKey, zeroize::Zeroize}; /// Non-zero scalar type. /// @@ -114,6 +114,20 @@ where } } +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl From<&SecretKey> for NonZeroScalar +where + C: Curve + ProjectiveArithmetic, + Scalar: PrimeField>, +{ + fn from(sk: &SecretKey) -> NonZeroScalar { + let scalar = sk.as_scalar_bytes().to_scalar(); + debug_assert!(!scalar.is_zero()); + Self { scalar } + } +} + impl Invert for NonZeroScalar where C: Curve + ProjectiveArithmetic, diff --git a/elliptic-curve/src/sec1.rs b/elliptic-curve/src/sec1.rs index 3e67a5b3c..91a7b335b 100644 --- a/elliptic-curve/src/sec1.rs +++ b/elliptic-curve/src/sec1.rs @@ -28,10 +28,7 @@ use crate::{ }; #[cfg(feature = "zeroize")] -use crate::{ - secret_key::{SecretKey, SecretValue}, - zeroize::Zeroize, -}; +use crate::{secret_key::SecretKey, zeroize::Zeroize}; /// Size of a compressed point for the given elliptic curve when encoded /// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm @@ -134,7 +131,7 @@ where AffinePoint: ToEncodedPoint, Scalar: PrimeField> + Zeroize, { - (C::ProjectivePoint::generator() * secret_key.secret_scalar().as_ref()) + (C::ProjectivePoint::generator() * secret_key.to_secret_scalar().as_ref()) .to_affine() .to_encoded_point(compress) } @@ -496,7 +493,7 @@ where #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] pub trait ValidatePublicKey where - Self: Curve + SecretValue, + Self: Curve, UntaggedPointSize: Add + ArrayLength, UncompressedPointSize: ArrayLength, { diff --git a/elliptic-curve/src/secret_key.rs b/elliptic-curve/src/secret_key.rs index cad09836b..ce83f8c30 100644 --- a/elliptic-curve/src/secret_key.rs +++ b/elliptic-curve/src/secret_key.rs @@ -10,11 +10,10 @@ #[cfg(feature = "pkcs8")] mod pkcs8; -use crate::{bigint::NumBytes, Curve, Error, FieldBytes, Result}; +use crate::{Curve, Error, FieldBytes, Result, ScalarBytes}; use core::{ convert::TryFrom, fmt::{self, Debug}, - ops::Deref, }; use zeroize::Zeroize; @@ -71,68 +70,69 @@ use {crate::pkcs8::FromPrivateKey, core::str::FromStr}; /// curve crate) is enabled, a [`FromStr`] impl is also available. #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] #[derive(Clone)] -pub struct SecretKey { - /// Secret value (i.e. secret scalar) - secret_value: C::Secret, +pub struct SecretKey { + /// Serialized scalar value + inner: ScalarBytes, } impl SecretKey where - C: Curve + SecretValue, - C::Secret: Clone + Zeroize, - FieldBytes: From, + C: Curve, { /// Generate a random [`SecretKey`] #[cfg(feature = "arithmetic")] #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] pub fn random(rng: impl CryptoRng + RngCore) -> Self where - C: ProjectiveArithmetic + SecretValue>, + C: ProjectiveArithmetic, Scalar: PrimeField> + Zeroize, { Self { - secret_value: NonZeroScalar::::random(rng), + inner: NonZeroScalar::::random(rng).into(), } } /// Create a new secret key from a serialized scalar value - pub fn new(secret_value: C::Secret) -> Self { - Self { secret_value } + pub fn new(scalar: ScalarBytes) -> Self { + Self { inner: scalar } } /// Deserialize raw private scalar as a big endian integer pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result { - let bytes = bytes.as_ref(); + let scalar = ScalarBytes::try_from(bytes.as_ref())?; - if bytes.len() != C::UInt::NUM_BYTES { + if scalar.is_zero().into() { return Err(Error); } - C::from_secret_bytes(bytes.into()) - .map(|secret_value| SecretKey { secret_value }) - .ok_or(Error) + Ok(Self { inner: scalar }) } /// Expose the byte serialization of the value this [`SecretKey`] wraps pub fn to_bytes(&self) -> FieldBytes { - self.secret_value.clone().into() + self.inner.clone().into() } - /// Borrow the inner secret [`Scalar`] value. + /// Borrow the inner secret [`ScalarBytes`] value. /// /// # Warning /// /// This value is key material. /// /// Please treat it with the care it deserves! + pub fn as_scalar_bytes(&self) -> &ScalarBytes { + &self.inner + } + + /// Get the secret scalar value for this key.. #[cfg(feature = "arithmetic")] #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] - pub fn secret_scalar(&self) -> &NonZeroScalar + pub fn to_secret_scalar(&self) -> NonZeroScalar where - C: ProjectiveArithmetic + SecretValue>, + C: ProjectiveArithmetic, Scalar: PrimeField> + Zeroize, { - &self.secret_value + self.into() } /// Get the [`PublicKey`] which corresponds to this secret key @@ -140,12 +140,13 @@ where #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] pub fn public_key(&self) -> PublicKey where - C: weierstrass::Curve + ProjectiveArithmetic + SecretValue>, + C: weierstrass::Curve + ProjectiveArithmetic, AffinePoint: Copy + Clone + Debug + Default, ProjectivePoint: From>, Scalar: PrimeField> + Zeroize, { - PublicKey::from_secret_scalar(self.secret_scalar()) + // TODO(tarcieri): simplify conversion + PublicKey::from_secret_scalar(&self.to_secret_scalar()) } /// Parse a [`JwkEcKey`] JSON Web Key (JWK) into a [`SecretKey`]. @@ -207,9 +208,7 @@ where impl TryFrom<&[u8]> for SecretKey where - C: Curve + SecretValue, - C::Secret: Clone + Zeroize, - FieldBytes: From, + C: Curve, { type Error = Error; @@ -220,7 +219,7 @@ where impl Debug for SecretKey where - C: Curve + SecretValue, + C: Curve, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // TODO(tarcieri): use `debug_struct` and `finish_non_exhaustive` when stable @@ -230,90 +229,9 @@ where impl Drop for SecretKey where - C: Curve + SecretValue, + C: Curve, { fn drop(&mut self) { - self.secret_value.zeroize(); - } -} - -/// Inner value stored by a [`SecretKey`]. -#[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -pub trait SecretValue: Curve { - /// Inner secret value. - /// - /// ⚠️ WARNING ⚠️ - /// - /// This type is not intended to be part of the public API and in future - /// versions of this crate we will try to explore ways to hide it. - /// - /// Crates such as `k256` and `p256` conditionally define this type - /// differently depending on what cargo features are enabled. - /// This means any consumers of this crate attempting to use this type - /// may experience breakages if the cargo features are not what are - /// expected. - /// - /// We regret exposing it as part of the public API for now, however if - /// you do reference this type as a downstream consumer of a curve crate, - /// be aware you will experience breakages! - type Secret: Into> + Zeroize; - - /// Parse the secret value from bytes - // TODO(tarcieri): make this a `CtOption`? - fn from_secret_bytes(bytes: &FieldBytes) -> Option; -} - -#[cfg(feature = "arithmetic")] -#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] -impl SecretValue for C -where - C: Curve + ProjectiveArithmetic, - Scalar: PrimeField> + Zeroize, -{ - type Secret = NonZeroScalar; - - fn from_secret_bytes(repr: &FieldBytes) -> Option> { - NonZeroScalar::from_repr(repr.clone()) - } -} - -/// Newtype wrapper for [`FieldBytes`] which impls [`Zeroize`]. -/// -/// This allows it to fulfill the [`Zeroize`] bound on [`SecretValue::Secret`]. -#[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -#[derive(Clone)] -pub struct SecretBytes(FieldBytes); - -impl From> for SecretBytes { - fn from(bytes: FieldBytes) -> SecretBytes { - Self(bytes) - } -} - -impl From> for FieldBytes { - fn from(bytes: SecretBytes) -> FieldBytes { - bytes.0 - } -} - -impl AsRef<[u8]> for SecretBytes { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl Deref for SecretBytes { - type Target = FieldBytes; - - fn deref(&self) -> &FieldBytes { - &self.0 - } -} - -impl Zeroize for SecretBytes { - fn zeroize(&mut self) { - self.0.as_mut().zeroize(); + self.inner.zeroize(); } } diff --git a/elliptic-curve/src/secret_key/pkcs8.rs b/elliptic-curve/src/secret_key/pkcs8.rs index d64715a52..b6ca4491a 100644 --- a/elliptic-curve/src/secret_key/pkcs8.rs +++ b/elliptic-curve/src/secret_key/pkcs8.rs @@ -1,6 +1,6 @@ //! PKCS#8 encoding/decoding support -use super::{SecretKey, SecretValue}; +use super::SecretKey; use crate::{ sec1::{self, UncompressedPointSize, UntaggedPointSize, ValidatePublicKey}, weierstrass, AlgorithmParameters, FieldBytes, ALGORITHM_OID, @@ -43,9 +43,7 @@ const ENCODING_ERROR_MSG: &str = "DER encoding error"; #[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] impl FromPrivateKey for SecretKey where - C: weierstrass::Curve + AlgorithmParameters + ValidatePublicKey + SecretValue, - C::Secret: Clone + Zeroize, - FieldBytes: From, + C: weierstrass::Curve + AlgorithmParameters + ValidatePublicKey, UntaggedPointSize: Add + ArrayLength, UncompressedPointSize: ArrayLength, { @@ -157,9 +155,7 @@ where #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl FromStr for SecretKey where - C: weierstrass::Curve + AlgorithmParameters + ValidatePublicKey + SecretValue, - C::Secret: Clone + Zeroize, - FieldBytes: From, + C: weierstrass::Curve + AlgorithmParameters + ValidatePublicKey, UntaggedPointSize: Add + ArrayLength, UncompressedPointSize: ArrayLength, {