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