Skip to main content

rustls/
tls12.rs

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/// A TLS 1.2 cipher suite supported by rustls.
22#[expect(clippy::exhaustive_structs)]
23pub struct Tls12CipherSuite {
24    /// Common cipher suite fields.
25    pub common: CipherSuiteCommon,
26
27    /// The associated protocol version.
28    ///
29    /// This field should have the value [`rustls::version::TLS12_VERSION`].
30    ///
31    /// This value contains references to the TLS1.2 protocol handling code.
32    /// This means that a program that does not contain any `Tls12CipherSuite`
33    /// values also does not contain any reference to the TLS1.2 protocol handling
34    /// code, and the linker can remove it.
35    ///
36    /// [`rustls::version::TLS12_VERSION`]: crate::version::TLS12_VERSION
37    pub protocol_version: &'static Tls12Version,
38
39    /// How to compute the TLS1.2 PRF for the suite's hash function.
40    ///
41    /// If you have a TLS1.2 PRF implementation, you should directly implement the [`crypto::tls12::Prf`] trait.
42    ///
43    /// If not, you can implement the [`crypto::hmac::Hmac`] trait (and associated), and then use
44    /// [`crypto::tls12::PrfUsingHmac`].
45    pub prf_provider: &'static dyn crypto::tls12::Prf,
46
47    /// How to exchange/agree keys.
48    ///
49    /// In TLS1.2, the key exchange method (eg, Elliptic Curve Diffie-Hellman with Ephemeral keys -- ECDHE)
50    /// is baked into the cipher suite, but the details to achieve it are negotiated separately.
51    ///
52    /// This controls how protocol messages (like the `ClientKeyExchange` message) are interpreted
53    /// once this cipher suite has been negotiated.
54    pub kx: KeyExchangeAlgorithm,
55
56    /// How to sign messages for authentication.
57    ///
58    /// This is a set of [`SignatureScheme`]s that are usable once this cipher suite has been
59    /// negotiated.
60    ///
61    /// The precise scheme used is then chosen from this set by the selected authentication key.
62    pub sign: &'static [SignatureScheme],
63
64    /// How to produce a [`RecordDecrypter`] or [`RecordEncrypter`]
65    /// from raw key material.
66    pub aead_alg: &'static dyn Tls12AeadAlgorithm,
67}
68
69impl Tls12CipherSuite {
70    /// Resolve the set of supported [`SignatureScheme`]s from the
71    /// offered signature schemes.  If we return an empty
72    /// set, the handshake terminates.
73    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    /// Return the FIPS validation status of this implementation.
82    ///
83    /// This is the combination of the constituent parts of the cipher suite.
84    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    /// Does this suite support the `proto` protocol?
100    ///
101    /// All TLS1.2 suites support TCP-TLS. No TLS1.2 suites support QUIC.
102    fn usable_for_protocol(&self, proto: Protocol) -> bool {
103        matches!(proto, Protocol::Tcp)
104    }
105
106    /// Say if the given `KeyExchangeAlgorithm` is supported by this cipher suite.
107    fn usable_for_kx_algorithm(&self, kxa: KeyExchangeAlgorithm) -> bool {
108        self.kx == kxa
109    }
110
111    /// Return true if this suite is usable for a key only offering `sig_alg`
112    /// signatures.
113    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
150/// TLS1.2 per-connection keying material
151pub(crate) struct ConnectionSecrets {
152    pub(crate) randoms: ConnectionRandoms,
153    suite: &'static Tls12CipherSuite,
154    master_secret: Zeroizing<[u8; 48]>,
155
156    /// `master_secret` ready to be used as a TLS1.2 PRF secret.
157    ///
158    /// Zeroizing this on drop is left to the implementer of the trait.
159    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        // The API contract for for_key_exchange is that the caller guarantees `label` and `seed`
179        // slice parameters are non-empty.
180        // `label` is guaranteed non-empty because it's assigned from a `&str` above.
181        // `seed.as_ref()` is guaranteed non-empty by documentation on the AsRef impl.
182        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    /// Make a `RecordCipherPair` based on the given supported ciphersuite `self.suite`,
220    /// and the session's `secrets`.
221    pub(crate) fn make_cipher_pair(&self, side: Side) -> RecordCipherPair {
222        // Make a key block, and chop it up.
223        // Note: we don't implement any ciphersuites with nonzero mac_key_len.
224        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        // NOTE: opposite order to above for no good reason.
265        // Don't design security protocols on drugs, kids.
266        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        // Make a key block, and chop it up
324        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    /// This is guaranteed to return a non-empty slice.
388    fn as_ref(&self) -> &[u8] {
389        match self {
390            // seed is a hash::Output, which is a fixed, non-zero length array.
391            Self::Ems(seed) => seed.as_ref(),
392            // randoms is a fixed, non-zero length array.
393            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}