rustls/
tls12.rs

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/// A TLS 1.2 cipher suite supported by rustls.
23#[expect(clippy::exhaustive_structs)]
24pub struct Tls12CipherSuite {
25    /// Common cipher suite fields.
26    pub common: CipherSuiteCommon,
27
28    /// The associated protocol version.
29    ///
30    /// This field should have the value [`rustls::version::TLS12_VERSION`].
31    ///
32    /// This value contains references to the TLS1.2 protocol handling code.
33    /// This means that a program that does not contain any `Tls12CipherSuite`
34    /// values also does not contain any reference to the TLS1.2 protocol handling
35    /// code, and the linker can remove it.
36    ///
37    /// [`rustls::version::TLS12_VERSION`]: crate::version::TLS12_VERSION
38    pub protocol_version: &'static Tls12Version,
39
40    /// How to compute the TLS1.2 PRF for the suite's hash function.
41    ///
42    /// If you have a TLS1.2 PRF implementation, you should directly implement the [`crypto::tls12::Prf`] trait.
43    ///
44    /// If not, you can implement the [`crypto::hmac::Hmac`] trait (and associated), and then use
45    /// [`crypto::tls12::PrfUsingHmac`].
46    pub prf_provider: &'static dyn crypto::tls12::Prf,
47
48    /// How to exchange/agree keys.
49    ///
50    /// In TLS1.2, the key exchange method (eg, Elliptic Curve Diffie-Hellman with Ephemeral keys -- ECDHE)
51    /// is baked into the cipher suite, but the details to achieve it are negotiated separately.
52    ///
53    /// This controls how protocol messages (like the `ClientKeyExchange` message) are interpreted
54    /// once this cipher suite has been negotiated.
55    pub kx: KeyExchangeAlgorithm,
56
57    /// How to sign messages for authentication.
58    ///
59    /// This is a set of [`SignatureScheme`]s that are usable once this cipher suite has been
60    /// negotiated.
61    ///
62    /// The precise scheme used is then chosen from this set by the selected authentication key.
63    pub sign: &'static [SignatureScheme],
64
65    /// How to produce a [`MessageDecrypter`] or [`MessageEncrypter`]
66    /// from raw key material.
67    pub aead_alg: &'static dyn Tls12AeadAlgorithm,
68}
69
70impl Tls12CipherSuite {
71    /// Resolve the set of supported [`SignatureScheme`]s from the
72    /// offered signature schemes.  If we return an empty
73    /// set, the handshake terminates.
74    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    /// Return `true` if this is backed by a FIPS-approved implementation.
83    ///
84    /// This means all the constituent parts that do cryptography return `true` for `fips()`.
85    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    /// 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        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
146/// TLS1.2 per-connection keying material
147pub(crate) struct ConnectionSecrets {
148    pub(crate) randoms: ConnectionRandoms,
149    suite: &'static Tls12CipherSuite,
150    master_secret: Zeroizing<[u8; 48]>,
151
152    /// `master_secret` ready to be used as a TLS1.2 PRF secret.
153    ///
154    /// Zeroizing this on drop is left to the implementer of the trait.
155    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        // The API contract for for_key_exchange is that the caller guarantees `label` and `seed`
175        // slice parameters are non-empty.
176        // `label` is guaranteed non-empty because it's assigned from a `&str` above.
177        // `seed.as_ref()` is guaranteed non-empty by documentation on the AsRef impl.
178        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    /// Make a `MessageCipherPair` based on the given supported ciphersuite `self.suite`,
216    /// and the session's `secrets`.
217    pub(crate) fn make_cipher_pair(&self, side: Side) -> MessageCipherPair {
218        // Make a key block, and chop it up.
219        // Note: we don't implement any ciphersuites with nonzero mac_key_len.
220        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        // NOTE: opposite order to above for no good reason.
261        // Don't design security protocols on drugs, kids.
262        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        // Make a key block, and chop it up
320        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    /// This is guaranteed to return a non-empty slice.
384    fn as_ref(&self) -> &[u8] {
385        match self {
386            // seed is a hash::Output, which is a fixed, non-zero length array.
387            Self::Ems(seed) => seed.as_ref(),
388            // randoms is a fixed, non-zero length array.
389            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}