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