Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise and add additional 0-rtt doc comments #1826

Merged
merged 2 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions quinn-proto/src/crypto/rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,19 @@ pub struct HandshakeData {

/// A QUIC-compatible TLS client configuration
///
/// Can be constructed via [`ClientConfig::with_root_certificates()`][root_certs],
/// [`ClientConfig::with_platform_verifier()`][platform] or by using the [`TryFrom`] implementation with a
/// custom [`rustls::ClientConfig`]. A pre-existing `ClientConfig` must have TLS 1.3 support enabled for
/// this to work. 0-RTT support is available if `enable_early_data` is set to `true`.
/// Quinn implicitly constructs a `QuicClientConfig` with reasonable defaults within
/// [`ClientConfig::with_root_certificates()`][root_certs] and [`ClientConfig::with_platform_verifier()`][platform].
/// Alternatively, `QuicClientConfig`'s [`TryFrom`] implementation can be used to wrap around a
/// custom [`rustls::ClientConfig`], in which case care should be taken around certain points:
///
/// - If `enable_early_data` is not set to true, then sending 0-RTT data will not be possible on
/// outgoing connections.
/// - The [`rustls::ClientConfig`] must have TLS 1.3 support enabled for conversion to succeed.
///
/// The object in the `resumption` field of the inner [`rustls::ClientConfig`] determines whether
/// calling `into_0rtt` on outgoing connections returns `Ok` or `Err`. It typically allows
/// `into_0rtt` to proceed if it recognizes the server name, and defaults to an in-memory cache of
/// 256 server names.
///
/// [root_certs]: crate::config::ClientConfig::with_root_certificates()
/// [platform]: crate::config::ClientConfig::with_platform_verifier()
Expand Down Expand Up @@ -386,10 +395,14 @@ impl std::error::Error for NoInitialCipherSuite {}

/// A QUIC-compatible TLS server configuration
///
/// Can be constructed via [`ServerConfig::with_single_cert()`][single] or by using the
/// [`TryFrom`] implementation with a custom [`rustls::ServerConfig`]. A pre-existing
/// `ServerConfig` must have TLS 1.3 support enabled for this to work. 0-RTT support is
/// available to clients if `max_early_data_size` is set to `u32::MAX`.
/// Quinn implicitly constructs a `QuicServerConfig` with reasonable defaults within
/// [`ServerConfig::with_single_cert()`][single]. Alternatively, `QuicServerConfig`'s [`TryFrom`]
/// implementation or `with_initial` method can be used to wrap around a custom
/// [`rustls::ServerConfig`], in which case care should be taken around certain points:
///
/// - If `max_early_data_size` is not set to `u32::MAX`, the server will not be able to accept
/// incoming 0-RTT data. QUIC prohibits `max_early_data_size` values other than 0 or `u32::MAX`.
Ralith marked this conversation as resolved.
Show resolved Hide resolved
/// - The `rustls::ServerConfig` must have TLS 1.3 support enabled for conversion to succeed.
///
/// [single]: crate::config::ServerConfig::with_single_cert()
pub struct QuicServerConfig {
Expand Down
51 changes: 35 additions & 16 deletions quinn/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,29 +79,48 @@ impl Connecting {

/// Convert into a 0-RTT or 0.5-RTT connection at the cost of weakened security
///
/// Opens up the connection for use before the handshake finishes, allowing the API user to
/// send data with 0-RTT encryption if the necessary key material is available. This is useful
/// for reducing start-up latency by beginning transmission of application data without waiting
/// for the handshake's cryptographic security guarantees to be established.
/// Returns `Ok` immediately if the local endpoint is able to attempt sending 0/0.5-RTT data.
/// If so, the returned [`Connection`] can be used to send application data without waiting for
/// the rest of the handshake to complete, at the cost of weakened cryptographic security
/// guarantees. The returned [`ZeroRttAccepted`] future resolves when the handshake does
/// complete, at which point subsequently opened streams and written data will have full
/// cryptographic protection.
///
/// When the `ZeroRttAccepted` future completes, the connection has been fully established.
/// ## Outgoing
///
/// # Security
/// For outgoing connections, the initial attempt to convert to a [`Connection`] which sends
/// 0-RTT data will proceed if the [`crypto::ClientConfig`][crate::crypto::ClientConfig]
/// attempts to resume a previous TLS session. However, **the remote endpoint may not actually
/// _accept_ the 0-RTT data**--yet still accept the connection attempt in general. This
/// possibility is conveyed through the [`ZeroRttAccepted`] future--when the handshake
/// completes, it resolves to true if the 0-RTT data was accepted and false if it was rejected.
/// If it was rejected, the existence of streams opened and other application data sent prior
/// to the handshake completing will not be conveyed to the remote application, and local
/// operations on them will return `ZeroRttRejected` errors.
///
/// On outgoing connections, this enables transmission of 0-RTT data, which might be vulnerable
/// to replay attacks, and should therefore never invoke non-idempotent operations.
/// A server may reject 0-RTT data at its discretion, but accepting 0-RTT data requires the
/// relevant resumption state to be stored in the server, which servers may limit or lose for
/// various reasons including not persisting resumption state across server restarts.
///
/// On incoming connections, this enables transmission of 0.5-RTT data, which might be
/// intercepted by a man-in-the-middle. If this occurs, the handshake will not complete
/// successfully.
/// If manually providing a [`crypto::ClientConfig`][crate::crypto::ClientConfig], check your
/// implementation's docs for 0-RTT pitfalls.
///
/// # Errors
/// ## Incoming
///
/// Outgoing connections are only 0-RTT-capable when a cryptographic session ticket cached from
/// a previous connection to the same server is available, and includes a 0-RTT key. If no such
/// ticket is found, `self` is returned unmodified.
/// For incoming connections, conversion to 0.5-RTT will always fully succeed. `into_0rtt` will
/// always return `Ok` and the [`ZeroRttAccepted`] will always resolve to true.
///
/// For incoming connections, a 0.5-RTT connection will always be successfully constructed.
/// If manually providing a [`crypto::ServerConfig`][crate::crypto::ServerConfig], check your
/// implementation's docs for 0-RTT pitfalls.
///
/// ## Security
///
/// On outgoing connections, this enables transmission of 0-RTT data, which is vulnerable to
/// replay attacks, and should therefore never invoke non-idempotent operations.
///
/// On incoming connections, this enables transmission of 0.5-RTT data, which may be sent
/// before TLS client authentication has occurred, and should therefore not be used to send
/// data for which client authentication is being used.
pub fn into_0rtt(mut self) -> Result<(Connection, ZeroRttAccepted), Self> {
// This lock borrows `self` and would normally be dropped at the end of this scope, so we'll
// have to release it explicitly before returning `self` by value.
Expand Down
14 changes: 9 additions & 5 deletions quinn/src/send_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,16 @@ impl SendStream {
conn.inner.send_stream(self.stream).priority()
}

/// Completes when the stream is stopped or read to completion by the peer
/// Completes when the peer stops the stream or reads the stream to completion
///
/// Yields `Some` with the stop error code when the stream is stopped by the peer. Yields `None`
/// when the stream is [`finish()`](Self::finish)ed locally and all stream data has been
/// received (but not necessarily processed) by the peer, after which it is no longer meaningful
/// for the stream to be stopped.
/// Yields `Some` with the stop error code if the peer stops the stream. Yields `None` if the
/// local side [`finish()`](Self::finish)es the stream and then the peer acknowledges receipt
/// of all stream data (although not necessarily the processing of it), after which the peer
/// closing the stream is no longer meaningful.
///
/// For a variety of reasons, the peer may not send acknowledgements immediately upon receiving
/// data. As such, relying on `stopped` to know when the peer has read a stream to completion
/// may introduce more latency than using an application-level response of some sort.
pub async fn stopped(&mut self) -> Result<Option<VarInt>, StoppedError> {
Stopped { stream: self }.await
}
Expand Down