Skip to content

Commit 0a63820

Browse files
authored
feat(s2n-quic-dc): Add Unix socket packet encoding/decoding (#2840)
1 parent fe92456 commit 0a63820

File tree

6 files changed

+309
-0
lines changed

6 files changed

+309
-0
lines changed

dc/s2n-quic-dc/src/packet.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub mod control;
1616
pub mod datagram;
1717
pub mod secret_control;
1818
pub mod stream;
19+
pub mod uds;
1920

2021
pub use tag::Tag;
2122
pub use wire_version::WireVersion;

dc/s2n-quic-dc/src/packet/uds.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
pub mod decoder;
5+
pub mod encoder;
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::{
5+
packet::uds::encoder::{APP_PARAMS_VERSION, PACKET_VERSION},
6+
path::secret::schedule::Ciphersuite,
7+
};
8+
use s2n_codec::{DecoderBufferMut, DecoderBufferMutResult, DecoderError};
9+
use s2n_quic_core::{dc::ApplicationParams, varint::VarInt};
10+
11+
#[derive(Clone, Debug)]
12+
pub struct Packet {
13+
version_tag: u8,
14+
ciphersuite: Ciphersuite,
15+
export_secret: Vec<u8>,
16+
application_params_version: u8,
17+
application_params: ApplicationParams,
18+
payload: Vec<u8>,
19+
}
20+
21+
impl Packet {
22+
#[inline]
23+
pub fn version_tag(&self) -> u8 {
24+
self.version_tag
25+
}
26+
27+
#[inline]
28+
pub fn ciphersuite(&self) -> Ciphersuite {
29+
self.ciphersuite
30+
}
31+
32+
#[inline]
33+
pub fn export_secret(&self) -> &[u8] {
34+
&self.export_secret
35+
}
36+
37+
#[inline]
38+
pub fn application_params_version(&self) -> u8 {
39+
self.application_params_version
40+
}
41+
42+
#[inline]
43+
pub fn application_params(&self) -> &ApplicationParams {
44+
&self.application_params
45+
}
46+
47+
#[inline]
48+
pub fn payload(&self) -> &[u8] {
49+
&self.payload
50+
}
51+
52+
#[inline(always)]
53+
pub fn decode(buffer: DecoderBufferMut) -> DecoderBufferMutResult<Packet> {
54+
let (version_tag, buffer) = buffer.decode::<u8>()?;
55+
56+
if version_tag != PACKET_VERSION {
57+
return Err(DecoderError::InvariantViolation("Unsupported version tag"));
58+
}
59+
60+
let (ciphersuite_byte, buffer) = buffer.decode::<u8>()?;
61+
let ciphersuite = ciphersuite_byte
62+
.try_into()
63+
.map_err(DecoderError::InvariantViolation)?;
64+
65+
let (export_secret_slice, buffer) = buffer.decode_slice_with_len_prefix::<VarInt>()?;
66+
let export_secret = export_secret_slice.into_less_safe_slice().to_vec();
67+
68+
let (application_params_version, buffer) = buffer.decode::<u8>()?;
69+
70+
if application_params_version != APP_PARAMS_VERSION {
71+
return Err(DecoderError::InvariantViolation(
72+
"Unsupported application parameters version",
73+
));
74+
}
75+
76+
let (application_params, buffer) = buffer.decode::<ApplicationParams>()?;
77+
78+
let (payload_slice, buffer) = buffer.decode_slice_with_len_prefix::<VarInt>()?;
79+
let payload = payload_slice.into_less_safe_slice().to_vec();
80+
81+
let packet = Packet {
82+
version_tag,
83+
ciphersuite,
84+
export_secret,
85+
application_params_version,
86+
application_params,
87+
payload,
88+
};
89+
90+
Ok((packet, buffer))
91+
}
92+
}
93+
94+
#[cfg(test)]
95+
mod tests {
96+
use crate::{
97+
packet::uds::{
98+
decoder,
99+
encoder::{self, PACKET_VERSION},
100+
},
101+
path::secret::schedule::Ciphersuite,
102+
};
103+
use s2n_codec::{DecoderBufferMut, DecoderError, EncoderLenEstimator};
104+
use s2n_quic_core::dc;
105+
106+
#[test]
107+
fn test_encode_decode() {
108+
let ciphersuite = Ciphersuite::AES_GCM_128_SHA256;
109+
let export_secret = b"secret_data";
110+
let application_params = dc::testing::TEST_APPLICATION_PARAMS;
111+
let payload = b"payload_with_data";
112+
113+
// Encode
114+
let mut estimator = EncoderLenEstimator::new(usize::MAX);
115+
let expected_size = encoder::encode(
116+
&mut estimator,
117+
&ciphersuite,
118+
export_secret,
119+
&application_params,
120+
payload,
121+
);
122+
let mut buffer = vec![0u8; expected_size];
123+
let mut enc = s2n_codec::EncoderBuffer::new(&mut buffer);
124+
let encoded_size = encoder::encode(
125+
&mut enc,
126+
&ciphersuite,
127+
export_secret,
128+
&application_params,
129+
payload,
130+
);
131+
assert_eq!(encoded_size, expected_size);
132+
133+
// Decode
134+
let decoder = DecoderBufferMut::new(&mut buffer);
135+
let (packet, remaining) = decoder::Packet::decode(decoder).unwrap();
136+
assert!(remaining.is_empty());
137+
138+
let decoded_params = packet.application_params();
139+
140+
// Verify
141+
assert_eq!(packet.version_tag(), PACKET_VERSION);
142+
assert_eq!(packet.ciphersuite(), ciphersuite);
143+
assert_eq!(packet.export_secret(), export_secret);
144+
assert_eq!(packet.payload(), payload);
145+
146+
use core::sync::atomic::Ordering;
147+
assert_eq!(
148+
decoded_params.max_datagram_size.load(Ordering::Relaxed),
149+
application_params.max_datagram_size.load(Ordering::Relaxed)
150+
);
151+
assert_eq!(
152+
decoded_params.remote_max_data,
153+
application_params.remote_max_data
154+
);
155+
assert_eq!(
156+
decoded_params.local_send_max_data,
157+
application_params.local_send_max_data
158+
);
159+
assert_eq!(
160+
decoded_params.local_recv_max_data,
161+
application_params.local_recv_max_data
162+
);
163+
assert_eq!(
164+
decoded_params.max_idle_timeout,
165+
application_params.max_idle_timeout
166+
);
167+
}
168+
169+
#[test]
170+
fn test_decode_invalid_version_tag() {
171+
let mut buffer = vec![1u8, 0u8]; // Invalid version tag = 1
172+
let decoder = DecoderBufferMut::new(&mut buffer);
173+
let result = decoder::Packet::decode(decoder);
174+
assert!(result.is_err());
175+
match result.unwrap_err() {
176+
DecoderError::InvariantViolation(msg) => {
177+
assert_eq!(msg, "Unsupported version tag");
178+
}
179+
_ => panic!("Expected InvariantViolation error"),
180+
}
181+
}
182+
183+
#[test]
184+
fn test_decode_invalid_app_params_version() {
185+
let mut buffer = vec![
186+
0u8, // version tag = 0
187+
0u8, // ciphersuite = 0 (valid)
188+
4u8, // export secret length = 4
189+
b't', b'e', b's', b't', // export secret
190+
1u8, // invalid application params version = 1
191+
];
192+
let decoder = DecoderBufferMut::new(&mut buffer);
193+
let result = decoder::Packet::decode(decoder);
194+
assert!(result.is_err());
195+
match result.unwrap_err() {
196+
DecoderError::InvariantViolation(msg) => {
197+
assert_eq!(msg, "Unsupported application parameters version");
198+
}
199+
_ => panic!("Expected InvariantViolation error"),
200+
}
201+
}
202+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::path::secret::schedule::Ciphersuite;
5+
use s2n_codec::Encoder;
6+
use s2n_quic_core::{dc::ApplicationParams, varint::VarInt};
7+
8+
pub const PACKET_VERSION: u8 = 0;
9+
pub const APP_PARAMS_VERSION: u8 = 0;
10+
11+
#[inline(always)]
12+
pub fn encode<E: Encoder>(
13+
encoder: &mut E,
14+
ciphersuite: &Ciphersuite,
15+
export_secret: &[u8],
16+
application_params: &ApplicationParams,
17+
payload: &[u8],
18+
) -> usize {
19+
let start_len = encoder.len();
20+
21+
encoder.encode(&PACKET_VERSION);
22+
23+
let ciphersuite_byte: u8 = (*ciphersuite).into();
24+
encoder.encode(&ciphersuite_byte);
25+
26+
encoder.encode_with_len_prefix::<VarInt, _>(&export_secret);
27+
28+
encoder.encode(&APP_PARAMS_VERSION);
29+
30+
encoder.encode(application_params);
31+
32+
encoder.encode_with_len_prefix::<VarInt, _>(&payload);
33+
34+
encoder.len() - start_len
35+
}

dc/s2n-quic-dc/src/path/secret/schedule.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub const MAX_KEY_LEN: usize = 32;
1919
const MAX_HMAC_KEY_LEN: usize = 1024 / 8;
2020

2121
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22+
#[repr(u8)]
2223
#[cfg_attr(test, derive(bolero_generator::TypeGenerator))]
2324
#[allow(non_camel_case_types)]
2425
pub enum Ciphersuite {
@@ -62,6 +63,24 @@ impl hkdf::KeyType for Ciphersuite {
6263
}
6364
}
6465

66+
impl From<Ciphersuite> for u8 {
67+
fn from(c: Ciphersuite) -> u8 {
68+
c as u8
69+
}
70+
}
71+
72+
impl TryFrom<u8> for Ciphersuite {
73+
type Error = &'static str;
74+
75+
fn try_from(value: u8) -> Result<Self, Self::Error> {
76+
match value {
77+
0 => Ok(Ciphersuite::AES_GCM_128_SHA256),
78+
1 => Ok(Ciphersuite::AES_GCM_256_SHA384),
79+
_ => Err("Invalid Ciphersuite value"),
80+
}
81+
}
82+
}
83+
6584
#[derive(Clone, Copy, Debug)]
6685
pub enum Initiator {
6786
Local,

quic/s2n-quic-core/src/dc.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod traits;
2424
pub mod testing;
2525

2626
pub use disabled::*;
27+
use s2n_codec::{decoder_value, DecoderError, Encoder, EncoderValue};
2728
pub use traits::*;
2829

2930
pub type Version = u32;
@@ -146,6 +147,52 @@ impl ApplicationParams {
146147
}
147148
}
148149

150+
decoder_value!(
151+
impl<'a> ApplicationParams {
152+
fn decode(buffer: Buffer) -> Result<Self> {
153+
let (max_datagram_size, buffer) = buffer.decode::<u16>()?;
154+
let (remote_max_data, buffer) = buffer.decode::<VarInt>()?;
155+
let (local_send_max_data, buffer) = buffer.decode::<VarInt>()?;
156+
let (local_recv_max_data, buffer) = buffer.decode::<VarInt>()?;
157+
158+
let (timeout_value, buffer) = buffer.decode::<VarInt>()?;
159+
let timeout_value: u32 = timeout_value.try_into().map_err(|_| {
160+
DecoderError::InvariantViolation("Timeout value exceeds u32 maximum")
161+
})?;
162+
let max_idle_timeout = NonZeroU32::new(timeout_value);
163+
164+
Ok((
165+
Self {
166+
max_datagram_size: AtomicU16::new(max_datagram_size),
167+
remote_max_data,
168+
local_send_max_data,
169+
local_recv_max_data,
170+
max_idle_timeout,
171+
},
172+
buffer,
173+
))
174+
}
175+
}
176+
);
177+
178+
impl EncoderValue for ApplicationParams {
179+
fn encode<E: Encoder>(&self, buffer: &mut E) {
180+
buffer.encode(&self.max_datagram_size.load(Ordering::Relaxed));
181+
buffer.encode(&self.remote_max_data);
182+
buffer.encode(&self.local_send_max_data);
183+
buffer.encode(&self.local_recv_max_data);
184+
185+
match self.max_idle_timeout {
186+
Some(timeout) => {
187+
buffer.encode(&VarInt::from(u32::from(timeout)));
188+
}
189+
None => {
190+
buffer.encode(&VarInt::from(0u32));
191+
}
192+
}
193+
}
194+
}
195+
149196
#[cfg(test)]
150197
mod tests {
151198
use crate::{

0 commit comments

Comments
 (0)