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::suites::{CipherSuiteCommon, Suite, SupportedCipherSuite};
9use crate::version::Tls13Version;
10
11pub(crate) mod key_schedule;
12
13#[expect(clippy::exhaustive_structs)]
15pub struct Tls13CipherSuite {
16 pub common: CipherSuiteCommon,
18
19 pub protocol_version: &'static Tls13Version,
30
31 pub hkdf_provider: &'static dyn crypto::tls13::Hkdf,
39
40 pub aead_alg: &'static dyn crypto::cipher::Tls13AeadAlgorithm,
46
47 pub quic: Option<&'static dyn crate::quic::Algorithm>,
53}
54
55impl Tls13CipherSuite {
56 pub fn can_resume_from(&self, prev: &'static Self) -> Option<&'static Self> {
58 (prev.common.hash_provider.algorithm() == self.common.hash_provider.algorithm())
59 .then_some(prev)
60 }
61
62 pub fn fips(&self) -> FipsStatus {
66 let Self {
67 common,
68 protocol_version: _,
69 hkdf_provider,
70 aead_alg,
71 quic,
72 } = self;
73
74 let mut status = Ord::min(common.fips(), hkdf_provider.fips());
75 status = Ord::min(status, aead_alg.fips());
76 match quic {
77 Some(quic) => Ord::min(status, quic.fips()),
78 None => status,
79 }
80 }
81
82 pub fn quic_suite(&'static self) -> Option<crate::quic::Suite> {
84 self.quic
85 .map(|quic| crate::quic::Suite { suite: self, quic })
86 }
87}
88
89impl Suite for Tls13CipherSuite {
90 fn client_handler(&self) -> &'static dyn crate::client::ClientHandler<Self> {
91 self.protocol_version.client
92 }
93
94 fn server_handler(&self) -> &'static dyn crate::server::ServerHandler<Self> {
95 self.protocol_version.server
96 }
97
98 fn usable_for_protocol(&self, proto: Protocol) -> bool {
102 match proto {
103 Protocol::Tcp => true,
104 Protocol::Quic(_) => self.quic.is_some(),
105 }
106 }
107
108 fn usable_for_signature_scheme(&self, scheme: SignatureScheme) -> bool {
109 scheme.supported_in_tls13()
110 }
111
112 fn common(&self) -> &CipherSuiteCommon {
113 &self.common
114 }
115
116 const VERSION: ProtocolVersion = ProtocolVersion::TLSv1_3;
117}
118
119impl From<&'static Tls13CipherSuite> for SupportedCipherSuite {
120 fn from(s: &'static Tls13CipherSuite) -> Self {
121 Self::Tls13(s)
122 }
123}
124
125impl PartialEq for Tls13CipherSuite {
126 fn eq(&self, other: &Self) -> bool {
127 self.common.suite == other.common.suite
128 }
129}
130
131impl fmt::Debug for Tls13CipherSuite {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 f.debug_struct("Tls13CipherSuite")
134 .field("suite", &self.common.suite)
135 .finish_non_exhaustive()
136 }
137}
138
139pub(crate) fn construct_client_verify_message(handshake_hash: &hash::Output) -> VerifyMessage {
141 VerifyMessage::new(handshake_hash, CLIENT_CONSTANT)
142}
143
144pub(crate) fn construct_server_verify_message(handshake_hash: &hash::Output) -> VerifyMessage {
146 VerifyMessage::new(handshake_hash, SERVER_CONSTANT)
147}
148
149pub(crate) struct VerifyMessage {
150 buf: [u8; MAX_VERIFY_MSG],
151 used: usize,
152}
153
154impl VerifyMessage {
155 fn new(handshake_hash: &hash::Output, context_string_with_0: &[u8; 34]) -> Self {
156 let used = 64 + context_string_with_0.len() + handshake_hash.as_ref().len();
157 let mut buf = [0x20u8; MAX_VERIFY_MSG];
158
159 let (_spaces, context) = buf.split_at_mut(64);
160 let (context, hash) = context.split_at_mut(34);
161 context.copy_from_slice(context_string_with_0);
162 hash[..handshake_hash.as_ref().len()].copy_from_slice(handshake_hash.as_ref());
163
164 Self { buf, used }
165 }
166}
167
168impl AsRef<[u8]> for VerifyMessage {
169 fn as_ref(&self) -> &[u8] {
170 &self.buf[..self.used]
171 }
172}
173
174const SERVER_CONSTANT: &[u8; 34] = b"TLS 1.3, server CertificateVerify\x00";
175const CLIENT_CONSTANT: &[u8; 34] = b"TLS 1.3, client CertificateVerify\x00";
176const MAX_VERIFY_MSG: usize = 64 + CLIENT_CONSTANT.len() + hash::Output::MAX_LEN;
177
178#[cfg(test)]
179mod tests {
180 use crate::crypto::{CipherSuite, TEST_PROVIDER, tls13_suite};
181
182 #[test]
183 fn test_can_resume_to() {
184 let Some(cha_poly) = TEST_PROVIDER
185 .tls13_cipher_suites
186 .iter()
187 .find(|cs| cs.common.suite == CipherSuite::TLS13_CHACHA20_POLY1305_SHA256)
188 else {
189 return;
190 };
191
192 let aes_128_gcm = tls13_suite(CipherSuite::TLS13_AES_128_GCM_SHA256, &TEST_PROVIDER);
193 assert!(
194 aes_128_gcm
195 .can_resume_from(cha_poly)
196 .is_some()
197 );
198
199 let aes_256_gcm = tls13_suite(CipherSuite::TLS13_AES_256_GCM_SHA384, &TEST_PROVIDER);
200 assert!(
201 aes_256_gcm
202 .can_resume_from(cha_poly)
203 .is_none()
204 );
205 }
206}