Skip to content

Commit

Permalink
feat(dc): DcSupportedVersions transport parameter (#2193)
Browse files Browse the repository at this point in the history
* feat(dc): DcSupportedVersions transport parameter

* put vec functions behind alloc feature

* clean up

* check server_params len

* clippy

* update docs

* remove testing dc version

* support less than 4 versions

* store as u32 internally

* remove allocation

* allow for 0 version

* only iterator over len values
  • Loading branch information
WesleyRosenblum authored May 1, 2024
1 parent 1436af7 commit 7188ce4
Show file tree
Hide file tree
Showing 15 changed files with 435 additions and 5 deletions.
20 changes: 20 additions & 0 deletions quic/s2n-quic-core/src/crypto/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ pub trait TlsSession: Send {
//# implementation to communicate its buffering limits.
#[cfg(feature = "alloc")]
pub trait Context<Crypto: crate::crypto::CryptoSuite> {
/// Called when the client's application parameters are available, prior
/// to completion of the handshake.
///
/// The `server_params` is provided as a mutable Vec<u8> of encoded
/// server transport parameters to allow for additional parameters
/// dependent on the `client_params` to be appended before transmitting
/// them to the client.
///
/// The value of transport parameters is not authenticated until
/// the handshake completes, so any use of these parameters cannot
/// depend on their authenticity.
///
/// NOTE: This function is not currently supported
/// for the `s2n-quic-rustls` provider
fn on_client_application_params(
&mut self,
client_params: ApplicationParameters,
server_params: &mut alloc::vec::Vec<u8>,
) -> Result<(), crate::transport::Error>;

fn on_handshake_keys(
&mut self,
key: Crypto::HandshakeKey,
Expand Down
13 changes: 12 additions & 1 deletion quic/s2n-quic-core/src/crypto/tls/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use crate::{
application::ServerName,
crypto::{
header_crypto::{LONG_HEADER_MASK, SHORT_HEADER_MASK},
scatter, tls, CryptoSuite, HeaderKey, Key,
scatter, tls,
tls::ApplicationParameters,
CryptoSuite, HeaderKey, Key,
},
endpoint, transport,
transport::parameters::{ClientTransportParameters, ServerTransportParameters},
Expand Down Expand Up @@ -639,6 +641,15 @@ impl<C: CryptoSuite, State: Debug, Params> tls::Context<C> for Context<C, State,
where
for<'a> Params: DecoderValue<'a>,
{
fn on_client_application_params(
&mut self,
_client_params: ApplicationParameters,
_server_params: &mut Vec<u8>,
) -> Result<(), transport::Error> {
self.log("client application params");
Ok(())
}

fn on_handshake_keys(
&mut self,
key: C::HandshakeKey,
Expand Down
4 changes: 4 additions & 0 deletions quic/s2n-quic-core/src/event/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub mod api {
pub initial_max_streams_bidi: u64,
pub initial_max_streams_uni: u64,
pub max_datagram_frame_size: u64,
pub dc_supported_versions: &'a [u32],
}
#[derive(Clone, Debug)]
#[non_exhaustive]
Expand Down Expand Up @@ -2472,6 +2473,7 @@ pub mod builder {
pub initial_max_streams_bidi: u64,
pub initial_max_streams_uni: u64,
pub max_datagram_frame_size: u64,
pub dc_supported_versions: &'a [u32],
}
impl<'a> IntoEvent<api::TransportParameters<'a>> for TransportParameters<'a> {
#[inline]
Expand All @@ -2494,6 +2496,7 @@ pub mod builder {
initial_max_streams_bidi,
initial_max_streams_uni,
max_datagram_frame_size,
dc_supported_versions,
} = self;
api::TransportParameters {
original_destination_connection_id: original_destination_connection_id.into_event(),
Expand All @@ -2514,6 +2517,7 @@ pub mod builder {
initial_max_streams_bidi: initial_max_streams_bidi.into_event(),
initial_max_streams_uni: initial_max_streams_uni.into_event(),
max_datagram_frame_size: max_datagram_frame_size.into_event(),
dc_supported_versions: dc_supported_versions.into_event(),
}
}
}
Expand Down
153 changes: 151 additions & 2 deletions quic/s2n-quic-core/src/transport/parameters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use crate::{
use core::{mem::size_of, time::Duration};
use s2n_codec::{
decoder_invariant, decoder_value, DecoderBuffer, DecoderBufferMut, DecoderBufferMutResult,
DecoderBufferResult, DecoderError, DecoderValue, DecoderValueMut, Encoder, EncoderValue,
DecoderBufferResult, DecoderError, DecoderValue, DecoderValueMut, Encoder, EncoderBuffer,
EncoderValue,
};

#[cfg(test)]
Expand All @@ -27,7 +28,7 @@ pub trait TransportParameter: Sized {
const ENABLED: bool = true;

/// Associated type for decoding/encoding the TransportParameter
type CodecValue;
type CodecValue: EncoderValue;

/// Create a `TransportParameter` from the CodecValue
fn from_codec_value(value: Self::CodecValue) -> Self;
Expand All @@ -39,6 +40,18 @@ pub trait TransportParameter: Sized {
/// This is used instead of `Default::default` so it is
/// easily overridable
fn default_value() -> Self;

/// Appends this `TransportParameter` to the given buffer containing
/// already encoded TransportParameters
#[cfg(feature = "alloc")]
fn append_to_buffer(&self, buffer: &mut alloc::vec::Vec<u8>) {
let original_size = buffer.len();
let new_parameter_size = TransportParameterCodec(self).encoding_size();
buffer.resize(original_size + new_parameter_size, 0);
let mut buffer = EncoderBuffer::new(buffer);
buffer.set_position(original_size);
buffer.encode(&TransportParameterCodec(self));
}
}

/// Trait for validating transport parameter values
Expand Down Expand Up @@ -1063,6 +1076,139 @@ impl From<connection::id::LocalId> for InitialSourceConnectionId {
connection_id_parameter!(RetrySourceConnectionId, LocalId, 0x10);
optional_transport_parameter!(RetrySourceConnectionId);

/// Used by the client to indicate which versions of s2n-quic-dc it supports
/// and by the server to indicate which version it is using
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Eq, Ord)]
pub struct DcSupportedVersions {
len: u8,
versions: [u32; DC_SUPPORTED_VERSIONS_MAX_LEN as usize],
}
// The maximum number of supported versions that may be transmitted using
// the `DcSupportedVersions` transport parameter
const DC_SUPPORTED_VERSIONS_MAX_LEN: u8 = 4;

impl DcSupportedVersions {
/// Create a `DcSupportedVersions` for the given `supported_versions` the client supports
pub fn for_client<I: IntoIterator<Item = u32>>(supported_versions: I) -> Self {
let mut versions = [0; DC_SUPPORTED_VERSIONS_MAX_LEN as usize];
let mut len = 0;

for (index, version) in supported_versions.into_iter().enumerate() {
versions[index] = version;
len += 1;

debug_assert!(
len <= DC_SUPPORTED_VERSIONS_MAX_LEN,
"Only {DC_SUPPORTED_VERSIONS_MAX_LEN} supported versions are supported"
);
ensure!(len <= DC_SUPPORTED_VERSIONS_MAX_LEN, break);
}

DcSupportedVersions { len, versions }
}

/// Create a `DcSupportedVersions` for the `supported_version` the server has selected
pub fn for_server(supported_version: u32) -> Self {
DcSupportedVersions {
len: 1,
versions: [supported_version, 0, 0, 0],
}
}

/// The version the server has selected
///
/// Returns `None` if no version was selected
pub fn selected_version(&self) -> Result<Option<u32>, DecoderError> {
match self.len {
0 => Ok(None),
1 => Ok(Some(self.versions[0])),
_ => Err(DecoderError::InvariantViolation(
"multiple versions selected by the server",
)),
}
}
}

impl TransportParameter for DcSupportedVersions {
const ID: TransportParameterId = TransportParameterId::from_u32(0xdc0000);
type CodecValue = Self;

fn from_codec_value(value: Self::CodecValue) -> Self {
value
}

fn try_into_codec_value(&self) -> Option<&Self::CodecValue> {
if *self == Self::default_value() {
None
} else {
Some(self)
}
}

fn default_value() -> Self {
Self::default()
}
}

impl EncoderValue for DcSupportedVersions {
fn encode<E: Encoder>(&self, buffer: &mut E) {
for &version in self.versions.iter().take(self.len as usize) {
VarInt::from_u32(version).encode(buffer);
}
}
}

decoder_value!(
impl<'a> DcSupportedVersions {
fn decode(buffer: Buffer) -> Result<Self> {
let mut versions = [0; DC_SUPPORTED_VERSIONS_MAX_LEN as usize];
let mut len = 0;
let mut buffer = buffer;
while !buffer.is_empty() {
let (version, remaining) = buffer.decode::<VarInt>()?;
buffer = remaining;

decoder_invariant!(
version.as_u64() <= u32::MAX as u64,
"the largest supported version is u32::MAX"
);
versions[len] = version.as_u64() as u32;
len += 1;
ensure!(len < DC_SUPPORTED_VERSIONS_MAX_LEN as usize, break);
}

// Skip the rest of the buffer to allow for future versions of
// `DcSupportedVersions` that may support more than 4 versions
let remaining_capacity = buffer.len();
let buffer = buffer.skip(remaining_capacity)?;
Ok((
Self {
len: len as u8,
versions,
},
buffer,
))
}
}
);

impl TransportParameterValidator for DcSupportedVersions {}

impl<'a> IntoIterator for &'a DcSupportedVersions {
type Item = &'a u32;
type IntoIter = core::slice::Iter<'a, u32>;

fn into_iter(self) -> Self::IntoIter {
self.versions[..self.len as usize].iter()
}
}

impl<'a> IntoEvent<&'a [u32]> for &'a DcSupportedVersions {
fn into_event(self) -> &'a [u32] {
&self.versions[..self.len as usize]
}
}

//= https://www.rfc-editor.org/rfc/rfc9000#section-18.2
//# If present, transport parameters that set initial per-stream flow
//# control limits (initial_max_stream_data_bidi_local,
Expand Down Expand Up @@ -1244,6 +1390,7 @@ impl<'a> IntoEvent<event::builder::TransportParameters<'a>> for &'a ServerTransp
initial_max_streams_bidi: self.initial_max_streams_bidi.into_event(),
initial_max_streams_uni: self.initial_max_streams_uni.into_event(),
max_datagram_frame_size: self.max_datagram_frame_size.into_event(),
dc_supported_versions: self.dc_supported_versions.into_event(),
}
}
}
Expand Down Expand Up @@ -1275,6 +1422,7 @@ impl<'a> IntoEvent<event::builder::TransportParameters<'a>> for &'a ClientTransp
initial_max_streams_bidi: self.initial_max_streams_bidi.into_event(),
initial_max_streams_uni: self.initial_max_streams_uni.into_event(),
max_datagram_frame_size: self.max_datagram_frame_size.into_event(),
dc_supported_versions: self.dc_supported_versions.into_event(),
}
}
}
Expand Down Expand Up @@ -1419,6 +1567,7 @@ impl_transport_parameters!(
preferred_address: PreferredAddress,
initial_source_connection_id: Option<InitialSourceConnectionId>,
retry_source_connection_id: RetrySourceConnectionId,
dc_supported_versions: DcSupportedVersions,
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ TransportParameters {
preferred_address: DisabledParameter,
initial_source_connection_id: None,
retry_source_connection_id: DisabledParameter,
dc_supported_versions: DcSupportedVersions {
len: 0,
versions: [
0,
0,
0,
0,
],
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ TransportParameters {
preferred_address: None,
initial_source_connection_id: None,
retry_source_connection_id: None,
dc_supported_versions: DcSupportedVersions {
len: 0,
versions: [
0,
0,
0,
0,
],
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,13 @@ expression: encoded_output
2,
3,
4,
128,
220,
0,
0,
4,
1,
2,
3,
4,
]
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ TransportParameters {
preferred_address: DisabledParameter,
initial_source_connection_id: None,
retry_source_connection_id: DisabledParameter,
dc_supported_versions: DcSupportedVersions {
len: 0,
versions: [
0,
0,
0,
0,
],
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ TransportParameters {
preferred_address: None,
initial_source_connection_id: None,
retry_source_connection_id: None,
dc_supported_versions: DcSupportedVersions {
len: 0,
versions: [
0,
0,
0,
0,
],
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,10 @@ expression: encoded_output
2,
3,
4,
128,
220,
0,
0,
1,
3,
]
Loading

0 comments on commit 7188ce4

Please sign in to comment.