Skip to content

Commit

Permalink
elliptic-curve: ecdh::SharedSecret::extract HKDF helper (#1007)
Browse files Browse the repository at this point in the history
Adds support for using HKDF to generate key material from an ECDH shared
secret using the `hkdf` crate.

Renames the previous `as_bytes` method to `raw_secret_bytes` with a
warning that the `extract` method should be preferred instead.
  • Loading branch information
tarcieri authored May 9, 2022
1 parent 4e4e69a commit 30a0487
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 27 deletions.
26 changes: 23 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion elliptic-curve/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ base64ct = { version = "1", optional = true, default-features = false }
digest = { version = "0.10", optional = true }
ff = { version = "0.12", optional = true, default-features = false }
group = { version = "0.12", optional = true, default-features = false }
hkdf = { version = "0.12", optional = true, default-features = false }
hex-literal = { version = "0.3", optional = true }
pem-rfc7468 = { version = "0.5", optional = true }
pkcs8 = { version = "0.9", optional = true, default-features = false }
Expand All @@ -48,7 +49,7 @@ arithmetic = ["ff", "group"]
bits = ["arithmetic", "ff/bits"]
dev = ["arithmetic", "hex-literal", "pem", "pkcs8"]
hash2curve = ["arithmetic", "digest"]
ecdh = ["arithmetic"]
ecdh = ["arithmetic", "digest", "hkdf"]
hazmat = []
jwk = ["alloc", "base64ct/alloc", "serde", "serde_json", "zeroize/alloc"]
pem = ["alloc", "arithmetic", "pem-rfc7468/alloc", "pkcs8", "sec1/pem"]
Expand Down
63 changes: 43 additions & 20 deletions elliptic-curve/src/ecdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ use crate::{
ProjectiveArithmetic, ProjectivePoint, PublicKey,
};
use core::borrow::Borrow;
use digest::{crypto_common::BlockSizeUser, Digest};
use group::Curve as _;
use hkdf::{hmac::SimpleHmac, Hkdf};
use rand_core::{CryptoRng, RngCore};
use zeroize::{Zeroize, ZeroizeOnDrop};

Expand Down Expand Up @@ -150,20 +152,6 @@ where
}

/// Shared secret value computed via ECDH key agreement.
///
/// This value contains the raw serialized x-coordinate of the elliptic curve
/// point computed from a Diffie-Hellman exchange.
///
/// # ⚠️ WARNING: NOT UNIFORMLY RANDOM! ⚠️
///
/// This value is not uniformly random and should not be used directly
/// as a cryptographic key for anything which requires that property
/// (e.g. symmetric ciphers).
///
/// Instead, the resulting value should be used as input to a Key Derivation
/// Function (KDF) or cryptographic hash function to produce a symmetric key.
// TODO(tarcieri): KDF traits and support for deriving uniform keys
// See: https://github.com/RustCrypto/traits/issues/5
pub struct SharedSecret<C: Curve> {
/// Computed secret value
secret_bytes: FieldBytes<C>,
Expand All @@ -181,13 +169,48 @@ impl<C: Curve> SharedSecret<C> {
}
}

/// Shared secret value, serialized as bytes.
/// Use [HKDF] (HMAC-based Extract-and-Expand Key Derivation Function) to
/// extract entropy from this shared secret.
///
/// This method can be used to transform the shared secret into uniformly
/// random values which are suitable as key material.
///
/// The `D` type parameter is a cryptographic digest function.
/// `sha2::Sha256` is a common choice for use with HKDF.
///
/// The `salt` parameter can be used to supply additional randomness.
/// Some examples include:
///
/// - randomly generated (but authenticated) string
/// - fixed application-specific value
/// - previous shared secret used for rekeying (as in TLS 1.3 and Noise)
///
/// After initializing HKDF, use [`Hkdf::expand`] to obtain output key
/// material.
///
/// [HKDF]: https://en.wikipedia.org/wiki/HKDF
pub fn extract<D>(&self, salt: Option<&[u8]>) -> Hkdf<D, SimpleHmac<D>>
where
D: BlockSizeUser + Clone + Digest,
{
Hkdf::new(salt, &self.secret_bytes)
}

/// This value contains the raw serialized x-coordinate of the elliptic curve
/// point computed from a Diffie-Hellman exchange, serialized as bytes.
///
/// When in doubt, use [`SharedSecret::extract`] instead.
///
/// # ⚠️ WARNING: NOT UNIFORMLY RANDOM! ⚠️
///
/// This value is not uniformly random and should not be used directly
/// as a cryptographic key for anything which requires that property
/// (e.g. symmetric ciphers).
///
/// As noted in the comments for this struct, this value is non-uniform and
/// should not be used directly as a symmetric encryption key, but instead
/// as input to a KDF (or failing that, a hash function) used to produce
/// a symmetric key.
pub fn as_bytes(&self) -> &FieldBytes<C> {
/// Instead, the resulting value should be used as input to a Key Derivation
/// Function (KDF) or cryptographic hash function to produce a symmetric key.
/// The [`SharedSecret::extract`] function will do this for you.
pub fn raw_secret_bytes(&self) -> &FieldBytes<C> {
&self.secret_bytes
}
}
Expand Down
4 changes: 1 addition & 3 deletions elliptic-curve/src/secret_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ pub(crate) const SEC1_PEM_TYPE_LABEL: &str = "EC PRIVATE KEY";
///
/// To decode an elliptic curve private key from PKCS#8, enable the `pkcs8`
/// feature of this crate (or the `pkcs8` feature of a specific RustCrypto
/// elliptic curve crate) and use the
/// [`DecodePrivateKey`][`elliptic_curve::pkcs8::DecodePrivateKey`]
/// trait to parse it.
/// elliptic curve crate) and use the [`DecodePrivateKey`] trait to parse it.
///
/// When the `pem` feature of this crate (or a specific RustCrypto elliptic
/// curve crate) is enabled, a [`FromStr`] impl is also available.
Expand Down

0 comments on commit 30a0487

Please sign in to comment.