/rust/registry/src/index.crates.io-1949cf8c6b5b557f/elliptic-curve-0.13.8/src/ecdh.rs
Line | Count | Source |
1 | | //! Elliptic Curve Diffie-Hellman Support. |
2 | | //! |
3 | | //! This module contains a generic ECDH implementation which is usable with |
4 | | //! any elliptic curve which implements the [`CurveArithmetic`] trait (presently |
5 | | //! the `k256` and `p256` crates) |
6 | | //! |
7 | | //! # ECDH Ephemeral (ECDHE) Usage |
8 | | //! |
9 | | //! Ephemeral Diffie-Hellman provides a one-time key exchange between two peers |
10 | | //! using a randomly generated set of keys for each exchange. |
11 | | //! |
12 | | //! In practice ECDHE is used as part of an [Authenticated Key Exchange (AKE)][AKE] |
13 | | //! protocol (e.g. [SIGMA]), where an existing cryptographic trust relationship |
14 | | //! can be used to determine the authenticity of the ephemeral keys, such as |
15 | | //! a digital signature. Without such an additional step, ECDHE is insecure! |
16 | | //! (see security warning below) |
17 | | //! |
18 | | //! See the documentation for the [`EphemeralSecret`] type for more information |
19 | | //! on performing ECDH ephemeral key exchanges. |
20 | | //! |
21 | | //! # Static ECDH Usage |
22 | | //! |
23 | | //! Static ECDH key exchanges are supported via the low-level |
24 | | //! [`diffie_hellman`] function. |
25 | | //! |
26 | | //! [AKE]: https://en.wikipedia.org/wiki/Authenticated_Key_Exchange |
27 | | //! [SIGMA]: https://webee.technion.ac.il/~hugo/sigma-pdf.pdf |
28 | | |
29 | | use crate::{ |
30 | | point::AffineCoordinates, AffinePoint, Curve, CurveArithmetic, FieldBytes, NonZeroScalar, |
31 | | ProjectivePoint, PublicKey, |
32 | | }; |
33 | | use core::borrow::Borrow; |
34 | | use digest::{crypto_common::BlockSizeUser, Digest}; |
35 | | use group::Curve as _; |
36 | | use hkdf::{hmac::SimpleHmac, Hkdf}; |
37 | | use rand_core::CryptoRngCore; |
38 | | use zeroize::{Zeroize, ZeroizeOnDrop}; |
39 | | |
40 | | /// Low-level Elliptic Curve Diffie-Hellman (ECDH) function. |
41 | | /// |
42 | | /// Whenever possible, we recommend using the high-level ECDH ephemeral API |
43 | | /// provided by [`EphemeralSecret`]. |
44 | | /// |
45 | | /// However, if you are implementing a protocol which requires a static scalar |
46 | | /// value as part of an ECDH exchange, this API can be used to compute a |
47 | | /// [`SharedSecret`] from that value. |
48 | | /// |
49 | | /// Note that this API operates on the low-level [`NonZeroScalar`] and |
50 | | /// [`AffinePoint`] types. If you are attempting to use the higher-level |
51 | | /// [`SecretKey`][`crate::SecretKey`] and [`PublicKey`] types, you will |
52 | | /// need to use the following conversions: |
53 | | /// |
54 | | /// ```ignore |
55 | | /// let shared_secret = elliptic_curve::ecdh::diffie_hellman( |
56 | | /// secret_key.to_nonzero_scalar(), |
57 | | /// public_key.as_affine() |
58 | | /// ); |
59 | | /// ``` |
60 | 0 | pub fn diffie_hellman<C>( |
61 | 0 | secret_key: impl Borrow<NonZeroScalar<C>>, |
62 | 0 | public_key: impl Borrow<AffinePoint<C>>, |
63 | 0 | ) -> SharedSecret<C> |
64 | 0 | where |
65 | 0 | C: CurveArithmetic, |
66 | | { |
67 | 0 | let public_point = ProjectivePoint::<C>::from(*public_key.borrow()); |
68 | 0 | let secret_point = (public_point * secret_key.borrow().as_ref()).to_affine(); |
69 | 0 | SharedSecret::new(secret_point) |
70 | 0 | } |
71 | | |
72 | | /// Ephemeral Diffie-Hellman Secret. |
73 | | /// |
74 | | /// These are ephemeral "secret key" values which are deliberately designed |
75 | | /// to avoid being persisted. |
76 | | /// |
77 | | /// To perform an ephemeral Diffie-Hellman exchange, do the following: |
78 | | /// |
79 | | /// - Have each participant generate an [`EphemeralSecret`] value |
80 | | /// - Compute the [`PublicKey`] for that value |
81 | | /// - Have each peer provide their [`PublicKey`] to their counterpart |
82 | | /// - Use [`EphemeralSecret`] and the other participant's [`PublicKey`] |
83 | | /// to compute a [`SharedSecret`] value. |
84 | | /// |
85 | | /// # ⚠️ SECURITY WARNING ⚠️ |
86 | | /// |
87 | | /// Ephemeral Diffie-Hellman exchanges are unauthenticated and without a |
88 | | /// further authentication step are trivially vulnerable to man-in-the-middle |
89 | | /// attacks! |
90 | | /// |
91 | | /// These exchanges should be performed in the context of a protocol which |
92 | | /// takes further steps to authenticate the peers in a key exchange. |
93 | | pub struct EphemeralSecret<C> |
94 | | where |
95 | | C: CurveArithmetic, |
96 | | { |
97 | | scalar: NonZeroScalar<C>, |
98 | | } |
99 | | |
100 | | impl<C> EphemeralSecret<C> |
101 | | where |
102 | | C: CurveArithmetic, |
103 | | { |
104 | | /// Generate a cryptographically random [`EphemeralSecret`]. |
105 | 39.7k | pub fn random(rng: &mut impl CryptoRngCore) -> Self { |
106 | 39.7k | Self { |
107 | 39.7k | scalar: NonZeroScalar::random(rng), |
108 | 39.7k | } |
109 | 39.7k | } |
110 | | |
111 | | /// Get the public key associated with this ephemeral secret. |
112 | | /// |
113 | | /// The `compress` flag enables point compression. |
114 | 1.85k | pub fn public_key(&self) -> PublicKey<C> { |
115 | 1.85k | PublicKey::from_secret_scalar(&self.scalar) |
116 | 1.85k | } |
117 | | |
118 | | /// Compute a Diffie-Hellman shared secret from an ephemeral secret and the |
119 | | /// public key of the other participant in the exchange. |
120 | 0 | pub fn diffie_hellman(&self, public_key: &PublicKey<C>) -> SharedSecret<C> { |
121 | 0 | diffie_hellman(self.scalar, public_key.as_affine()) |
122 | 0 | } |
123 | | } |
124 | | |
125 | | impl<C> From<&EphemeralSecret<C>> for PublicKey<C> |
126 | | where |
127 | | C: CurveArithmetic, |
128 | | { |
129 | | fn from(ephemeral_secret: &EphemeralSecret<C>) -> Self { |
130 | | ephemeral_secret.public_key() |
131 | | } |
132 | | } |
133 | | |
134 | | impl<C> Zeroize for EphemeralSecret<C> |
135 | | where |
136 | | C: CurveArithmetic, |
137 | | { |
138 | 39.7k | fn zeroize(&mut self) { |
139 | 39.7k | self.scalar.zeroize() |
140 | 39.7k | } |
141 | | } |
142 | | |
143 | | impl<C> ZeroizeOnDrop for EphemeralSecret<C> where C: CurveArithmetic {} |
144 | | |
145 | | impl<C> Drop for EphemeralSecret<C> |
146 | | where |
147 | | C: CurveArithmetic, |
148 | | { |
149 | 39.7k | fn drop(&mut self) { |
150 | 39.7k | self.zeroize(); |
151 | 39.7k | } |
152 | | } |
153 | | |
154 | | /// Shared secret value computed via ECDH key agreement. |
155 | | pub struct SharedSecret<C: Curve> { |
156 | | /// Computed secret value |
157 | | secret_bytes: FieldBytes<C>, |
158 | | } |
159 | | |
160 | | impl<C: Curve> SharedSecret<C> { |
161 | | /// Create a new [`SharedSecret`] from an [`AffinePoint`] for this curve. |
162 | | #[inline] |
163 | 0 | fn new(point: AffinePoint<C>) -> Self |
164 | 0 | where |
165 | 0 | C: CurveArithmetic, |
166 | | { |
167 | 0 | Self { |
168 | 0 | secret_bytes: point.x(), |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | | /// Use [HKDF] (HMAC-based Extract-and-Expand Key Derivation Function) to |
173 | | /// extract entropy from this shared secret. |
174 | | /// |
175 | | /// This method can be used to transform the shared secret into uniformly |
176 | | /// random values which are suitable as key material. |
177 | | /// |
178 | | /// The `D` type parameter is a cryptographic digest function. |
179 | | /// `sha2::Sha256` is a common choice for use with HKDF. |
180 | | /// |
181 | | /// The `salt` parameter can be used to supply additional randomness. |
182 | | /// Some examples include: |
183 | | /// |
184 | | /// - randomly generated (but authenticated) string |
185 | | /// - fixed application-specific value |
186 | | /// - previous shared secret used for rekeying (as in TLS 1.3 and Noise) |
187 | | /// |
188 | | /// After initializing HKDF, use [`Hkdf::expand`] to obtain output key |
189 | | /// material. |
190 | | /// |
191 | | /// [HKDF]: https://en.wikipedia.org/wiki/HKDF |
192 | | pub fn extract<D>(&self, salt: Option<&[u8]>) -> Hkdf<D, SimpleHmac<D>> |
193 | | where |
194 | | D: BlockSizeUser + Clone + Digest, |
195 | | { |
196 | | Hkdf::new(salt, &self.secret_bytes) |
197 | | } |
198 | | |
199 | | /// This value contains the raw serialized x-coordinate of the elliptic curve |
200 | | /// point computed from a Diffie-Hellman exchange, serialized as bytes. |
201 | | /// |
202 | | /// When in doubt, use [`SharedSecret::extract`] instead. |
203 | | /// |
204 | | /// # ⚠️ WARNING: NOT UNIFORMLY RANDOM! ⚠️ |
205 | | /// |
206 | | /// This value is not uniformly random and should not be used directly |
207 | | /// as a cryptographic key for anything which requires that property |
208 | | /// (e.g. symmetric ciphers). |
209 | | /// |
210 | | /// Instead, the resulting value should be used as input to a Key Derivation |
211 | | /// Function (KDF) or cryptographic hash function to produce a symmetric key. |
212 | | /// The [`SharedSecret::extract`] function will do this for you. |
213 | 0 | pub fn raw_secret_bytes(&self) -> &FieldBytes<C> { |
214 | 0 | &self.secret_bytes |
215 | 0 | } |
216 | | } |
217 | | |
218 | | impl<C: Curve> From<FieldBytes<C>> for SharedSecret<C> { |
219 | | /// NOTE: this impl is intended to be used by curve implementations to |
220 | | /// instantiate a [`SharedSecret`] value from their respective |
221 | | /// [`AffinePoint`] type. |
222 | | /// |
223 | | /// Curve implementations should provide the field element representing |
224 | | /// the affine x-coordinate as `secret_bytes`. |
225 | | fn from(secret_bytes: FieldBytes<C>) -> Self { |
226 | | Self { secret_bytes } |
227 | | } |
228 | | } |
229 | | |
230 | | impl<C: Curve> ZeroizeOnDrop for SharedSecret<C> {} |
231 | | |
232 | | impl<C: Curve> Drop for SharedSecret<C> { |
233 | 0 | fn drop(&mut self) { |
234 | 0 | self.secret_bytes.zeroize() |
235 | 0 | } |
236 | | } |