s2n-quic-rustls 0.85.0

Internal crate used by s2n-quic
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::cipher_suite::{
    HeaderProtectionKey, HeaderProtectionKeys, OneRttKey, PacketKey, PacketKeys,
};
use bytes::Bytes;
use core::{fmt, fmt::Debug, task::Poll};
use rustls::quic::{self, Connection};
use s2n_quic_core::{
    application::ServerName,
    crypto::{
        self, tls,
        tls::{CipherSuite, NamedGroup},
    },
    transport,
};

pub struct Session {
    connection: Connection,
    rx_phase: HandshakePhase,
    tx_phase: HandshakePhase,
    emitted_zero_rtt_keys: bool,
    emitted_handshake_complete: bool,
    emitted_server_name: bool,
    emitted_application_protocol: bool,
    emitted_key_exchange_group: bool,
    server_name: Option<ServerName>,
}

impl tls::TlsSession for Session {
    fn tls_exporter(
        &self,
        label: &[u8],
        context: &[u8],
        output: &mut [u8],
    ) -> Result<(), tls::TlsExportError> {
        match self
            .connection
            .export_keying_material(output, label, Some(context))
        {
            Ok(_) => Ok(()),
            Err(_) => Err(tls::TlsExportError::failure()),
        }
    }

    fn cipher_suite(&self) -> CipherSuite {
        if let Some(rustls_cipher_suite) = self.connection.negotiated_cipher_suite() {
            match rustls_cipher_suite.suite() {
                rustls::CipherSuite::TLS13_AES_128_GCM_SHA256 => {
                    CipherSuite::TLS_AES_128_GCM_SHA256
                }
                rustls::CipherSuite::TLS13_AES_256_GCM_SHA384 => {
                    CipherSuite::TLS_AES_256_GCM_SHA384
                }
                rustls::CipherSuite::TLS13_CHACHA20_POLY1305_SHA256 => {
                    CipherSuite::TLS_CHACHA20_POLY1305_SHA256
                }
                _ => CipherSuite::Unknown,
            }
        } else {
            CipherSuite::Unknown
        }
    }

    fn peer_cert_chain_der(&self) -> Result<Vec<Vec<u8>>, tls::ChainError> {
        let err = tls::ChainError::failure();
        Ok(self
            .connection
            .peer_certificates()
            .ok_or(err)?
            .iter()
            .map(|v| v.to_vec())
            .collect())
    }

    fn client_cert_chain_der(&self) -> Result<Option<Vec<u8>>, tls::ChainError> {
        // As far as I can tell, rustls doesn't support retrieving unverified cert chains.
        Err(tls::ChainError::failure())
    }
}

impl fmt::Debug for Session {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Session")
            .field("rx_phase", &self.rx_phase)
            .field("tx_phase", &self.tx_phase)
            .finish()
    }
}

impl Session {
    pub fn new(connection: Connection, server_name: Option<ServerName>) -> Self {
        Self {
            connection,
            rx_phase: Default::default(),
            tx_phase: Default::default(),
            emitted_zero_rtt_keys: false,
            emitted_handshake_complete: false,
            emitted_server_name: false,
            emitted_application_protocol: false,
            emitted_key_exchange_group: false,
            server_name,
        }
    }

    fn receive(&mut self, crypto_data: &[u8]) -> Result<(), transport::Error> {
        self.connection
            .read_hs(crypto_data)
            .map_err(crate::error::reason)
            .map_err(|reason| {
                //= https://www.rfc-editor.org/rfc/rfc9001#section-4.8
                //# QUIC is only able to convey an alert level of "fatal".  In TLS 1.3,
                //# the only existing uses for the "warning" level are to signal
                //# connection close; see Section 6.1 of [TLS13].  As QUIC provides
                //# alternative mechanisms for connection termination and the TLS
                //# connection is only closed if an error is encountered, a QUIC endpoint
                //# MUST treat any alert from TLS as if it were at the "fatal" level.

                // According to the rustls docs, `alert` only returns fatal alerts:
                // > https://docs.rs/rustls/0.19.0/rustls/quic/trait.QuicExt#tymethod.get_alert
                // > Emit the TLS description code of a fatal alert, if one has arisen.

                self.connection
                    .alert()
                    .map(|alert| {
                        // Explicitly annotate the type to detect if rustls starts
                        // returning a large array
                        let code: [u8; 1] = alert.to_array();
                        let code = code[0];
                        tls::Error { code, reason }
                    })
                    .unwrap_or(tls::Error::INTERNAL_ERROR)
            })?;
        Ok(())
    }

    fn application_parameters(&self) -> Result<tls::ApplicationParameters<'_>, transport::Error> {
        //= https://www.rfc-editor.org/rfc/rfc9001#section-8.2
        //# endpoints that
        //# receive ClientHello or EncryptedExtensions messages without the
        //# quic_transport_parameters extension MUST close the connection with an
        //# error of type 0x16d (equivalent to a fatal TLS missing_extension
        //# alert, see Section 4.8).
        let transport_parameters =
            self.connection.quic_transport_parameters().ok_or_else(|| {
                tls::Error::MISSING_EXTENSION.with_reason("Missing QUIC transport parameters")
            })?;

        Ok(tls::ApplicationParameters {
            transport_parameters,
        })
    }

    //= https://www.rfc-editor.org/rfc/rfc9001#section-8.1
    //# Unless
    //# another mechanism is used for agreeing on an application protocol,
    //# endpoints MUST use ALPN for this purpose.
    //
    //= https://www.rfc-editor.org/rfc/rfc7301#section-3.1
    //# Client                                              Server
    //#
    //#    ClientHello                     -------->       ServerHello
    //#      (ALPN extension &                               (ALPN extension &
    //#       list of protocols)                              selected protocol)
    //#                                                    [ChangeCipherSpec]
    //#                                    <--------       Finished
    //#    [ChangeCipherSpec]
    //#    Finished                        -------->
    //#    Application Data                <------->       Application Data
    fn application_protocol(&self) -> Option<&[u8]> {
        self.connection.alpn_protocol()
    }

    fn server_name(&self) -> Option<ServerName> {
        match &self.connection {
            Connection::Client(_) => self.server_name.clone(),
            Connection::Server(server) => {
                server.server_name().map(|server_name| server_name.into())
            }
        }
    }

    fn key_exchange_group(&self) -> Option<NamedGroup> {
        let group = self
            .connection
            .negotiated_key_exchange_group()
            .and_then(|group| group.name().as_str())?;

        let contains_kem = group.to_lowercase().contains("kem");

        Some(NamedGroup {
            group_name: group,
            contains_kem,
        })
    }

    fn zero_rtt_keys(&mut self) -> Option<quic::DirectionalKeys> {
        if self.emitted_zero_rtt_keys {
            return None;
        }

        let keys = self.connection.zero_rtt_keys()?;
        self.emitted_zero_rtt_keys = true;
        Some(keys)
    }

    /// Check and process TLS handshake complete.
    ///
    /// Upon TLS handshake complete, emit an event to notify the transport layer.
    fn poll_complete_handshake<C: tls::Context<Self>>(
        &mut self,
        context: &mut C,
    ) -> Poll<Result<(), transport::Error>> {
        if self.tx_phase == HandshakePhase::Application && !self.connection.is_handshaking() {
            // attempt to emit server_name and application_protocol events prior to completing the
            // handshake
            self.emit_events(context)?;

            // the handshake is complete!
            if !self.emitted_handshake_complete {
                self.rx_phase.transition();
                context.on_handshake_complete()?;
                context.on_tls_exporter_ready(self)?;
            }

            self.emitted_handshake_complete = true;
        }

        if self.emitted_handshake_complete {
            Poll::Ready(Ok(()))
        } else {
            Poll::Pending
        }
    }

    fn poll_impl<C: tls::Context<Self>>(
        &mut self,
        context: &mut C,
    ) -> Poll<Result<(), transport::Error>> {
        // Tracks if we have attempted to receive data at least once
        let mut has_tried_receive = false;

        loop {
            let crypto_data = match self.rx_phase {
                HandshakePhase::Initial => context.receive_initial(None),
                HandshakePhase::Handshake => context.receive_handshake(None),
                HandshakePhase::Application => context.receive_application(None),
            };

            // receive anything in the incoming buffer
            if let Some(crypto_data) = crypto_data {
                self.receive(&crypto_data)?;
            } else if has_tried_receive {
                return self.poll_complete_handshake(context);
                // If there's nothing to receive then we're done for now
            }

            if let Poll::Ready(()) = self.poll_complete_handshake(context)? {
                return Poll::Ready(Ok(()));
            }

            // mark that we tried to receive some data so we know next time we loop
            // to bail if nothing changed
            has_tried_receive = true;

            // try to pull out the early secrets, if any
            if let Some(keys) = self.zero_rtt_keys() {
                let (key, header_key) = PacketKey::new(
                    keys,
                    s2n_quic_core::crypto::tls::CipherSuite::TLS_AES_128_GCM_SHA256,
                );
                context.on_zero_rtt_keys(key, header_key, self.application_parameters()?)?;
            }

            loop {
                // make sure we can send data before pulling it out of rustls
                let can_send = match self.tx_phase {
                    HandshakePhase::Initial => context.can_send_initial(),
                    HandshakePhase::Handshake => context.can_send_handshake(),
                    HandshakePhase::Application => context.can_send_application(),
                };

                if !can_send {
                    break;
                }

                let mut transmission_buffer = vec![];

                let key_change = self.connection.write_hs(&mut transmission_buffer);

                // if we didn't upgrade the key or transmit anything then we're waiting for
                // more reads
                if key_change.is_none() && transmission_buffer.is_empty() {
                    break;
                }

                // fill the correct buffer according to the handshake phase
                match self.tx_phase {
                    HandshakePhase::Initial => context.send_initial(transmission_buffer.into()),
                    HandshakePhase::Handshake => context.send_handshake(transmission_buffer.into()),
                    HandshakePhase::Application => {
                        context.send_application(transmission_buffer.into())
                    }
                }

                if let Some(key_change) = key_change {
                    let cipher_suite = self
                        .connection
                        .negotiated_cipher_suite()
                        .expect("cipher_suite should be negotiated")
                        .suite();
                    match key_change {
                        quic::KeyChange::Handshake { keys } => {
                            let (key, header_key) = PacketKeys::new(keys, cipher_suite);

                            context.on_handshake_keys(key, header_key)?;

                            // Transition both phases to Handshake
                            self.tx_phase.transition();
                            self.rx_phase.transition();
                        }
                        quic::KeyChange::OneRtt { keys, next } => {
                            let (key, header_key) = OneRttKey::new(keys, next, cipher_suite);

                            // at this point we should have both SNI and ALPN values
                            self.emit_events(context)?;

                            let application_parameters = self.application_parameters()?;

                            context.on_one_rtt_keys(key, header_key, application_parameters)?;

                            // Transition the tx_phase to Application
                            // Note: the rx_phase is transitioned when the handshake is complete
                            self.tx_phase.transition();
                        }
                    }
                }
            }
        }
    }

    fn emit_events<C: tls::Context<Self>>(
        &mut self,
        context: &mut C,
    ) -> Result<(), transport::Error> {
        if !self.emitted_server_name {
            if let Some(server_name) = self.server_name() {
                context.on_server_name(server_name)?;
                self.emitted_server_name = true;
            }
        }
        if !self.emitted_application_protocol {
            if let Some(application_protocol) = self.application_protocol() {
                let application_protocol = Bytes::copy_from_slice(application_protocol);
                context.on_application_protocol(application_protocol)?;
                self.emitted_application_protocol = true;
            }
        }
        if !self.emitted_key_exchange_group {
            if let Some(key_exchange_group) = self.key_exchange_group() {
                context.on_key_exchange_group(key_exchange_group)?;
                self.emitted_key_exchange_group = true;
            }
        }

        Ok(())
    }
}

impl crypto::CryptoSuite for Session {
    type HandshakeKey = PacketKeys;
    type HandshakeHeaderKey = HeaderProtectionKeys;
    type InitialKey = s2n_quic_crypto::initial::InitialKey;
    type InitialHeaderKey = s2n_quic_crypto::initial::InitialHeaderKey;
    type OneRttKey = OneRttKey;
    type OneRttHeaderKey = HeaderProtectionKeys;
    type ZeroRttKey = PacketKey;
    type ZeroRttHeaderKey = HeaderProtectionKey;
    type RetryKey = s2n_quic_crypto::retry::RetryKey;
}

impl tls::Session for Session {
    fn poll<C: tls::Context<Self>>(
        &mut self,
        context: &mut C,
    ) -> Poll<Result<(), transport::Error>> {
        let result = self.poll_impl(context);
        if let Poll::Ready(Err(e)) = &result {
            context.on_tls_handshake_failed(self, e)?;
        }
        // attempt to emit server_name and application_protocol events prior to possibly
        // returning with an error
        self.emit_events(context)?;
        result
    }
}

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
enum HandshakePhase {
    #[default]
    Initial,
    Handshake,
    Application,
}

impl HandshakePhase {
    fn transition(&mut self) {
        *self = match self {
            Self::Initial => Self::Handshake,
            _ => Self::Application,
        };
    }
}