summaryrefslogtreecommitdiffstats
path: root/vendor/elliptic-curve/src/ecdh.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/elliptic-curve/src/ecdh.rs')
-rw-r--r--vendor/elliptic-curve/src/ecdh.rs236
1 files changed, 236 insertions, 0 deletions
diff --git a/vendor/elliptic-curve/src/ecdh.rs b/vendor/elliptic-curve/src/ecdh.rs
new file mode 100644
index 0000000..c64a696
--- /dev/null
+++ b/vendor/elliptic-curve/src/ecdh.rs
@@ -0,0 +1,236 @@
+//! Elliptic Curve Diffie-Hellman Support.
+//!
+//! This module contains a generic ECDH implementation which is usable with
+//! any elliptic curve which implements the [`CurveArithmetic`] trait (presently
+//! the `k256` and `p256` crates)
+//!
+//! # ECDH Ephemeral (ECDHE) Usage
+//!
+//! Ephemeral Diffie-Hellman provides a one-time key exchange between two peers
+//! using a randomly generated set of keys for each exchange.
+//!
+//! In practice ECDHE is used as part of an [Authenticated Key Exchange (AKE)][AKE]
+//! protocol (e.g. [SIGMA]), where an existing cryptographic trust relationship
+//! can be used to determine the authenticity of the ephemeral keys, such as
+//! a digital signature. Without such an additional step, ECDHE is insecure!
+//! (see security warning below)
+//!
+//! See the documentation for the [`EphemeralSecret`] type for more information
+//! on performing ECDH ephemeral key exchanges.
+//!
+//! # Static ECDH Usage
+//!
+//! Static ECDH key exchanges are supported via the low-level
+//! [`diffie_hellman`] function.
+//!
+//! [AKE]: https://en.wikipedia.org/wiki/Authenticated_Key_Exchange
+//! [SIGMA]: https://webee.technion.ac.il/~hugo/sigma-pdf.pdf
+
+use crate::{
+ point::AffineCoordinates, AffinePoint, Curve, CurveArithmetic, FieldBytes, NonZeroScalar,
+ ProjectivePoint, PublicKey,
+};
+use core::borrow::Borrow;
+use digest::{crypto_common::BlockSizeUser, Digest};
+use group::Curve as _;
+use hkdf::{hmac::SimpleHmac, Hkdf};
+use rand_core::CryptoRngCore;
+use zeroize::{Zeroize, ZeroizeOnDrop};
+
+/// Low-level Elliptic Curve Diffie-Hellman (ECDH) function.
+///
+/// Whenever possible, we recommend using the high-level ECDH ephemeral API
+/// provided by [`EphemeralSecret`].
+///
+/// However, if you are implementing a protocol which requires a static scalar
+/// value as part of an ECDH exchange, this API can be used to compute a
+/// [`SharedSecret`] from that value.
+///
+/// Note that this API operates on the low-level [`NonZeroScalar`] and
+/// [`AffinePoint`] types. If you are attempting to use the higher-level
+/// [`SecretKey`][`crate::SecretKey`] and [`PublicKey`] types, you will
+/// need to use the following conversions:
+///
+/// ```ignore
+/// let shared_secret = elliptic_curve::ecdh::diffie_hellman(
+/// secret_key.to_nonzero_scalar(),
+/// public_key.as_affine()
+/// );
+/// ```
+pub fn diffie_hellman<C>(
+ secret_key: impl Borrow<NonZeroScalar<C>>,
+ public_key: impl Borrow<AffinePoint<C>>,
+) -> SharedSecret<C>
+where
+ C: CurveArithmetic,
+{
+ let public_point = ProjectivePoint::<C>::from(*public_key.borrow());
+ let secret_point = (public_point * secret_key.borrow().as_ref()).to_affine();
+ SharedSecret::new(secret_point)
+}
+
+/// Ephemeral Diffie-Hellman Secret.
+///
+/// These are ephemeral "secret key" values which are deliberately designed
+/// to avoid being persisted.
+///
+/// To perform an ephemeral Diffie-Hellman exchange, do the following:
+///
+/// - Have each participant generate an [`EphemeralSecret`] value
+/// - Compute the [`PublicKey`] for that value
+/// - Have each peer provide their [`PublicKey`] to their counterpart
+/// - Use [`EphemeralSecret`] and the other participant's [`PublicKey`]
+/// to compute a [`SharedSecret`] value.
+///
+/// # ⚠️ SECURITY WARNING ⚠️
+///
+/// Ephemeral Diffie-Hellman exchanges are unauthenticated and without a
+/// further authentication step are trivially vulnerable to man-in-the-middle
+/// attacks!
+///
+/// These exchanges should be performed in the context of a protocol which
+/// takes further steps to authenticate the peers in a key exchange.
+pub struct EphemeralSecret<C>
+where
+ C: CurveArithmetic,
+{
+ scalar: NonZeroScalar<C>,
+}
+
+impl<C> EphemeralSecret<C>
+where
+ C: CurveArithmetic,
+{
+ /// Generate a cryptographically random [`EphemeralSecret`].
+ pub fn random(rng: &mut impl CryptoRngCore) -> Self {
+ Self {
+ scalar: NonZeroScalar::random(rng),
+ }
+ }
+
+ /// Get the public key associated with this ephemeral secret.
+ ///
+ /// The `compress` flag enables point compression.
+ pub fn public_key(&self) -> PublicKey<C> {
+ PublicKey::from_secret_scalar(&self.scalar)
+ }
+
+ /// Compute a Diffie-Hellman shared secret from an ephemeral secret and the
+ /// public key of the other participant in the exchange.
+ pub fn diffie_hellman(&self, public_key: &PublicKey<C>) -> SharedSecret<C> {
+ diffie_hellman(self.scalar, public_key.as_affine())
+ }
+}
+
+impl<C> From<&EphemeralSecret<C>> for PublicKey<C>
+where
+ C: CurveArithmetic,
+{
+ fn from(ephemeral_secret: &EphemeralSecret<C>) -> Self {
+ ephemeral_secret.public_key()
+ }
+}
+
+impl<C> Zeroize for EphemeralSecret<C>
+where
+ C: CurveArithmetic,
+{
+ fn zeroize(&mut self) {
+ self.scalar.zeroize()
+ }
+}
+
+impl<C> ZeroizeOnDrop for EphemeralSecret<C> where C: CurveArithmetic {}
+
+impl<C> Drop for EphemeralSecret<C>
+where
+ C: CurveArithmetic,
+{
+ fn drop(&mut self) {
+ self.zeroize();
+ }
+}
+
+/// Shared secret value computed via ECDH key agreement.
+pub struct SharedSecret<C: Curve> {
+ /// Computed secret value
+ secret_bytes: FieldBytes<C>,
+}
+
+impl<C: Curve> SharedSecret<C> {
+ /// Create a new [`SharedSecret`] from an [`AffinePoint`] for this curve.
+ #[inline]
+ fn new(point: AffinePoint<C>) -> Self
+ where
+ C: CurveArithmetic,
+ {
+ Self {
+ secret_bytes: point.x(),
+ }
+ }
+
+ /// 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).
+ ///
+ /// 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
+ }
+}
+
+impl<C: Curve> From<FieldBytes<C>> for SharedSecret<C> {
+ /// NOTE: this impl is intended to be used by curve implementations to
+ /// instantiate a [`SharedSecret`] value from their respective
+ /// [`AffinePoint`] type.
+ ///
+ /// Curve implementations should provide the field element representing
+ /// the affine x-coordinate as `secret_bytes`.
+ fn from(secret_bytes: FieldBytes<C>) -> Self {
+ Self { secret_bytes }
+ }
+}
+
+impl<C: Curve> ZeroizeOnDrop for SharedSecret<C> {}
+
+impl<C: Curve> Drop for SharedSecret<C> {
+ fn drop(&mut self) {
+ self.secret_bytes.zeroize()
+ }
+}