1use core::fmt;
2
3use pki_types::FipsStatus;
4
5use crate::common_state::Protocol;
6use crate::crypto::{self, SignatureScheme, hash};
7use crate::enums::ProtocolVersion;
8use crate::quic;
9use crate::suites::{CipherSuiteCommon, Suite, SupportedCipherSuite};
10use crate::version::Tls13Version;
11
12pub(crate) mod key_schedule;
13
14#[derive(Clone, Copy, Debug)]
15pub(crate) enum Tls13ProtocolSuite {
16 Tcp(&'static Tls13CipherSuite),
17 Quic(quic::Suite),
18}
19
20impl Tls13ProtocolSuite {
21 pub(crate) fn suite(&self) -> &'static Tls13CipherSuite {
22 match self {
23 Self::Tcp(suite) => suite,
24 Self::Quic(quic) => quic.inner,
25 }
26 }
27
28 pub(crate) fn is_quic(&self) -> bool {
29 matches!(self, Self::Quic(_))
30 }
31}
32
33#[expect(clippy::exhaustive_structs)]
35pub struct Tls13CipherSuite {
36 pub common: CipherSuiteCommon,
38
39 pub protocol_version: &'static Tls13Version,
50
51 pub hkdf_provider: &'static dyn crypto::tls13::Hkdf,
59
60 pub aead_alg: &'static dyn crypto::cipher::Tls13AeadAlgorithm,
66
67 pub quic: Option<&'static dyn quic::Algorithm>,
73}
74
75impl Tls13CipherSuite {
76 pub fn can_resume_from(&self, prev: &'static Self) -> Option<&'static Self> {
78 (prev.common.hash_provider.algorithm() == self.common.hash_provider.algorithm())
79 .then_some(prev)
80 }
81
82 pub fn fips(&self) -> FipsStatus {
86 let Self {
87 common,
88 protocol_version: _,
89 hkdf_provider,
90 aead_alg,
91 quic,
92 } = self;
93
94 let mut status = Ord::min(common.fips(), hkdf_provider.fips());
95 status = Ord::min(status, aead_alg.fips());
96 match quic {
97 Some(quic) => Ord::min(status, quic.fips()),
98 None => status,
99 }
100 }
101}
102
103impl Suite for Tls13CipherSuite {
104 fn client_handler(&self) -> &'static dyn crate::client::ClientHandler<Self> {
105 self.protocol_version.client
106 }
107
108 fn server_handler(&self) -> &'static dyn crate::server::ServerHandler<Self> {
109 self.protocol_version.server
110 }
111
112 fn usable_for_protocol(&self, proto: Protocol) -> bool {
116 match proto {
117 Protocol::Tcp => true,
118 Protocol::Quic(_) => self.quic.is_some(),
119 }
120 }
121
122 fn usable_for_signature_scheme(&self, scheme: SignatureScheme) -> bool {
123 scheme.supported_in_tls13()
124 }
125
126 fn common(&self) -> &CipherSuiteCommon {
127 &self.common
128 }
129
130 const VERSION: ProtocolVersion = ProtocolVersion::TLSv1_3;
131}
132
133impl From<&'static Tls13CipherSuite> for SupportedCipherSuite {
134 fn from(s: &'static Tls13CipherSuite) -> Self {
135 Self::Tls13(s)
136 }
137}
138
139impl PartialEq for Tls13CipherSuite {
140 fn eq(&self, other: &Self) -> bool {
141 self.common.suite == other.common.suite
142 }
143}
144
145impl fmt::Debug for Tls13CipherSuite {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.debug_struct("Tls13CipherSuite")
148 .field("suite", &self.common.suite)
149 .finish_non_exhaustive()
150 }
151}
152
153pub(crate) fn construct_client_verify_message(handshake_hash: &hash::Output) -> VerifyMessage {
155 VerifyMessage::new(handshake_hash, CLIENT_CONSTANT)
156}
157
158pub(crate) fn construct_server_verify_message(handshake_hash: &hash::Output) -> VerifyMessage {
160 VerifyMessage::new(handshake_hash, SERVER_CONSTANT)
161}
162
163pub(crate) struct VerifyMessage {
164 buf: [u8; MAX_VERIFY_MSG],
165 used: usize,
166}
167
168impl VerifyMessage {
169 fn new(handshake_hash: &hash::Output, context_string_with_0: &[u8; 34]) -> Self {
170 let used = 64 + context_string_with_0.len() + handshake_hash.as_ref().len();
171 let mut buf = [0x20u8; MAX_VERIFY_MSG];
172
173 let (_spaces, context) = buf.split_at_mut(64);
174 let (context, hash) = context.split_at_mut(34);
175 context.copy_from_slice(context_string_with_0);
176 hash[..handshake_hash.as_ref().len()].copy_from_slice(handshake_hash.as_ref());
177
178 Self { buf, used }
179 }
180}
181
182impl AsRef<[u8]> for VerifyMessage {
183 fn as_ref(&self) -> &[u8] {
184 &self.buf[..self.used]
185 }
186}
187
188const SERVER_CONSTANT: &[u8; 34] = b"TLS 1.3, server CertificateVerify\x00";
189const CLIENT_CONSTANT: &[u8; 34] = b"TLS 1.3, client CertificateVerify\x00";
190const MAX_VERIFY_MSG: usize = 64 + CLIENT_CONSTANT.len() + hash::Output::MAX_LEN;
191
192#[cfg(test)]
193mod tests {
194 use std::boxed::Box;
195
196 use crate::crypto::test_provider::FAKE_HASH;
197 use crate::crypto::{HashAlgorithm, TLS13_TEST_SUITE, hash};
198 use crate::{CipherSuiteCommon, Tls13CipherSuite};
199
200 #[test]
201 fn test_can_resume_to() {
202 let other_tls13_suite = Tls13CipherSuite {
203 common: CipherSuiteCommon {
204 hash_provider: &OtherHash,
205 ..TLS13_TEST_SUITE.common
206 },
207 ..*TLS13_TEST_SUITE
208 };
209
210 assert!(
211 TLS13_TEST_SUITE
212 .can_resume_from(TLS13_TEST_SUITE)
213 .is_some()
214 );
215
216 assert!(
217 other_tls13_suite
218 .can_resume_from(TLS13_TEST_SUITE)
219 .is_none()
220 );
221 }
222
223 struct OtherHash;
224
225 impl hash::Hash for OtherHash {
226 #[cfg_attr(coverage_nightly, coverage(off))]
227 fn start(&self) -> Box<dyn hash::Context> {
228 FAKE_HASH.start()
229 }
230
231 #[cfg_attr(coverage_nightly, coverage(off))]
232 fn hash(&self, data: &[u8]) -> hash::Output {
233 FAKE_HASH.hash(data)
234 }
235
236 #[cfg_attr(coverage_nightly, coverage(off))]
237 fn output_len(&self) -> usize {
238 FAKE_HASH.output_len()
239 }
240
241 fn algorithm(&self) -> HashAlgorithm {
242 HashAlgorithm(123)
243 }
244 }
245}