Skip to content

Commit 7188ce4

Browse files
feat(dc): DcSupportedVersions transport parameter (#2193)
* 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
1 parent 1436af7 commit 7188ce4

15 files changed

+435
-5
lines changed

quic/s2n-quic-core/src/crypto/tls.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,26 @@ pub trait TlsSession: Send {
5555
//# implementation to communicate its buffering limits.
5656
#[cfg(feature = "alloc")]
5757
pub trait Context<Crypto: crate::crypto::CryptoSuite> {
58+
/// Called when the client's application parameters are available, prior
59+
/// to completion of the handshake.
60+
///
61+
/// The `server_params` is provided as a mutable Vec<u8> of encoded
62+
/// server transport parameters to allow for additional parameters
63+
/// dependent on the `client_params` to be appended before transmitting
64+
/// them to the client.
65+
///
66+
/// The value of transport parameters is not authenticated until
67+
/// the handshake completes, so any use of these parameters cannot
68+
/// depend on their authenticity.
69+
///
70+
/// NOTE: This function is not currently supported
71+
/// for the `s2n-quic-rustls` provider
72+
fn on_client_application_params(
73+
&mut self,
74+
client_params: ApplicationParameters,
75+
server_params: &mut alloc::vec::Vec<u8>,
76+
) -> Result<(), crate::transport::Error>;
77+
5878
fn on_handshake_keys(
5979
&mut self,
6080
key: Crypto::HandshakeKey,

quic/s2n-quic-core/src/crypto/tls/testing.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use crate::{
77
application::ServerName,
88
crypto::{
99
header_crypto::{LONG_HEADER_MASK, SHORT_HEADER_MASK},
10-
scatter, tls, CryptoSuite, HeaderKey, Key,
10+
scatter, tls,
11+
tls::ApplicationParameters,
12+
CryptoSuite, HeaderKey, Key,
1113
},
1214
endpoint, transport,
1315
transport::parameters::{ClientTransportParameters, ServerTransportParameters},
@@ -639,6 +641,15 @@ impl<C: CryptoSuite, State: Debug, Params> tls::Context<C> for Context<C, State,
639641
where
640642
for<'a> Params: DecoderValue<'a>,
641643
{
644+
fn on_client_application_params(
645+
&mut self,
646+
_client_params: ApplicationParameters,
647+
_server_params: &mut Vec<u8>,
648+
) -> Result<(), transport::Error> {
649+
self.log("client application params");
650+
Ok(())
651+
}
652+
642653
fn on_handshake_keys(
643654
&mut self,
644655
key: C::HandshakeKey,

quic/s2n-quic-core/src/event/generated.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub mod api {
4646
pub initial_max_streams_bidi: u64,
4747
pub initial_max_streams_uni: u64,
4848
pub max_datagram_frame_size: u64,
49+
pub dc_supported_versions: &'a [u32],
4950
}
5051
#[derive(Clone, Debug)]
5152
#[non_exhaustive]
@@ -2472,6 +2473,7 @@ pub mod builder {
24722473
pub initial_max_streams_bidi: u64,
24732474
pub initial_max_streams_uni: u64,
24742475
pub max_datagram_frame_size: u64,
2476+
pub dc_supported_versions: &'a [u32],
24752477
}
24762478
impl<'a> IntoEvent<api::TransportParameters<'a>> for TransportParameters<'a> {
24772479
#[inline]
@@ -2494,6 +2496,7 @@ pub mod builder {
24942496
initial_max_streams_bidi,
24952497
initial_max_streams_uni,
24962498
max_datagram_frame_size,
2499+
dc_supported_versions,
24972500
} = self;
24982501
api::TransportParameters {
24992502
original_destination_connection_id: original_destination_connection_id.into_event(),
@@ -2514,6 +2517,7 @@ pub mod builder {
25142517
initial_max_streams_bidi: initial_max_streams_bidi.into_event(),
25152518
initial_max_streams_uni: initial_max_streams_uni.into_event(),
25162519
max_datagram_frame_size: max_datagram_frame_size.into_event(),
2520+
dc_supported_versions: dc_supported_versions.into_event(),
25172521
}
25182522
}
25192523
}

quic/s2n-quic-core/src/transport/parameters/mod.rs

Lines changed: 151 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use crate::{
1212
use core::{mem::size_of, time::Duration};
1313
use s2n_codec::{
1414
decoder_invariant, decoder_value, DecoderBuffer, DecoderBufferMut, DecoderBufferMutResult,
15-
DecoderBufferResult, DecoderError, DecoderValue, DecoderValueMut, Encoder, EncoderValue,
15+
DecoderBufferResult, DecoderError, DecoderValue, DecoderValueMut, Encoder, EncoderBuffer,
16+
EncoderValue,
1617
};
1718

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

2930
/// Associated type for decoding/encoding the TransportParameter
30-
type CodecValue;
31+
type CodecValue: EncoderValue;
3132

3233
/// Create a `TransportParameter` from the CodecValue
3334
fn from_codec_value(value: Self::CodecValue) -> Self;
@@ -39,6 +40,18 @@ pub trait TransportParameter: Sized {
3940
/// This is used instead of `Default::default` so it is
4041
/// easily overridable
4142
fn default_value() -> Self;
43+
44+
/// Appends this `TransportParameter` to the given buffer containing
45+
/// already encoded TransportParameters
46+
#[cfg(feature = "alloc")]
47+
fn append_to_buffer(&self, buffer: &mut alloc::vec::Vec<u8>) {
48+
let original_size = buffer.len();
49+
let new_parameter_size = TransportParameterCodec(self).encoding_size();
50+
buffer.resize(original_size + new_parameter_size, 0);
51+
let mut buffer = EncoderBuffer::new(buffer);
52+
buffer.set_position(original_size);
53+
buffer.encode(&TransportParameterCodec(self));
54+
}
4255
}
4356

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

1079+
/// Used by the client to indicate which versions of s2n-quic-dc it supports
1080+
/// and by the server to indicate which version it is using
1081+
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Eq, Ord)]
1082+
pub struct DcSupportedVersions {
1083+
len: u8,
1084+
versions: [u32; DC_SUPPORTED_VERSIONS_MAX_LEN as usize],
1085+
}
1086+
// The maximum number of supported versions that may be transmitted using
1087+
// the `DcSupportedVersions` transport parameter
1088+
const DC_SUPPORTED_VERSIONS_MAX_LEN: u8 = 4;
1089+
1090+
impl DcSupportedVersions {
1091+
/// Create a `DcSupportedVersions` for the given `supported_versions` the client supports
1092+
pub fn for_client<I: IntoIterator<Item = u32>>(supported_versions: I) -> Self {
1093+
let mut versions = [0; DC_SUPPORTED_VERSIONS_MAX_LEN as usize];
1094+
let mut len = 0;
1095+
1096+
for (index, version) in supported_versions.into_iter().enumerate() {
1097+
versions[index] = version;
1098+
len += 1;
1099+
1100+
debug_assert!(
1101+
len <= DC_SUPPORTED_VERSIONS_MAX_LEN,
1102+
"Only {DC_SUPPORTED_VERSIONS_MAX_LEN} supported versions are supported"
1103+
);
1104+
ensure!(len <= DC_SUPPORTED_VERSIONS_MAX_LEN, break);
1105+
}
1106+
1107+
DcSupportedVersions { len, versions }
1108+
}
1109+
1110+
/// Create a `DcSupportedVersions` for the `supported_version` the server has selected
1111+
pub fn for_server(supported_version: u32) -> Self {
1112+
DcSupportedVersions {
1113+
len: 1,
1114+
versions: [supported_version, 0, 0, 0],
1115+
}
1116+
}
1117+
1118+
/// The version the server has selected
1119+
///
1120+
/// Returns `None` if no version was selected
1121+
pub fn selected_version(&self) -> Result<Option<u32>, DecoderError> {
1122+
match self.len {
1123+
0 => Ok(None),
1124+
1 => Ok(Some(self.versions[0])),
1125+
_ => Err(DecoderError::InvariantViolation(
1126+
"multiple versions selected by the server",
1127+
)),
1128+
}
1129+
}
1130+
}
1131+
1132+
impl TransportParameter for DcSupportedVersions {
1133+
const ID: TransportParameterId = TransportParameterId::from_u32(0xdc0000);
1134+
type CodecValue = Self;
1135+
1136+
fn from_codec_value(value: Self::CodecValue) -> Self {
1137+
value
1138+
}
1139+
1140+
fn try_into_codec_value(&self) -> Option<&Self::CodecValue> {
1141+
if *self == Self::default_value() {
1142+
None
1143+
} else {
1144+
Some(self)
1145+
}
1146+
}
1147+
1148+
fn default_value() -> Self {
1149+
Self::default()
1150+
}
1151+
}
1152+
1153+
impl EncoderValue for DcSupportedVersions {
1154+
fn encode<E: Encoder>(&self, buffer: &mut E) {
1155+
for &version in self.versions.iter().take(self.len as usize) {
1156+
VarInt::from_u32(version).encode(buffer);
1157+
}
1158+
}
1159+
}
1160+
1161+
decoder_value!(
1162+
impl<'a> DcSupportedVersions {
1163+
fn decode(buffer: Buffer) -> Result<Self> {
1164+
let mut versions = [0; DC_SUPPORTED_VERSIONS_MAX_LEN as usize];
1165+
let mut len = 0;
1166+
let mut buffer = buffer;
1167+
while !buffer.is_empty() {
1168+
let (version, remaining) = buffer.decode::<VarInt>()?;
1169+
buffer = remaining;
1170+
1171+
decoder_invariant!(
1172+
version.as_u64() <= u32::MAX as u64,
1173+
"the largest supported version is u32::MAX"
1174+
);
1175+
versions[len] = version.as_u64() as u32;
1176+
len += 1;
1177+
ensure!(len < DC_SUPPORTED_VERSIONS_MAX_LEN as usize, break);
1178+
}
1179+
1180+
// Skip the rest of the buffer to allow for future versions of
1181+
// `DcSupportedVersions` that may support more than 4 versions
1182+
let remaining_capacity = buffer.len();
1183+
let buffer = buffer.skip(remaining_capacity)?;
1184+
Ok((
1185+
Self {
1186+
len: len as u8,
1187+
versions,
1188+
},
1189+
buffer,
1190+
))
1191+
}
1192+
}
1193+
);
1194+
1195+
impl TransportParameterValidator for DcSupportedVersions {}
1196+
1197+
impl<'a> IntoIterator for &'a DcSupportedVersions {
1198+
type Item = &'a u32;
1199+
type IntoIter = core::slice::Iter<'a, u32>;
1200+
1201+
fn into_iter(self) -> Self::IntoIter {
1202+
self.versions[..self.len as usize].iter()
1203+
}
1204+
}
1205+
1206+
impl<'a> IntoEvent<&'a [u32]> for &'a DcSupportedVersions {
1207+
fn into_event(self) -> &'a [u32] {
1208+
&self.versions[..self.len as usize]
1209+
}
1210+
}
1211+
10661212
//= https://www.rfc-editor.org/rfc/rfc9000#section-18.2
10671213
//# If present, transport parameters that set initial per-stream flow
10681214
//# control limits (initial_max_stream_data_bidi_local,
@@ -1244,6 +1390,7 @@ impl<'a> IntoEvent<event::builder::TransportParameters<'a>> for &'a ServerTransp
12441390
initial_max_streams_bidi: self.initial_max_streams_bidi.into_event(),
12451391
initial_max_streams_uni: self.initial_max_streams_uni.into_event(),
12461392
max_datagram_frame_size: self.max_datagram_frame_size.into_event(),
1393+
dc_supported_versions: self.dc_supported_versions.into_event(),
12471394
}
12481395
}
12491396
}
@@ -1275,6 +1422,7 @@ impl<'a> IntoEvent<event::builder::TransportParameters<'a>> for &'a ClientTransp
12751422
initial_max_streams_bidi: self.initial_max_streams_bidi.into_event(),
12761423
initial_max_streams_uni: self.initial_max_streams_uni.into_event(),
12771424
max_datagram_frame_size: self.max_datagram_frame_size.into_event(),
1425+
dc_supported_versions: self.dc_supported_versions.into_event(),
12781426
}
12791427
}
12801428
}
@@ -1419,6 +1567,7 @@ impl_transport_parameters!(
14191567
preferred_address: PreferredAddress,
14201568
initial_source_connection_id: Option<InitialSourceConnectionId>,
14211569
retry_source_connection_id: RetrySourceConnectionId,
1570+
dc_supported_versions: DcSupportedVersions,
14221571
}
14231572
);
14241573

quic/s2n-quic-core/src/transport/parameters/snapshots/s2n_quic_core__transport__parameters__tests__ClientTransportParameters__default.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,13 @@ TransportParameters {
6767
preferred_address: DisabledParameter,
6868
initial_source_connection_id: None,
6969
retry_source_connection_id: DisabledParameter,
70+
dc_supported_versions: DcSupportedVersions {
71+
len: 0,
72+
versions: [
73+
0,
74+
0,
75+
0,
76+
0,
77+
],
78+
},
7079
}

quic/s2n-quic-core/src/transport/parameters/snapshots/s2n_quic_core__transport__parameters__tests__ServerTransportParameters__default.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,13 @@ TransportParameters {
6767
preferred_address: None,
6868
initial_source_connection_id: None,
6969
retry_source_connection_id: None,
70+
dc_supported_versions: DcSupportedVersions {
71+
len: 0,
72+
versions: [
73+
0,
74+
0,
75+
0,
76+
0,
77+
],
78+
},
7079
}

quic/s2n-quic-core/src/transport/parameters/snapshots/s2n_quic_core__transport__parameters__tests__client_snapshot_test.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,13 @@ expression: encoded_output
4545
2,
4646
3,
4747
4,
48+
128,
49+
220,
50+
0,
51+
0,
52+
4,
53+
1,
54+
2,
55+
3,
56+
4,
4857
]

quic/s2n-quic-core/src/transport/parameters/snapshots/s2n_quic_core__transport__parameters__tests__load_client_limits.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,13 @@ TransportParameters {
6767
preferred_address: DisabledParameter,
6868
initial_source_connection_id: None,
6969
retry_source_connection_id: DisabledParameter,
70+
dc_supported_versions: DcSupportedVersions {
71+
len: 0,
72+
versions: [
73+
0,
74+
0,
75+
0,
76+
0,
77+
],
78+
},
7079
}

quic/s2n-quic-core/src/transport/parameters/snapshots/s2n_quic_core__transport__parameters__tests__load_server_limits.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,13 @@ TransportParameters {
6767
preferred_address: None,
6868
initial_source_connection_id: None,
6969
retry_source_connection_id: None,
70+
dc_supported_versions: DcSupportedVersions {
71+
len: 0,
72+
versions: [
73+
0,
74+
0,
75+
0,
76+
0,
77+
],
78+
},
7079
}

quic/s2n-quic-core/src/transport/parameters/snapshots/s2n_quic_core__transport__parameters__tests__server_snapshot_test.snap

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,10 @@ expression: encoded_output
126126
2,
127127
3,
128128
4,
129+
128,
130+
220,
131+
0,
132+
0,
133+
1,
134+
3,
129135
]

0 commit comments

Comments
 (0)