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#[expect(clippy::exhaustive_structs)]
36pub struct Tls13CipherSuite {
37 pub common: CipherSuiteCommon,
39
40 pub protocol_version: &'static Tls13Version,
51
52 pub hkdf_provider: &'static dyn crypto::tls13::Hkdf,
60
61 pub aead_alg: &'static dyn crypto::cipher::Tls13AeadAlgorithm,
67
68 pub quic: Option<&'static dyn quic::Algorithm>,
74}
75
76impl Tls13CipherSuite {
77 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 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 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
162pub(crate) fn construct_client_verify_message(handshake_hash: &hash::Output) -> VerifyMessage {
164 VerifyMessage::new(handshake_hash, CLIENT_CONSTANT)
165}
166
167pub(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}