Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/elliptic-curve.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
9 changes: 7 additions & 2 deletions elliptic-curve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;

Expand Down
14 changes: 8 additions & 6 deletions elliptic-curve/src/sec1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<C>, compress: bool) -> Result<Self, Error>
where
C: Arithmetic,
C::AffinePoint: Mul<NonZeroScalar<C>, Output = C::AffinePoint> + ToEncodedPoint<C>,
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);
Expand Down
102 changes: 76 additions & 26 deletions elliptic-curve/src/secret_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FieldBytes<Self>> + Zeroize;

/// Parse the secret value from bytes
fn from_secret_bytes(bytes: &FieldBytes<Self>) -> CtOption<Self::Secret>;
}

#[cfg(feature = "arithmetic")]
impl<C: Curve + Arithmetic> SecretValue for C
where
C::Scalar: Zeroize,
{
type Secret = NonZeroScalar<C>;

fn from_secret_bytes(bytes: &FieldBytes<C>) -> CtOption<NonZeroScalar<C>> {
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<C: Curve> {
/// Private scalar value
scalar: FieldBytes<C>,
pub struct SecretKey<C: Curve + SecretValue> {
/// Secret value (i.e. secret scalar)
secret_value: C::Secret,
}

impl<C: Curve> SecretKey<C> {
impl<C> SecretKey<C>
where
C: Curve + SecretValue,
C::Secret: Clone + Zeroize,
FieldBytes<C>: From<C::Secret>,
{
/// 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<Secret = NonZeroScalar<C>>,
{
Self {
scalar: NonZeroScalar::<C>::random(rng).into(),
secret_value: NonZeroScalar::<C>::random(rng),
}
}

/// Create a new secret key from a serialized scalar value
pub fn new(bytes: FieldBytes<C>) -> 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<Self, Error> {
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<C> {
&self.scalar
pub fn to_bytes(&self) -> FieldBytes<C> {
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<C>
where
C: Arithmetic + SecretValue<Secret = NonZeroScalar<C>>,
{
&self.secret_value
}
}

impl<C: Curve> TryFrom<&[u8]> for SecretKey<C> {
impl<C> TryFrom<&[u8]> for SecretKey<C>
where
C: Curve + SecretValue,
C::Secret: Clone + Zeroize,
FieldBytes<C>: From<C::Secret>,
{
type Error = Error;

fn try_from(slice: &[u8]) -> Result<Self, Error> {
if slice.len() == C::FieldSize::to_usize() {
Ok(SecretKey {
scalar: GenericArray::clone_from_slice(slice),
})
} else {
Err(Error)
}
Self::from_bytes(slice)
}
}

impl<C: Curve> Debug for SecretKey<C> {
impl<C> Debug for SecretKey<C>
where
C: Curve + SecretValue,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SecretKey<{:?}>{{ ... }}", C::default())
}
}

#[cfg(feature = "zeroize")]
impl<C: Curve> Drop for SecretKey<C> {
impl<C> Drop for SecretKey<C>
where
C: Curve + SecretValue,
{
fn drop(&mut self) {
use zeroize::Zeroize;
self.scalar.zeroize();
self.secret_value.zeroize();
}
}