1use alloc::boxed::Box;
2use alloc::vec;
3use alloc::vec::Vec;
4use core::fmt;
5
6use pki_types::FipsStatus;
7use zeroize::Zeroizing;
8
9use crate::common_state::{Protocol, Side};
10use crate::conn::{ConnectionRandoms, Exporter};
11use crate::crypto::cipher::{AeadKey, RecordDecrypter, RecordEncrypter, Tls12AeadAlgorithm};
12use crate::crypto::kx::{ActiveKeyExchange, KeyExchangeAlgorithm};
13use crate::crypto::tls12::PrfSecret;
14use crate::crypto::{self, SignatureScheme, hash};
15use crate::enums::ProtocolVersion;
16use crate::error::{ApiMisuse, Error, InvalidMessage};
17use crate::msgs::{Codec, HandshakeAlignedProof, KxDecode, Reader};
18use crate::suites::{CipherSuiteCommon, PartiallyExtractedSecrets, Suite, SupportedCipherSuite};
19use crate::version::Tls12Version;
20
21#[expect(clippy::exhaustive_structs)]
23pub struct Tls12CipherSuite {
24 pub common: CipherSuiteCommon,
26
27 pub protocol_version: &'static Tls12Version,
38
39 pub prf_provider: &'static dyn crypto::tls12::Prf,
46
47 pub kx: KeyExchangeAlgorithm,
55
56 pub sign: &'static [SignatureScheme],
63
64 pub aead_alg: &'static dyn Tls12AeadAlgorithm,
66}
67
68impl Tls12CipherSuite {
69 pub fn resolve_sig_schemes(&self, offered: &[SignatureScheme]) -> Vec<SignatureScheme> {
73 self.sign
74 .iter()
75 .filter(|pref| offered.contains(pref))
76 .copied()
77 .collect()
78 }
79
80 pub fn fips(&self) -> FipsStatus {
84 let status = Ord::min(self.common.fips(), self.prf_provider.fips());
85 Ord::min(status, self.aead_alg.fips())
86 }
87}
88
89impl Suite for Tls12CipherSuite {
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 matches!(proto, Protocol::Tcp)
103 }
104
105 fn usable_for_kx_algorithm(&self, kxa: KeyExchangeAlgorithm) -> bool {
107 self.kx == kxa
108 }
109
110 fn usable_for_signature_scheme(&self, scheme: SignatureScheme) -> bool {
112 let Some(alg) = scheme.algorithm() else {
113 return false;
114 };
115
116 self.sign
117 .iter()
118 .any(|s| s.algorithm() == Some(alg))
119 }
120
121 fn common(&self) -> &CipherSuiteCommon {
122 &self.common
123 }
124
125 const VERSION: ProtocolVersion = ProtocolVersion::TLSv1_2;
126}
127
128impl From<&'static Tls12CipherSuite> for SupportedCipherSuite {
129 fn from(s: &'static Tls12CipherSuite) -> Self {
130 Self::Tls12(s)
131 }
132}
133
134impl PartialEq for Tls12CipherSuite {
135 fn eq(&self, other: &Self) -> bool {
136 self.common.suite == other.common.suite
137 }
138}
139
140impl fmt::Debug for Tls12CipherSuite {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 f.debug_struct("Tls12CipherSuite")
143 .field("suite", &self.common.suite)
144 .finish_non_exhaustive()
145 }
146}
147
148pub(crate) struct ConnectionSecrets {
150 pub(crate) randoms: ConnectionRandoms,
151 suite: &'static Tls12CipherSuite,
152 master_secret: Zeroizing<[u8; 48]>,
153
154 master_secret_prf: Box<dyn PrfSecret>,
158}
159
160impl ConnectionSecrets {
161 pub(crate) fn from_key_exchange(
162 kx: Box<dyn ActiveKeyExchange>,
163 peer_pub_key: &[u8],
164 ems_seed: Option<hash::Output>,
165 randoms: ConnectionRandoms,
166 suite: &'static Tls12CipherSuite,
167 ) -> Result<Self, Error> {
168 let (label, seed) = match ems_seed {
169 Some(seed) => ("extended master secret", Seed::Ems(seed)),
170 None => (
171 "master secret",
172 Seed::Randoms(join_randoms(&randoms.client, &randoms.server)),
173 ),
174 };
175
176 let mut master_secret = [0u8; 48];
181 suite.prf_provider.for_key_exchange(
182 &mut master_secret,
183 kx,
184 peer_pub_key,
185 label.as_bytes(),
186 seed.as_ref(),
187 )?;
188 let master_secret = Zeroizing::new(master_secret);
189
190 let master_secret_prf = suite
191 .prf_provider
192 .new_secret(&master_secret);
193
194 Ok(Self {
195 randoms,
196 suite,
197 master_secret,
198 master_secret_prf,
199 })
200 }
201
202 pub(crate) fn new_resume(
203 randoms: ConnectionRandoms,
204 suite: &'static Tls12CipherSuite,
205 master_secret: &[u8; 48],
206 ) -> Self {
207 Self {
208 randoms,
209 suite,
210 master_secret: Zeroizing::new(*master_secret),
211 master_secret_prf: suite
212 .prf_provider
213 .new_secret(master_secret),
214 }
215 }
216
217 pub(crate) fn make_cipher_pair(&self, side: Side) -> RecordCipherPair {
220 let key_block = self.make_key_block();
223 let shape = self.suite.aead_alg.key_block_shape();
224
225 let (client_write_key, key_block) = key_block.split_at(shape.enc_key_len);
226 let (server_write_key, key_block) = key_block.split_at(shape.enc_key_len);
227 let (client_write_iv, key_block) = key_block.split_at(shape.fixed_iv_len);
228 let (server_write_iv, extra) = key_block.split_at(shape.fixed_iv_len);
229
230 let (write_key, write_iv, read_key, read_iv) = match side {
231 Side::Client => (
232 client_write_key,
233 client_write_iv,
234 server_write_key,
235 server_write_iv,
236 ),
237 Side::Server => (
238 server_write_key,
239 server_write_iv,
240 client_write_key,
241 client_write_iv,
242 ),
243 };
244
245 (
246 self.suite
247 .aead_alg
248 .decrypter(AeadKey::new(read_key), read_iv),
249 self.suite
250 .aead_alg
251 .encrypter(AeadKey::new(write_key), write_iv, extra),
252 )
253 }
254
255 fn make_key_block(&self) -> Zeroizing<Vec<u8>> {
256 let shape = self.suite.aead_alg.key_block_shape();
257
258 let len = (shape.enc_key_len + shape.fixed_iv_len) * 2 + shape.explicit_nonce_len;
259
260 let mut out = vec![0u8; len];
261
262 let randoms = join_randoms(&self.randoms.server, &self.randoms.client);
265 self.master_secret_prf
266 .prf(&mut out, b"key expansion", &randoms);
267
268 Zeroizing::new(out)
269 }
270
271 pub(crate) fn suite(&self) -> &'static Tls12CipherSuite {
272 self.suite
273 }
274
275 pub(crate) fn master_secret(&self) -> &[u8; 48] {
276 &self.master_secret
277 }
278
279 fn make_verify_data(
280 &self,
281 handshake_hash: &hash::Output,
282 label: &[u8],
283 _proof: &HandshakeAlignedProof,
284 ) -> [u8; 12] {
285 let mut out = [0u8; 12];
286 self.master_secret_prf
287 .prf(&mut out, label, handshake_hash.as_ref());
288 out
289 }
290
291 pub(crate) fn client_verify_data(
292 &self,
293 handshake_hash: &hash::Output,
294 proof: &HandshakeAlignedProof,
295 ) -> [u8; 12] {
296 self.make_verify_data(handshake_hash, b"client finished", proof)
297 }
298
299 pub(crate) fn server_verify_data(
300 &self,
301 handshake_hash: &hash::Output,
302 proof: &HandshakeAlignedProof,
303 ) -> [u8; 12] {
304 self.make_verify_data(handshake_hash, b"server finished", proof)
305 }
306
307 pub(crate) fn into_exporter(self) -> Box<dyn Exporter> {
308 let Self {
309 randoms,
310 master_secret_prf,
311 master_secret: _,
312 suite: _,
313 } = self;
314 Box::new(Tls12Exporter {
315 randoms,
316 master_secret_prf,
317 })
318 }
319
320 pub(crate) fn extract_secrets(&self, side: Side) -> Result<PartiallyExtractedSecrets, Error> {
321 let key_block = self.make_key_block();
323 let shape = self.suite.aead_alg.key_block_shape();
324
325 let (client_key, key_block) = key_block.split_at(shape.enc_key_len);
326 let (server_key, key_block) = key_block.split_at(shape.enc_key_len);
327 let (client_iv, key_block) = key_block.split_at(shape.fixed_iv_len);
328 let (server_iv, explicit_nonce) = key_block.split_at(shape.fixed_iv_len);
329
330 let client_secrets = self.suite.aead_alg.extract_keys(
331 AeadKey::new(client_key),
332 client_iv,
333 explicit_nonce,
334 )?;
335 let server_secrets = self.suite.aead_alg.extract_keys(
336 AeadKey::new(server_key),
337 server_iv,
338 explicit_nonce,
339 )?;
340
341 let (tx, rx) = match side {
342 Side::Client => (client_secrets, server_secrets),
343 Side::Server => (server_secrets, client_secrets),
344 };
345 Ok(PartiallyExtractedSecrets { tx, rx })
346 }
347}
348
349pub(crate) struct Tls12Exporter {
350 randoms: ConnectionRandoms,
351 master_secret_prf: Box<dyn PrfSecret>,
352}
353
354impl Exporter for Tls12Exporter {
355 fn derive(&self, label: &[u8], context: Option<&[u8]>, output: &mut [u8]) -> Result<(), Error> {
356 let mut randoms = Vec::with_capacity(
357 32 + 32
358 + context
359 .as_ref()
360 .map(|c| 2 + c.len())
361 .unwrap_or_default(),
362 );
363 randoms.extend_from_slice(&self.randoms.client);
364 randoms.extend_from_slice(&self.randoms.server);
365 if let Some(context) = context {
366 let Ok(len) = u16::try_from(context.len()) else {
367 return Err(ApiMisuse::ExporterContextTooLong.into());
368 };
369 len.encode(&mut randoms);
370 randoms.extend_from_slice(context);
371 }
372
373 self.master_secret_prf
374 .prf(output, label, &randoms);
375 Ok(())
376 }
377}
378
379enum Seed {
380 Ems(hash::Output),
381 Randoms([u8; 64]),
382}
383
384impl AsRef<[u8]> for Seed {
385 fn as_ref(&self) -> &[u8] {
387 match self {
388 Self::Ems(seed) => seed.as_ref(),
390 Self::Randoms(randoms) => randoms.as_ref(),
392 }
393 }
394}
395
396fn join_randoms(first: &[u8; 32], second: &[u8; 32]) -> [u8; 64] {
397 let mut randoms = [0u8; 64];
398 randoms[..32].copy_from_slice(first);
399 randoms[32..].copy_from_slice(second);
400 randoms
401}
402
403type RecordCipherPair = (Box<dyn RecordDecrypter>, Box<dyn RecordEncrypter>);
404
405pub(crate) fn decode_kx_params<'a, T: KxDecode<'a>>(
406 kx_algorithm: KeyExchangeAlgorithm,
407 kx_params: &'a [u8],
408) -> Result<T, Error> {
409 let mut rd = Reader::new(kx_params);
410 let kx_params = T::decode(&mut rd, kx_algorithm)?;
411 match rd.any_left() {
412 false => Ok(kx_params),
413 true => Err(InvalidMessage::InvalidDhParams.into()),
414 }
415}
416
417pub(crate) const DOWNGRADE_SENTINEL: [u8; 8] = [0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01];
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422 use crate::crypto::test_provider::KEY_EXCHANGE_GROUP;
423 use crate::msgs::{ServerEcdhParams, ServerKeyExchangeParams};
424
425 #[test]
426 fn server_ecdhe_remaining_bytes() {
427 let key = KEY_EXCHANGE_GROUP.start().unwrap();
428 let server_params = ServerEcdhParams::new(&*key);
429 let mut server_buf = Vec::new();
430 server_params.encode(&mut server_buf);
431 server_buf.push(34);
432
433 assert!(
434 decode_kx_params::<ServerKeyExchangeParams>(KeyExchangeAlgorithm::ECDHE, &server_buf)
435 .is_err()
436 );
437 }
438
439 #[test]
440 fn client_ecdhe_invalid() {
441 assert!(
442 decode_kx_params::<ServerKeyExchangeParams>(KeyExchangeAlgorithm::ECDHE, &[34],)
443 .is_err()
444 );
445 }
446}