From 4bf0825e10062781453076e8b6e6e5cba2d35155 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 9 Sep 2020 16:51:06 -0700 Subject: [PATCH] elliptic-curve: make SecretKey inner type generic Makes `SecretKey` a generic wrapper for an inner zeroizable type, with a blanket impl for curves with an arithmetic backend which uses `NonZeroScalar`. --- .github/workflows/elliptic-curve.yml | 2 + elliptic-curve/src/lib.rs | 9 ++- elliptic-curve/src/sec1.rs | 14 ++-- elliptic-curve/src/secret_key.rs | 102 ++++++++++++++++++++------- 4 files changed, 93 insertions(+), 34 deletions(-) diff --git a/.github/workflows/elliptic-curve.yml b/.github/workflows/elliptic-curve.yml index 65b73fe51..a7bb03a89 100644 --- a/.github/workflows/elliptic-curve.yml +++ b/.github/workflows/elliptic-curve.yml @@ -37,6 +37,8 @@ jobs: override: true - run: cargo build --no-default-features --release --target ${{ matrix.target }} - run: cargo build --no-default-features --release --target ${{ matrix.target }} --features arithmetic + - run: cargo build --no-default-features --release --target ${{ matrix.target }} --features zeroize + - run: cargo build --no-default-features --release --target ${{ matrix.target }} --features ecdh test: runs-on: ubuntu-latest strategy: diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index c1dbd3a0f..fdbeafff3 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -29,7 +29,6 @@ pub mod error; pub mod ops; pub mod point; pub mod sec1; -pub mod secret_key; pub mod util; pub mod weierstrass; @@ -41,7 +40,11 @@ pub mod scalar; #[cfg_attr(docsrs, doc(cfg(feature = "ecdh")))] pub mod ecdh; -pub use self::{error::Error, secret_key::SecretKey}; +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +pub mod secret_key; + +pub use self::error::Error; pub use generic_array::{self, typenum::consts}; pub use rand_core; @@ -61,6 +64,8 @@ pub use digest::{self, Digest}; #[cfg(feature = "oid")] pub use oid; +#[cfg(feature = "zeroize")] +pub use secret_key::SecretKey; #[cfg(feature = "zeroize")] pub use zeroize; diff --git a/elliptic-curve/src/sec1.rs b/elliptic-curve/src/sec1.rs index 7446dab34..9f687fea1 100644 --- a/elliptic-curve/src/sec1.rs +++ b/elliptic-curve/src/sec1.rs @@ -20,10 +20,10 @@ use subtle::CtOption; use alloc::boxed::Box; #[cfg(feature = "arithmetic")] -use crate::{ - ops::Mul, point::Generator, scalar::NonZeroScalar, subtle::Choice, - weierstrass::point::Decompress, Arithmetic, FromBytes, SecretKey, -}; +use crate::{subtle::Choice, weierstrass::point::Decompress, Arithmetic}; + +#[cfg(all(feature = "arithmetic", feature = "zeroize"))] +use crate::{ops::Mul, point::Generator, scalar::NonZeroScalar, secret_key::SecretKey, FromBytes}; #[cfg(feature = "zeroize")] use zeroize::Zeroize; @@ -121,14 +121,16 @@ where /// [`SecretKey`]. /// /// The `compress` flag requests point compression. - #[cfg(feature = "arithmetic")] + #[cfg(all(feature = "arithmetic", feature = "zeroize"))] #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] + #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] pub fn from_secret_key(secret_key: &SecretKey, compress: bool) -> Result where C: Arithmetic, C::AffinePoint: Mul, Output = C::AffinePoint> + ToEncodedPoint, + C::Scalar: Zeroize, { - let ct_option = C::Scalar::from_bytes(secret_key.as_bytes()).and_then(NonZeroScalar::new); + let ct_option = C::Scalar::from_bytes(&secret_key.to_bytes()).and_then(NonZeroScalar::new); if ct_option.is_none().into() { return Err(Error); diff --git a/elliptic-curve/src/secret_key.rs b/elliptic-curve/src/secret_key.rs index ae11996f7..ab92d2870 100644 --- a/elliptic-curve/src/secret_key.rs +++ b/elliptic-curve/src/secret_key.rs @@ -12,77 +12,127 @@ use core::{ convert::{TryFrom, TryInto}, fmt::{self, Debug}, }; -use generic_array::{typenum::Unsigned, GenericArray}; +use subtle::CtOption; +use zeroize::Zeroize; #[cfg(feature = "arithmetic")] -use crate::{scalar::NonZeroScalar, Arithmetic}; +use crate::{scalar::NonZeroScalar, Arithmetic, FromBytes}; #[cfg(feature = "arithmetic")] use rand_core::{CryptoRng, RngCore}; +/// Inner value stored by a [`SecretKey`]. +pub trait SecretValue: Curve { + /// Inner secret value + type Secret: Into> + Zeroize; + + /// Parse the secret value from bytes + fn from_secret_bytes(bytes: &FieldBytes) -> CtOption; +} + +#[cfg(feature = "arithmetic")] +impl SecretValue for C +where + C::Scalar: Zeroize, +{ + type Secret = NonZeroScalar; + + fn from_secret_bytes(bytes: &FieldBytes) -> CtOption> { + NonZeroScalar::from_bytes(bytes) + } +} + /// Elliptic curve secret keys. /// /// This type wraps a serialized scalar value, helping to prevent accidental /// exposure and securely erasing the value from memory when dropped /// (when the `zeroize` feature of this crate is enabled). #[derive(Clone)] -pub struct SecretKey { - /// Private scalar value - scalar: FieldBytes, +pub struct SecretKey { + /// Secret value (i.e. secret scalar) + secret_value: C::Secret, } -impl SecretKey { +impl SecretKey +where + C: Curve + SecretValue, + C::Secret: Clone + Zeroize, + FieldBytes: From, +{ /// Generate a random [`SecretKey`] #[cfg(feature = "arithmetic")] #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] pub fn random(rng: impl CryptoRng + RngCore) -> Self where - C: Arithmetic, + C: Arithmetic + SecretValue>, { Self { - scalar: NonZeroScalar::::random(rng).into(), + secret_value: NonZeroScalar::::random(rng), } } /// Create a new secret key from a serialized scalar value - pub fn new(bytes: FieldBytes) -> Self { - Self { scalar: bytes } + pub fn new(secret_value: C::Secret) -> Self { + Self { secret_value } } /// Deserialize this secret key from a bytestring pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result { - bytes.as_ref().try_into() + bytes + .as_ref() + .try_into() + .ok() + .and_then(|bytes| C::from_secret_bytes(bytes).into()) + .map(|secret_value| SecretKey { secret_value }) + .ok_or(Error) } /// Expose the byte serialization of the value this [`SecretKey`] wraps - pub fn as_bytes(&self) -> &FieldBytes { - &self.scalar + pub fn to_bytes(&self) -> FieldBytes { + self.secret_value.clone().into() + } + + /// Borrow the inner secret scalar value. + /// + /// # Notice + /// + /// This value is key material. Please treat it accordingly! + #[cfg(feature = "arithmetic")] + #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] + pub fn secret_scalar(&self) -> &NonZeroScalar + where + C: Arithmetic + SecretValue>, + { + &self.secret_value } } -impl TryFrom<&[u8]> for SecretKey { +impl TryFrom<&[u8]> for SecretKey +where + C: Curve + SecretValue, + C::Secret: Clone + Zeroize, + FieldBytes: From, +{ type Error = Error; fn try_from(slice: &[u8]) -> Result { - if slice.len() == C::FieldSize::to_usize() { - Ok(SecretKey { - scalar: GenericArray::clone_from_slice(slice), - }) - } else { - Err(Error) - } + Self::from_bytes(slice) } } -impl Debug for SecretKey { +impl Debug for SecretKey +where + C: Curve + SecretValue, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "SecretKey<{:?}>{{ ... }}", C::default()) } } -#[cfg(feature = "zeroize")] -impl Drop for SecretKey { +impl Drop for SecretKey +where + C: Curve + SecretValue, +{ fn drop(&mut self) { - use zeroize::Zeroize; - self.scalar.zeroize(); + self.secret_value.zeroize(); } }