Skip to main content

rustls/crypto/kx/
mod.rs

1use alloc::boxed::Box;
2use alloc::vec::Vec;
3use core::fmt::Debug;
4use core::ops::Deref;
5
6use pki_types::FipsStatus;
7use zeroize::Zeroize;
8
9use crate::enums::ProtocolVersion;
10use crate::error::{Error, PeerMisbehaved};
11
12pub mod ffdhe;
13use ffdhe::FfdheGroup;
14
15/// A generalization of hybrid key exchange.
16#[expect(clippy::exhaustive_structs)]
17#[derive(Debug)]
18pub struct Hybrid {
19    /// Classical key exchange component.
20    pub classical: &'static dyn SupportedKxGroup,
21    /// Post-quantum key exchange component.
22    pub post_quantum: &'static dyn SupportedKxGroup,
23    /// TLS NamedGroup for this hybrid key exchange.
24    pub name: NamedGroup,
25    /// Layout of the hybrid key exchange.
26    pub layout: HybridLayout,
27}
28
29impl SupportedKxGroup for Hybrid {
30    fn start(&self) -> Result<StartedKeyExchange, Error> {
31        let classical = self.classical.start()?.into_single();
32        let post_quantum = self.post_quantum.start()?.into_single();
33
34        let combined_pub_key = self
35            .layout
36            .concat(post_quantum.pub_key(), classical.pub_key());
37
38        Ok(StartedKeyExchange::Hybrid(Box::new(ActiveHybrid {
39            classical,
40            post_quantum,
41            name: self.name,
42            layout: self.layout,
43            combined_pub_key,
44        })))
45    }
46
47    fn start_and_complete(&self, client_share: &[u8]) -> Result<CompletedKeyExchange, Error> {
48        let (post_quantum_share, classical_share) = self
49            .layout
50            .split_received_client_share(client_share)
51            .ok_or(PeerMisbehaved::InvalidKeyShare)?;
52
53        let cl = self
54            .classical
55            .start_and_complete(classical_share)?;
56        let pq = self
57            .post_quantum
58            .start_and_complete(post_quantum_share)?;
59
60        let combined_pub_key = self
61            .layout
62            .concat(&pq.pub_key, &cl.pub_key);
63        let secret = self
64            .layout
65            .concat(pq.secret.secret_bytes(), cl.secret.secret_bytes());
66
67        Ok(CompletedKeyExchange {
68            group: self.name,
69            pub_key: combined_pub_key,
70            secret: SharedSecret::from(secret),
71        })
72    }
73
74    fn name(&self) -> NamedGroup {
75        self.name
76    }
77
78    fn fips(&self) -> FipsStatus {
79        // Behold! The Night Mare: SP800-56C rev 2:
80        //
81        // "In addition to the currently approved techniques for the generation of the
82        // shared secret Z as specified in SP 800-56A and SP 800-56B, this Recommendation
83        // permits the use of a "hybrid" shared secret of the form Z′ = Z || T, a
84        // concatenation consisting of a "standard" shared secret Z that was generated
85        // during the execution of a key-establishment scheme (as currently specified in
86        // [SP 800-56A] or [SP 800-56B])"
87        //
88        // NIST plan to adjust this and allow both orders: see
89        // <https://csrc.nist.gov/pubs/sp/800/227/ipd> (Jan 2025) lines 1070-1080.
90        //
91        // But, for now, we follow the SP800-56C logic: the element appearing first is the
92        // one that controls approval.
93        match self.layout.post_quantum_first {
94            true => self.post_quantum.fips(),
95            false => self.classical.fips(),
96        }
97    }
98}
99
100struct ActiveHybrid {
101    classical: Box<dyn ActiveKeyExchange>,
102    post_quantum: Box<dyn ActiveKeyExchange>,
103    name: NamedGroup,
104    layout: HybridLayout,
105    combined_pub_key: Vec<u8>,
106}
107
108impl ActiveKeyExchange for ActiveHybrid {
109    fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
110        let (post_quantum_share, classical_share) = self
111            .layout
112            .split_received_server_share(peer_pub_key)
113            .ok_or(PeerMisbehaved::InvalidKeyShare)?;
114
115        let cl = self
116            .classical
117            .complete(classical_share)?;
118        let pq = self
119            .post_quantum
120            .complete(post_quantum_share)?;
121
122        let secret = self
123            .layout
124            .concat(pq.secret_bytes(), cl.secret_bytes());
125        Ok(SharedSecret::from(secret))
126    }
127
128    fn pub_key(&self) -> &[u8] {
129        &self.combined_pub_key
130    }
131
132    fn group(&self) -> NamedGroup {
133        self.name
134    }
135}
136
137impl HybridKeyExchange for ActiveHybrid {
138    fn component(&self) -> (NamedGroup, &[u8]) {
139        (self.classical.group(), self.classical.pub_key())
140    }
141
142    fn complete_component(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
143        self.classical.complete(peer_pub_key)
144    }
145
146    fn into_key_exchange(self: Box<Self>) -> Box<dyn ActiveKeyExchange> {
147        self
148    }
149
150    fn as_key_exchange(&self) -> &(dyn ActiveKeyExchange + 'static) {
151        self
152    }
153}
154
155/// Layout of a hybrid key exchange's key shares and secrets.
156#[expect(clippy::exhaustive_structs)]
157#[derive(Clone, Copy, Debug)]
158pub struct HybridLayout {
159    /// Length of classical key share.
160    pub classical_share_len: usize,
161
162    /// Length of post-quantum key share sent by client
163    pub post_quantum_client_share_len: usize,
164
165    /// Length of post-quantum key share sent by server
166    pub post_quantum_server_share_len: usize,
167
168    /// Whether the post-quantum element comes first in shares and secrets.
169    ///
170    /// For dismal and unprincipled reasons, SECP256R1MLKEM768 has the
171    /// classical element first, while X25519MLKEM768 has it second.
172    pub post_quantum_first: bool,
173}
174
175impl HybridLayout {
176    fn split_received_client_share<'a>(&self, share: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> {
177        self.split(share, self.post_quantum_client_share_len)
178    }
179
180    fn split_received_server_share<'a>(&self, share: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> {
181        self.split(share, self.post_quantum_server_share_len)
182    }
183
184    /// Return the PQ and classical component of a key share.
185    fn split<'a>(
186        &self,
187        share: &'a [u8],
188        post_quantum_share_len: usize,
189    ) -> Option<(&'a [u8], &'a [u8])> {
190        if share.len() != self.classical_share_len + post_quantum_share_len {
191            return None;
192        }
193
194        Some(match self.post_quantum_first {
195            true => {
196                let (first_share, second_share) = share.split_at(post_quantum_share_len);
197                (first_share, second_share)
198            }
199            false => {
200                let (first_share, second_share) = share.split_at(self.classical_share_len);
201                (second_share, first_share)
202            }
203        })
204    }
205
206    fn concat(&self, post_quantum: &[u8], classical: &[u8]) -> Vec<u8> {
207        match self.post_quantum_first {
208            true => [post_quantum, classical].concat(),
209            false => [classical, post_quantum].concat(),
210        }
211    }
212}
213
214/// A supported key exchange group.
215///
216/// This type carries both configuration and implementation. Specifically,
217/// it has a TLS-level name expressed using the [`NamedGroup`] enum, and
218/// a function which produces a [`ActiveKeyExchange`].
219///
220/// Compare with [`NamedGroup`], which carries solely a protocol identifier.
221pub trait SupportedKxGroup: Send + Sync + Debug {
222    /// Start a key exchange.
223    ///
224    /// This will prepare an ephemeral secret key in the supported group, and a corresponding
225    /// public key. The key exchange can be completed by calling [`ActiveKeyExchange::complete()`]
226    /// or discarded.
227    ///
228    /// Most implementations will want to return the `StartedKeyExchange::Single(_)` variant.
229    /// Hybrid key exchange algorithms, which are constructed from two underlying algorithms,
230    /// may wish to return `StartedKeyExchange::Hybrid(_)` variant which additionally allows
231    /// one part of the key exchange to be completed separately.  See the documentation
232    /// on [`HybridKeyExchange`] for more detail.
233    ///
234    /// # Errors
235    ///
236    /// This can fail if the random source fails during ephemeral key generation.
237    fn start(&self) -> Result<StartedKeyExchange, Error>;
238
239    /// Start and complete a key exchange, in one operation.
240    ///
241    /// The default implementation for this calls `start()` and then calls
242    /// `complete()` on the result.  This is suitable for Diffie-Hellman-like
243    /// key exchange algorithms, where there is not a data dependency between
244    /// our key share (named "pub_key" in this API) and the peer's (`peer_pub_key`).
245    ///
246    /// If there is such a data dependency (like key encapsulation mechanisms), this
247    /// function should be implemented.
248    fn start_and_complete(&self, peer_pub_key: &[u8]) -> Result<CompletedKeyExchange, Error> {
249        let kx = self.start()?.into_single();
250
251        Ok(CompletedKeyExchange {
252            group: kx.group(),
253            pub_key: kx.pub_key().to_vec(),
254            secret: kx.complete(peer_pub_key)?,
255        })
256    }
257
258    /// FFDHE group the `SupportedKxGroup` operates in, if any.
259    ///
260    /// The default implementation returns `None`, so non-FFDHE groups (the
261    /// most common) do not need to do anything.
262    ///
263    /// FFDHE groups must implement this. [`ffdhe`] contains suitable values to return, for
264    /// example [`ffdhe::FFDHE2048`].
265    fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
266        None
267    }
268
269    /// Named group the SupportedKxGroup operates in.
270    ///
271    /// If the `NamedGroup` enum does not have a name for the algorithm you are implementing,
272    /// you can use [`NamedGroup::Unknown`].
273    fn name(&self) -> NamedGroup;
274
275    /// Return `true` if this is backed by a FIPS-approved implementation.
276    fn fips(&self) -> FipsStatus {
277        FipsStatus::Unvalidated
278    }
279}
280
281/// Return value from [`SupportedKxGroup::start()`].
282#[non_exhaustive]
283pub enum StartedKeyExchange {
284    /// A single [`ActiveKeyExchange`].
285    Single(Box<dyn ActiveKeyExchange>),
286    /// A [`HybridKeyExchange`] that can potentially be split.
287    Hybrid(Box<dyn HybridKeyExchange>),
288}
289
290impl StartedKeyExchange {
291    /// Collapses this object into its underlying [`ActiveKeyExchange`].
292    ///
293    /// This removes the ability to do the hybrid key exchange optimization,
294    /// but still allows the key exchange as a whole to be completed.
295    pub fn into_single(self) -> Box<dyn ActiveKeyExchange> {
296        match self {
297            Self::Single(s) => s,
298            Self::Hybrid(h) => h.into_key_exchange(),
299        }
300    }
301
302    /// Accesses the [`HybridKeyExchange`], and checks it was also usable separately.
303    ///
304    /// Returns:
305    ///
306    /// - the [`HybridKeyExchange`]
307    /// - the stand-alone `SupportedKxGroup` for the hybrid's component group.
308    ///
309    /// This returns `None` for:
310    ///
311    /// - non-hybrid groups,
312    /// - if the hybrid component group is not present in `supported`
313    /// - if the hybrid component group is not usable with `version`
314    pub(crate) fn as_hybrid_checked(
315        &self,
316        supported: &[&'static dyn SupportedKxGroup],
317        version: ProtocolVersion,
318    ) -> Option<(&dyn HybridKeyExchange, &'static dyn SupportedKxGroup)> {
319        let Self::Hybrid(hybrid) = self else {
320            return None;
321        };
322
323        let component_group = hybrid.component().0;
324        if !component_group.usable_for_version(version) {
325            return None;
326        }
327
328        supported
329            .iter()
330            .find(|g| g.name() == component_group)
331            .copied()
332            .map(|g| (hybrid.as_ref(), g))
333    }
334}
335
336impl Deref for StartedKeyExchange {
337    type Target = dyn ActiveKeyExchange;
338
339    fn deref(&self) -> &Self::Target {
340        match self {
341            Self::Single(s) => s.as_ref(),
342            Self::Hybrid(h) => h.as_key_exchange(),
343        }
344    }
345}
346
347/// An in-progress key exchange originating from a [`SupportedKxGroup`].
348pub trait ActiveKeyExchange: Send + Sync {
349    /// Completes the key exchange, given the peer's public key.
350    ///
351    /// This method must return an error if `peer_pub_key` is invalid: either
352    /// misencoded, or an invalid public key (such as, but not limited to, being
353    /// in a small order subgroup).
354    ///
355    /// If the key exchange algorithm is FFDHE, the result must be left-padded with zeros,
356    /// as required by [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446#section-7.4.1)
357    /// (see [`complete_for_tls_version()`](Self::complete_for_tls_version) for more details).
358    ///
359    /// The shared secret is returned as a [`SharedSecret`] which can be constructed
360    /// from a `&[u8]`.
361    ///
362    /// This consumes and so terminates the [`ActiveKeyExchange`].
363    fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error>;
364
365    /// Completes the key exchange for the given TLS version, given the peer's public key.
366    ///
367    /// Note that finite-field Diffie–Hellman key exchange has different requirements for the derived
368    /// shared secret in TLS 1.2 and TLS 1.3 (ECDHE key exchange is the same in TLS 1.2 and TLS 1.3):
369    ///
370    /// In TLS 1.2, the calculated secret is required to be stripped of leading zeros
371    /// [(RFC 5246)](https://www.rfc-editor.org/rfc/rfc5246#section-8.1.2).
372    ///
373    /// In TLS 1.3, the calculated secret is required to be padded with leading zeros to be the same
374    /// byte-length as the group modulus [(RFC 8446)](https://www.rfc-editor.org/rfc/rfc8446#section-7.4.1).
375    ///
376    /// The default implementation of this method delegates to [`complete()`](Self::complete) assuming it is
377    /// implemented for TLS 1.3 (i.e., for FFDHE KX, removes padding as needed). Implementers of this trait
378    /// are encouraged to just implement [`complete()`](Self::complete) assuming TLS 1.3, and let the default
379    /// implementation of this method handle TLS 1.2-specific requirements.
380    ///
381    /// This method must return an error if `peer_pub_key` is invalid: either
382    /// misencoded, or an invalid public key (such as, but not limited to, being
383    /// in a small order subgroup).
384    ///
385    /// The shared secret is returned as a [`SharedSecret`] which can be constructed
386    /// from a `&[u8]`.
387    ///
388    /// This consumes and so terminates the [`ActiveKeyExchange`].
389    fn complete_for_tls_version(
390        self: Box<Self>,
391        peer_pub_key: &[u8],
392        tls_version: ProtocolVersion,
393    ) -> Result<SharedSecret, Error> {
394        if tls_version == ProtocolVersion::TLSv1_3 {
395            return self.complete(peer_pub_key);
396        }
397
398        let group = self.group();
399        let mut complete_res = self.complete(peer_pub_key)?;
400        if group.key_exchange_algorithm() == KeyExchangeAlgorithm::DHE {
401            complete_res.strip_leading_zeros();
402        }
403        Ok(complete_res)
404    }
405
406    /// Return the public key being used.
407    ///
408    /// For ECDHE, the encoding required is defined in
409    /// [RFC8446 section 4.2.8.2](https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.2).
410    ///
411    /// For FFDHE, the encoding required is defined in
412    /// [RFC8446 section 4.2.8.1](https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.1).
413    fn pub_key(&self) -> &[u8];
414
415    /// FFDHE group the `ActiveKeyExchange` is operating in.
416    ///
417    /// The default implementation returns `None`, so non-FFDHE groups (the
418    /// most common) do not need to do anything.
419    ///
420    /// FFDHE groups must implement this. [`ffdhe`] contains suitable values to return, for
421    /// example [`ffdhe::FFDHE2048`].
422    fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
423        None
424    }
425
426    /// Return the group being used.
427    fn group(&self) -> NamedGroup;
428}
429
430/// An in-progress hybrid key exchange originating from a [`SupportedKxGroup`].
431///
432/// "Hybrid" means a key exchange algorithm which is constructed from two
433/// (or more) independent component algorithms. Usually one is post-quantum-secure,
434/// and the other is "classical".  See
435/// <https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/11/>
436///
437/// There is no requirement for a hybrid scheme (or any other!) to implement
438/// `HybridKeyExchange` if it is not desirable for it to be "split" like this.
439/// It only enables an optimization; described below.
440///
441/// # Background
442/// Rustls always sends a presumptive key share in its `ClientHello`, using
443/// (absent any other information) the first item in
444/// [`CryptoProvider::kx_groups`][super::CryptoProvider::kx_groups].
445/// If the server accepts the client's selection, it can complete the handshake
446/// using that key share.  If not, the server sends a `HelloRetryRequest` instructing
447/// the client to send a different key share instead.
448///
449/// This request costs an extra round trip, and wastes the key exchange computation
450/// (in [`SupportedKxGroup::start()`]) the client already did.  We would
451/// like to avoid those wastes if possible.
452///
453/// It is early days for post-quantum-secure hybrid key exchange deployment.
454/// This means (commonly) continuing to offer both the hybrid and classical
455/// key exchanges, so the handshake can be completed without a `HelloRetryRequest`
456/// for servers that support the offered hybrid or classical schemes.
457///
458/// Implementing `HybridKeyExchange` enables two optimizations:
459///
460/// 1. Sending both the hybrid and classical key shares in the `ClientHello`.
461///
462/// 2. Performing the classical key exchange setup only once.  This is important
463///    because the classical key exchange setup is relatively expensive.
464///    This optimization is permitted and described in
465///    <https://www.ietf.org/archive/id/draft-ietf-tls-hybrid-design-11.html#section-3.2>
466///
467/// Both of these only happen if the classical algorithm appears separately in
468/// the client's [`CryptoProvider::kx_groups`][super::CryptoProvider::kx_groups],
469/// and if the hybrid algorithm appears first in that list.
470///
471/// # How it works
472/// This function is only called by rustls for clients.  It is called when
473/// constructing the initial `ClientHello`.  rustls follows these steps:
474///
475/// 1. If the return value is `None`, nothing further happens.
476/// 2. If the given [`NamedGroup`] does not appear in
477///    [`CryptoProvider::kx_groups`][super::CryptoProvider::kx_groups], nothing further happens.
478/// 3. The given key share is added to the `ClientHello`, after the hybrid entry.
479///
480/// Then, one of three things may happen when the server replies to the `ClientHello`:
481///
482/// 1. The server sends a `HelloRetryRequest`.  Everything is thrown away and
483///    we start again.
484/// 2. The server agrees to our hybrid key exchange: rustls calls
485///    [`ActiveKeyExchange::complete()`] consuming `self`.
486/// 3. The server agrees to our classical key exchange: rustls calls
487///    [`HybridKeyExchange::complete_component()`] which
488///    discards the hybrid key data, and completes just the classical key exchange.
489pub trait HybridKeyExchange: ActiveKeyExchange {
490    /// Returns the [`NamedGroup`] and public key "share" for the component.
491    fn component(&self) -> (NamedGroup, &[u8]);
492
493    /// Completes the classical component of the key exchange, given the peer's public key.
494    ///
495    /// This method must return an error if `peer_pub_key` is invalid: either
496    /// misencoded, or an invalid public key (such as, but not limited to, being
497    /// in a small order subgroup).
498    ///
499    /// The shared secret is returned as a [`SharedSecret`] which can be constructed
500    /// from a `&[u8]`.
501    ///
502    /// See the documentation on [`HybridKeyExchange`] for explanation.
503    fn complete_component(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error>;
504
505    /// Obtain the value as a `dyn ActiveKeyExchange`
506    fn as_key_exchange(&self) -> &(dyn ActiveKeyExchange + 'static);
507
508    /// Remove the ability to do hybrid key exchange on this object.
509    fn into_key_exchange(self: Box<Self>) -> Box<dyn ActiveKeyExchange>;
510}
511
512/// The result from [`SupportedKxGroup::start_and_complete()`].
513#[expect(clippy::exhaustive_structs)]
514pub struct CompletedKeyExchange {
515    /// Which group was used.
516    pub group: NamedGroup,
517
518    /// Our key share (sometimes a public key).
519    pub pub_key: Vec<u8>,
520
521    /// The computed shared secret.
522    pub secret: SharedSecret,
523}
524
525enum_builder! {
526    /// The `NamedGroup` TLS protocol enum.  Values in this enum are taken
527    /// from the various RFCs covering TLS, and are listed by IANA.
528    /// The `Unknown` item is used when processing unrecognized ordinals.
529    ///
530    /// This enum is used for recognizing key exchange groups advertised
531    /// by a peer during a TLS handshake. It is **not** a list of groups that
532    /// Rustls supports. The supported groups are determined via the
533    /// [`CryptoProvider`][crate::crypto::CryptoProvider] interface.
534    #[repr(u16)]
535    #[expect(non_camel_case_types)]
536    pub enum NamedGroup {
537        secp256r1 => 0x0017,
538        secp384r1 => 0x0018,
539        secp521r1 => 0x0019,
540        X25519 => 0x001d,
541        X448 => 0x001e,
542        /// <https://www.iana.org/go/rfc8734>
543        brainpoolP256r1tls13 => 0x001f,
544        /// <https://www.iana.org/go/rfc8734>
545        brainpoolP384r1tls13 => 0x0020,
546        /// <https://www.iana.org/go/rfc8734>
547        brainpoolP512r1tls13 => 0x0021,
548        /// <https://www.iana.org/go/rfc8998>
549        curveSM2 => 0x0029,
550        FFDHE2048 => 0x0100,
551        FFDHE3072 => 0x0101,
552        FFDHE4096 => 0x0102,
553        FFDHE6144 => 0x0103,
554        FFDHE8192 => 0x0104,
555        /// <https://datatracker.ietf.org/doc/draft-ietf-tls-mlkem/>
556        MLKEM512 => 0x0200,
557        /// <https://datatracker.ietf.org/doc/draft-ietf-tls-mlkem/>
558        MLKEM768 => 0x0201,
559        /// <https://datatracker.ietf.org/doc/draft-ietf-tls-mlkem/>
560        MLKEM1024 => 0x0202,
561        /// <https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/>
562        secp256r1MLKEM768 => 0x11eb,
563        /// <https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/>
564        X25519MLKEM768 => 0x11ec,
565        /// <https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/>
566        secp384r1MLKEM1024 => 0x11ed,
567    }
568}
569
570impl NamedGroup {
571    /// Return the key exchange algorithm associated with this `NamedGroup`
572    pub fn key_exchange_algorithm(self) -> KeyExchangeAlgorithm {
573        match u16::from(self) {
574            x if (0x100..0x200).contains(&x) => KeyExchangeAlgorithm::DHE,
575            _ => KeyExchangeAlgorithm::ECDHE,
576        }
577    }
578
579    /// Returns whether this `NamedGroup` is usable for the given protocol version.
580    pub fn usable_for_version(&self, version: ProtocolVersion) -> bool {
581        match version {
582            ProtocolVersion::TLSv1_3 => true,
583            _ => !matches!(
584                self,
585                Self::MLKEM512
586                    | Self::MLKEM768
587                    | Self::MLKEM1024
588                    | Self::X25519MLKEM768
589                    | Self::secp256r1MLKEM768
590                    | Self::secp384r1MLKEM1024
591                    | Self::brainpoolP256r1tls13
592                    | Self::brainpoolP384r1tls13
593                    | Self::brainpoolP512r1tls13
594                    | Self::curveSM2
595            ),
596        }
597    }
598}
599
600/// The result from [`ActiveKeyExchange::complete()`] or [`HybridKeyExchange::complete_component()`].
601pub struct SharedSecret {
602    buf: Vec<u8>,
603    offset: usize,
604}
605
606impl SharedSecret {
607    /// Returns the shared secret as a slice of bytes.
608    pub fn secret_bytes(&self) -> &[u8] {
609        &self.buf[self.offset..]
610    }
611
612    /// Removes leading zeros from `secret_bytes()` by adjusting the `offset`.
613    ///
614    /// This function does not re-allocate.
615    fn strip_leading_zeros(&mut self) {
616        let start = self
617            .secret_bytes()
618            .iter()
619            .enumerate()
620            .find(|(_i, x)| **x != 0)
621            .map(|(i, _x)| i)
622            .unwrap_or_else(|| self.secret_bytes().len());
623        self.offset += start;
624    }
625}
626
627impl Drop for SharedSecret {
628    fn drop(&mut self) {
629        self.buf.zeroize();
630    }
631}
632
633impl From<&[u8]> for SharedSecret {
634    fn from(source: &[u8]) -> Self {
635        Self {
636            buf: source.to_vec(),
637            offset: 0,
638        }
639    }
640}
641
642impl From<Vec<u8>> for SharedSecret {
643    fn from(buf: Vec<u8>) -> Self {
644        Self { buf, offset: 0 }
645    }
646}
647
648/// Describes supported key exchange mechanisms.
649#[derive(Clone, Copy, Debug, PartialEq)]
650#[non_exhaustive]
651pub enum KeyExchangeAlgorithm {
652    /// Diffie-Hellman Key exchange (with only known parameters as defined in [RFC 7919]).
653    ///
654    /// [RFC 7919]: https://datatracker.ietf.org/doc/html/rfc7919
655    DHE,
656    /// Key exchange performed via elliptic curve Diffie-Hellman.
657    ECDHE,
658}
659
660#[cfg(test)]
661mod tests {
662    use std::vec;
663
664    use super::{NamedGroup, SharedSecret};
665    use crate::msgs::test_enum16;
666
667    #[test]
668    fn test_shared_secret_strip_leading_zeros() {
669        let test_cases = [
670            (vec![0, 1], vec![1]),
671            (vec![1], vec![1]),
672            (vec![1, 0, 2], vec![1, 0, 2]),
673            (vec![0, 0, 1, 2], vec![1, 2]),
674            (vec![0, 0, 0], vec![]),
675            (vec![], vec![]),
676        ];
677        for (buf, expected) in test_cases {
678            let mut secret = SharedSecret::from(&buf[..]);
679            assert_eq!(secret.secret_bytes(), buf);
680            secret.strip_leading_zeros();
681            assert_eq!(secret.secret_bytes(), expected);
682        }
683    }
684
685    #[test]
686    fn test_enums() {
687        test_enum16::<NamedGroup>(NamedGroup::secp256r1, NamedGroup::FFDHE8192);
688    }
689}