x25519_dalek/
x25519.rs

1// -*- mode: rust; -*-
2//
3// This file is part of x25519-dalek.
4// Copyright (c) 2017-2021 isis lovecruft
5// Copyright (c) 2019-2021 DebugSteven
6// See LICENSE for licensing information.
7//
8// Authors:
9// - isis agora lovecruft <isis@patternsinthevoid.net>
10// - DebugSteven <debugsteven@gmail.com>
11
12//! x25519 Diffie-Hellman key exchange
13//!
14//! This implements x25519 key exchange as specified by Mike Hamburg
15//! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748).
16
17use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint, traits::IsIdentity};
18
19use rand_core::CryptoRng;
20use rand_core::RngCore;
21
22#[cfg(feature = "zeroize")]
23use zeroize::Zeroize;
24
25/// A Diffie-Hellman public key
26///
27/// We implement `Zeroize` so that downstream consumers may derive it for `Drop`
28/// should they wish to erase public keys from memory.  Note that this erasure
29/// (in this crate) does *not* automatically happen, but either must be derived
30/// for Drop or explicitly called.
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32#[cfg_attr(feature = "zeroize", derive(Zeroize))]
33#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
34pub struct PublicKey(pub(crate) MontgomeryPoint);
35
36impl From<[u8; 32]> for PublicKey {
37    /// Given a byte array, construct a x25519 `PublicKey`.
38    fn from(bytes: [u8; 32]) -> PublicKey {
39        PublicKey(MontgomeryPoint(bytes))
40    }
41}
42
43impl PublicKey {
44    /// Convert this public key to a byte array.
45    #[inline]
46    pub fn to_bytes(&self) -> [u8; 32] {
47        self.0.to_bytes()
48    }
49
50    /// View this public key as a byte array.
51    #[inline]
52    pub fn as_bytes(&self) -> &[u8; 32] {
53        self.0.as_bytes()
54    }
55}
56
57impl AsRef<[u8]> for PublicKey {
58    /// View this public key as a byte array.
59    #[inline]
60    fn as_ref(&self) -> &[u8] {
61        self.as_bytes()
62    }
63}
64
65/// A short-lived Diffie-Hellman secret key that can only be used to compute a single
66/// [`SharedSecret`].
67///
68/// This type is identical to the `StaticSecret` type, except that the
69/// [`EphemeralSecret::diffie_hellman`] method consumes and then wipes the secret key, and there
70/// are no serialization methods defined.  This means that [`EphemeralSecret`]s can only be
71/// generated from fresh randomness where the compiler statically checks that the resulting
72/// secret is used at most once.
73#[cfg_attr(feature = "zeroize", derive(Zeroize))]
74#[cfg_attr(feature = "zeroize", zeroize(drop))]
75pub struct EphemeralSecret(pub(crate) [u8; 32]);
76
77impl EphemeralSecret {
78    /// Perform a Diffie-Hellman key agreement between `self` and
79    /// `their_public` key to produce a [`SharedSecret`].
80    pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
81        SharedSecret(their_public.0.mul_clamped(self.0))
82    }
83
84    /// Generate a new [`EphemeralSecret`] with the supplied RNG.
85    #[deprecated(
86        since = "2.0.0",
87        note = "Renamed to `random_from_rng`. This will be removed in 2.1.0"
88    )]
89    pub fn new<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
90        Self::random_from_rng(&mut csprng)
91    }
92
93    /// Generate a new [`EphemeralSecret`] with the supplied RNG.
94    pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
95        // The secret key is random bytes. Clamping is done later.
96        let mut bytes = [0u8; 32];
97        csprng.fill_bytes(&mut bytes);
98        EphemeralSecret(bytes)
99    }
100
101    /// Generate a new [`EphemeralSecret`].
102    #[cfg(feature = "getrandom")]
103    pub fn random() -> Self {
104        Self::random_from_rng(rand_core::OsRng)
105    }
106}
107
108impl<'a> From<&'a EphemeralSecret> for PublicKey {
109    /// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`].
110    fn from(secret: &'a EphemeralSecret) -> PublicKey {
111        PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery())
112    }
113}
114
115/// A Diffie-Hellman secret key which may be used more than once, but is
116/// purposefully not serialiseable in order to discourage key-reuse.  This is
117/// implemented to facilitate protocols such as Noise (e.g. Noise IK key usage,
118/// etc.) and X3DH which require an "ephemeral" key to conduct the
119/// Diffie-Hellman operation multiple times throughout the protocol, while the
120/// protocol run at a higher level is only conducted once per key.
121///
122/// Similarly to [`EphemeralSecret`], this type does _not_ have serialisation
123/// methods, in order to discourage long-term usage of secret key material. (For
124/// long-term secret keys, see `StaticSecret`.)
125///
126/// # Warning
127///
128/// If you're uncertain about whether you should use this, then you likely
129/// should not be using this.  Our strongly recommended advice is to use
130/// [`EphemeralSecret`] at all times, as that type enforces at compile-time that
131/// secret keys are never reused, which can have very serious security
132/// implications for many protocols.
133#[cfg(feature = "reusable_secrets")]
134#[cfg_attr(feature = "zeroize", derive(Zeroize))]
135#[cfg_attr(feature = "zeroize", zeroize(drop))]
136#[derive(Clone)]
137pub struct ReusableSecret(pub(crate) [u8; 32]);
138
139#[cfg(feature = "reusable_secrets")]
140impl ReusableSecret {
141    /// Perform a Diffie-Hellman key agreement between `self` and
142    /// `their_public` key to produce a [`SharedSecret`].
143    pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
144        SharedSecret(their_public.0.mul_clamped(self.0))
145    }
146
147    /// Generate a new [`ReusableSecret`] with the supplied RNG.
148    #[deprecated(
149        since = "2.0.0",
150        note = "Renamed to `random_from_rng`. This will be removed in 2.1.0."
151    )]
152    pub fn new<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
153        Self::random_from_rng(&mut csprng)
154    }
155
156    /// Generate a new [`ReusableSecret`] with the supplied RNG.
157    pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
158        // The secret key is random bytes. Clamping is done later.
159        let mut bytes = [0u8; 32];
160        csprng.fill_bytes(&mut bytes);
161        ReusableSecret(bytes)
162    }
163
164    /// Generate a new [`ReusableSecret`].
165    #[cfg(feature = "getrandom")]
166    pub fn random() -> Self {
167        Self::random_from_rng(rand_core::OsRng)
168    }
169}
170
171#[cfg(feature = "reusable_secrets")]
172impl<'a> From<&'a ReusableSecret> for PublicKey {
173    /// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`].
174    fn from(secret: &'a ReusableSecret) -> PublicKey {
175        PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery())
176    }
177}
178
179/// A Diffie-Hellman secret key that can be used to compute multiple [`SharedSecret`]s.
180///
181/// This type is identical to the [`EphemeralSecret`] type, except that the
182/// [`StaticSecret::diffie_hellman`] method does not consume the secret key, and the type provides
183/// serialization methods to save and load key material.  This means that the secret may be used
184/// multiple times (but does not *have to be*).
185///
186/// # Warning
187///
188/// If you're uncertain about whether you should use this, then you likely
189/// should not be using this.  Our strongly recommended advice is to use
190/// [`EphemeralSecret`] at all times, as that type enforces at compile-time that
191/// secret keys are never reused, which can have very serious security
192/// implications for many protocols.
193#[cfg(feature = "static_secrets")]
194#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
195#[cfg_attr(feature = "zeroize", derive(Zeroize))]
196#[cfg_attr(feature = "zeroize", zeroize(drop))]
197#[derive(Clone)]
198pub struct StaticSecret([u8; 32]);
199
200#[cfg(feature = "static_secrets")]
201impl StaticSecret {
202    /// Perform a Diffie-Hellman key agreement between `self` and
203    /// `their_public` key to produce a `SharedSecret`.
204    pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
205        SharedSecret(their_public.0.mul_clamped(self.0))
206    }
207
208    /// Generate a new [`StaticSecret`] with the supplied RNG.
209    #[deprecated(
210        since = "2.0.0",
211        note = "Renamed to `random_from_rng`. This will be removed in 2.1.0"
212    )]
213    pub fn new<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
214        Self::random_from_rng(&mut csprng)
215    }
216
217    /// Generate a new [`StaticSecret`] with the supplied RNG.
218    pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
219        // The secret key is random bytes. Clamping is done later.
220        let mut bytes = [0u8; 32];
221        csprng.fill_bytes(&mut bytes);
222        StaticSecret(bytes)
223    }
224
225    /// Generate a new [`StaticSecret`].
226    #[cfg(feature = "getrandom")]
227    pub fn random() -> Self {
228        Self::random_from_rng(rand_core::OsRng)
229    }
230
231    /// Extract this key's bytes for serialization.
232    #[inline]
233    pub fn to_bytes(&self) -> [u8; 32] {
234        self.0
235    }
236
237    /// View this key as a byte array.
238    #[inline]
239    pub fn as_bytes(&self) -> &[u8; 32] {
240        &self.0
241    }
242}
243
244#[cfg(feature = "static_secrets")]
245impl From<[u8; 32]> for StaticSecret {
246    /// Load a secret key from a byte array.
247    fn from(bytes: [u8; 32]) -> StaticSecret {
248        StaticSecret(bytes)
249    }
250}
251
252#[cfg(feature = "static_secrets")]
253impl<'a> From<&'a StaticSecret> for PublicKey {
254    /// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`].
255    fn from(secret: &'a StaticSecret) -> PublicKey {
256        PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery())
257    }
258}
259
260#[cfg(feature = "static_secrets")]
261impl AsRef<[u8]> for StaticSecret {
262    /// View this key as a byte array.
263    #[inline]
264    fn as_ref(&self) -> &[u8] {
265        self.as_bytes()
266    }
267}
268
269/// The result of a Diffie-Hellman key exchange.
270///
271/// Each party computes this using their [`EphemeralSecret`] or [`StaticSecret`] and their
272/// counterparty's [`PublicKey`].
273#[cfg_attr(feature = "zeroize", derive(Zeroize))]
274#[cfg_attr(feature = "zeroize", zeroize(drop))]
275pub struct SharedSecret(pub(crate) MontgomeryPoint);
276
277impl SharedSecret {
278    /// Convert this shared secret to a byte array.
279    #[inline]
280    pub fn to_bytes(&self) -> [u8; 32] {
281        self.0.to_bytes()
282    }
283
284    /// View this shared secret key as a byte array.
285    #[inline]
286    pub fn as_bytes(&self) -> &[u8; 32] {
287        self.0.as_bytes()
288    }
289
290    /// Ensure in constant-time that this shared secret did not result from a
291    /// key exchange with non-contributory behaviour.
292    ///
293    /// In some more exotic protocols which need to guarantee "contributory"
294    /// behaviour for both parties, that is, that each party contributed a public
295    /// value which increased the security of the resulting shared secret.
296    /// To take an example protocol attack where this could lead to undesirable
297    /// results [from Thái "thaidn" Dương](https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html):
298    ///
299    /// > If Mallory replaces Alice's and Bob's public keys with zero, which is
300    /// > a valid Curve25519 public key, he would be able to force the ECDH
301    /// > shared value to be zero, which is the encoding of the point at infinity,
302    /// > and thus get to dictate some publicly known values as the shared
303    /// > keys. It still requires an active man-in-the-middle attack to pull the
304    /// > trick, after which, however, not only Mallory can decode Alice's data,
305    /// > but everyone too! It is also impossible for Alice and Bob to detect the
306    /// > intrusion, as they still share the same keys, and can communicate with
307    /// > each other as normal.
308    ///
309    /// The original Curve25519 specification argues that checks for
310    /// non-contributory behaviour are "unnecessary for Diffie-Hellman".
311    /// Whether this check is necessary for any particular given protocol is
312    /// often a matter of debate, which we will not re-hash here, but simply
313    /// cite some of the [relevant] [public] [discussions].
314    ///
315    /// # Returns
316    ///
317    /// Returns `true` if the key exchange was contributory (good), and `false`
318    /// otherwise (can be bad for some protocols).
319    ///
320    /// [relevant]: https://tools.ietf.org/html/rfc7748#page-15
321    /// [public]: https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html
322    /// [discussions]: https://vnhacker.blogspot.com/2016/08/the-internet-of-broken-protocols.html
323    #[must_use]
324    pub fn was_contributory(&self) -> bool {
325        !self.0.is_identity()
326    }
327}
328
329impl AsRef<[u8]> for SharedSecret {
330    /// View this shared secret key as a byte array.
331    #[inline]
332    fn as_ref(&self) -> &[u8] {
333        self.as_bytes()
334    }
335}
336
337/// The bare, byte-oriented x25519 function, exactly as specified in RFC7748.
338///
339/// This can be used with [`X25519_BASEPOINT_BYTES`] for people who
340/// cannot use the better, safer, and faster ephemeral DH API.
341///
342/// # Example
343#[cfg_attr(feature = "static_secrets", doc = "```")]
344#[cfg_attr(not(feature = "static_secrets"), doc = "```ignore")]
345/// use rand_core::OsRng;
346/// use rand_core::RngCore;
347///
348/// use x25519_dalek::x25519;
349/// use x25519_dalek::StaticSecret;
350/// use x25519_dalek::PublicKey;
351///
352/// // Generate Alice's key pair.
353/// let alice_secret = StaticSecret::random_from_rng(&mut OsRng);
354/// let alice_public = PublicKey::from(&alice_secret);
355///
356/// // Generate Bob's key pair.
357/// let bob_secret = StaticSecret::random_from_rng(&mut OsRng);
358/// let bob_public = PublicKey::from(&bob_secret);
359///
360/// // Alice and Bob should now exchange their public keys.
361///
362/// // Once they've done so, they may generate a shared secret.
363/// let alice_shared = x25519(alice_secret.to_bytes(), bob_public.to_bytes());
364/// let bob_shared = x25519(bob_secret.to_bytes(), alice_public.to_bytes());
365///
366/// assert_eq!(alice_shared, bob_shared);
367/// ```
368pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] {
369    MontgomeryPoint(u).mul_clamped(k).to_bytes()
370}
371
372/// The X25519 basepoint, for use with the bare, byte-oriented x25519
373/// function.  This is provided for people who cannot use the typed
374/// DH API for some reason.
375pub const X25519_BASEPOINT_BYTES: [u8; 32] = [
376    9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
377];