Skip to main content

rustls/tls13/
mod.rs

1use alloc::vec::Vec;
2use core::fmt;
3
4use pki_types::FipsStatus;
5
6use crate::common_state::Protocol;
7use crate::crypto::{self, CipherSuite, SignatureScheme, hash};
8use crate::enums::ProtocolVersion;
9use crate::quic;
10use crate::suites::{CipherSuiteCommon, Suite, SupportedCipherSuite};
11use crate::version::Tls13Version;
12
13pub(crate) mod key_schedule;
14
15#[derive(Clone, Copy, Debug)]
16pub(crate) enum Tls13ProtocolSuite {
17    Tcp(&'static Tls13CipherSuite),
18    Quic(quic::Suite),
19}
20
21impl Tls13ProtocolSuite {
22    pub(crate) fn suite(&self) -> &'static Tls13CipherSuite {
23        match self {
24            Self::Tcp(suite) => suite,
25            Self::Quic(quic) => quic.inner,
26        }
27    }
28
29    pub(crate) fn is_quic(&self) -> bool {
30        matches!(self, Self::Quic(_))
31    }
32}
33
34/// A TLS 1.3 cipher suite supported by rustls.
35#[expect(clippy::exhaustive_structs)]
36pub struct Tls13CipherSuite {
37    /// Common cipher suite fields.
38    pub common: CipherSuiteCommon,
39
40    /// The associated protocol version.
41    ///
42    /// This field should have the value [`rustls::version::TLS13_VERSION`].
43    ///
44    /// This value contains references to the TLS1.3 protocol handling code.
45    /// This means that a program that does not contain any `Tls13CipherSuite`
46    /// values also does not contain any reference to the TLS1.3 protocol handling
47    /// code, and the linker can remove it.
48    ///
49    /// [`rustls::version::TLS13_VERSION`]: crate::version::TLS13_VERSION
50    pub protocol_version: &'static Tls13Version,
51
52    /// How to complete HKDF with the suite's hash function.
53    ///
54    /// If you have a HKDF implementation, you should directly implement the [`crypto::tls13::Hkdf`]
55    /// trait (and associated).
56    ///
57    /// If not, you can implement the [`crypto::hmac::Hmac`] trait (and associated), and then use
58    /// [`crypto::tls13::HkdfUsingHmac`].
59    pub hkdf_provider: &'static dyn crypto::tls13::Hkdf,
60
61    /// How to produce a [`RecordDecrypter`] or [`RecordEncrypter`]
62    /// from raw key material.
63    ///
64    /// [`RecordDecrypter`]: crate::crypto::cipher::RecordDecrypter
65    /// [`RecordEncrypter`]: crate::crypto::cipher::RecordEncrypter
66    pub aead_alg: &'static dyn crypto::cipher::Tls13AeadAlgorithm,
67
68    /// How to create QUIC header and record protection algorithms
69    /// for this suite.
70    ///
71    /// Provide `None` to opt out of QUIC support for this suite.  It will
72    /// not be offered in QUIC handshakes.
73    pub quic: Option<&'static dyn quic::Algorithm>,
74}
75
76impl Tls13CipherSuite {
77    /// Can a session using suite self resume from suite prev?
78    pub fn can_resume_from(&self, prev: &'static Self) -> Option<&'static Self> {
79        (prev.common.hash_provider.algorithm() == self.common.hash_provider.algorithm())
80            .then_some(prev)
81    }
82
83    /// Return the FIPS validation status of this implementation.
84    ///
85    /// This is the combination of the constituent parts of the cipher suite.
86    pub fn fips(&self) -> FipsStatus {
87        let Self {
88            common,
89            protocol_version: _,
90            hkdf_provider,
91            aead_alg,
92            quic,
93        } = self;
94
95        let mut status = Ord::min(common.fips(), hkdf_provider.fips());
96        status = Ord::min(status, aead_alg.fips());
97        match quic {
98            Some(quic) => Ord::min(status, quic.fips()),
99            None => status,
100        }
101    }
102}
103
104impl Suite for Tls13CipherSuite {
105    fn client_handler(&self) -> &'static dyn crate::client::ClientHandler<Self> {
106        self.protocol_version.client
107    }
108
109    fn server_handler(&self) -> &'static dyn crate::server::ServerHandler<Self> {
110        self.protocol_version.server
111    }
112
113    /// Does this suite support the `proto` protocol?
114    ///
115    /// All TLS1.3 suites support TCP-TLS. QUIC support is conditional on `quic` slot.
116    fn usable_for_protocol(&self, proto: Protocol) -> bool {
117        match proto {
118            Protocol::Tcp => true,
119            Protocol::Quic(_) => self.quic.is_some(),
120        }
121    }
122
123    fn filter_signature_schemes(
124        schemes: &mut Vec<SignatureScheme>,
125        _client: &[CipherSuite],
126        _server: &[&'static Self],
127    ) {
128        schemes.retain(|scheme| scheme.supported_in_tls13());
129    }
130
131    fn usable_for_signature_scheme(&self, scheme: SignatureScheme) -> bool {
132        scheme.supported_in_tls13()
133    }
134
135    fn common(&self) -> &CipherSuiteCommon {
136        &self.common
137    }
138
139    const VERSION: ProtocolVersion = ProtocolVersion::TLSv1_3;
140}
141
142impl From<&'static Tls13CipherSuite> for SupportedCipherSuite {
143    fn from(s: &'static Tls13CipherSuite) -> Self {
144        Self::Tls13(s)
145    }
146}
147
148impl PartialEq for Tls13CipherSuite {
149    fn eq(&self, other: &Self) -> bool {
150        self.common.suite == other.common.suite
151    }
152}
153
154impl fmt::Debug for Tls13CipherSuite {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        f.debug_struct("Tls13CipherSuite")
157            .field("suite", &self.common.suite)
158            .finish_non_exhaustive()
159    }
160}
161
162/// Constructs the signature message specified in section 4.5.2 of RFC 9846.
163pub(crate) fn construct_client_verify_message(handshake_hash: &hash::Output) -> VerifyMessage {
164    VerifyMessage::new(handshake_hash, CLIENT_CONSTANT)
165}
166
167/// Constructs the signature message specified in section 4.5.2 of RFC 9846.
168pub(crate) fn construct_server_verify_message(handshake_hash: &hash::Output) -> VerifyMessage {
169    VerifyMessage::new(handshake_hash, SERVER_CONSTANT)
170}
171
172pub(crate) struct VerifyMessage {
173    buf: [u8; MAX_VERIFY_MSG],
174    used: usize,
175}
176
177impl VerifyMessage {
178    fn new(handshake_hash: &hash::Output, context_string_with_0: &[u8; 34]) -> Self {
179        let used = 64 + context_string_with_0.len() + handshake_hash.as_ref().len();
180        let mut buf = [0x20u8; MAX_VERIFY_MSG];
181
182        let (_spaces, context) = buf.split_at_mut(64);
183        let (context, hash) = context.split_at_mut(34);
184        context.copy_from_slice(context_string_with_0);
185        hash[..handshake_hash.as_ref().len()].copy_from_slice(handshake_hash.as_ref());
186
187        Self { buf, used }
188    }
189}
190
191impl AsRef<[u8]> for VerifyMessage {
192    fn as_ref(&self) -> &[u8] {
193        &self.buf[..self.used]
194    }
195}
196
197const SERVER_CONSTANT: &[u8; 34] = b"TLS 1.3, server CertificateVerify\x00";
198const CLIENT_CONSTANT: &[u8; 34] = b"TLS 1.3, client CertificateVerify\x00";
199const MAX_VERIFY_MSG: usize = 64 + CLIENT_CONSTANT.len() + hash::Output::MAX_LEN;
200
201#[cfg(test)]
202mod tests {
203    use std::boxed::Box;
204
205    use crate::crypto::test_provider::FAKE_HASH;
206    use crate::crypto::{HashAlgorithm, TLS13_TEST_SUITE, hash};
207    use crate::{CipherSuiteCommon, Tls13CipherSuite};
208
209    #[test]
210    fn test_can_resume_to() {
211        let other_tls13_suite = Tls13CipherSuite {
212            common: CipherSuiteCommon {
213                hash_provider: &OtherHash,
214                ..TLS13_TEST_SUITE.common
215            },
216            ..*TLS13_TEST_SUITE
217        };
218
219        assert!(
220            TLS13_TEST_SUITE
221                .can_resume_from(TLS13_TEST_SUITE)
222                .is_some()
223        );
224
225        assert!(
226            other_tls13_suite
227                .can_resume_from(TLS13_TEST_SUITE)
228                .is_none()
229        );
230    }
231
232    struct OtherHash;
233
234    impl hash::Hash for OtherHash {
235        #[cfg_attr(coverage_nightly, coverage(off))]
236        fn start(&self) -> Box<dyn hash::Context> {
237            FAKE_HASH.start()
238        }
239
240        #[cfg_attr(coverage_nightly, coverage(off))]
241        fn hash(&self, data: &[u8]) -> hash::Output {
242            FAKE_HASH.hash(data)
243        }
244
245        #[cfg_attr(coverage_nightly, coverage(off))]
246        fn output_len(&self) -> usize {
247            FAKE_HASH.output_len()
248        }
249
250        fn algorithm(&self) -> HashAlgorithm {
251            HashAlgorithm(123)
252        }
253    }
254}