s2n_quic_core/packet/encoding.rs
1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5 crypto::{HeaderKey, Key as CryptoKey, ProtectedPayload},
6 packet::{
7 number::{PacketNumber, PacketNumberLen},
8 stateless_reset,
9 },
10};
11use s2n_codec::{encoder::scatter, Encoder, EncoderBuffer, EncoderLenEstimator, EncoderValue};
12
13pub trait PacketPayloadLenCursor: EncoderValue {
14 fn new() -> Self;
15 fn update(&self, buffer: &mut EncoderBuffer, actual_len: usize);
16}
17
18/// used for short packets that don't use a payload len
19impl PacketPayloadLenCursor for () {
20 #[inline]
21 fn new() {}
22
23 #[inline]
24 fn update(&self, _buffer: &mut EncoderBuffer, _actual_len: usize) {
25 // noop
26 }
27}
28
29pub trait PacketPayloadEncoder {
30 /// Returns an estimate of the encoding size of the payload. This
31 /// may be inaccurate from what actually is encoded. Estimates should
32 /// be less than or equal to what is actually written.
33 /// Implementations can return 0 to skip encoding.
34 fn encoding_size_hint<E: Encoder>(&mut self, encoder: &E, minimum_len: usize) -> usize;
35
36 /// Encodes the payload into the buffer. Implementations should ensure
37 /// the encoding len is at least the minimum_len, otherwise the packet
38 /// writing will panic.
39 fn encode(
40 &mut self,
41 buffer: &mut scatter::Buffer,
42 minimum_len: usize,
43 header_len: usize,
44 tag_len: usize,
45 );
46}
47
48impl<T: EncoderValue> PacketPayloadEncoder for T {
49 #[inline]
50 fn encoding_size_hint<E: Encoder>(&mut self, encoder: &E, minimum_len: usize) -> usize {
51 let len = self.encoding_size_for_encoder(encoder);
52 if len < minimum_len {
53 0
54 } else {
55 len
56 }
57 }
58
59 #[inline]
60 fn encode(
61 &mut self,
62 buffer: &mut scatter::Buffer,
63 _minimum_len: usize,
64 _header_len: usize,
65 _tag_len: usize,
66 ) {
67 // the minimum len check is not needed, as it was already performed
68 // in encoding_size_hint
69 self.encode_mut(buffer);
70 }
71}
72
73#[derive(Debug)]
74pub enum PacketEncodingError<'a> {
75 /// The packet number could not be truncated with the
76 /// current largest_acknowledged_packet_number
77 PacketNumberTruncationError(EncoderBuffer<'a>),
78
79 /// The buffer does not have enough space to hold
80 /// the packet encoding.
81 InsufficientSpace(EncoderBuffer<'a>),
82
83 /// The payload did not write anything
84 EmptyPayload(EncoderBuffer<'a>),
85
86 /// The key used to encrypt the buffer has exceeded the confidentiality limit
87 AeadLimitReached(EncoderBuffer<'a>),
88}
89
90impl<'a> PacketEncodingError<'a> {
91 /// Returns the buffer that experienced an encoding error
92 pub fn take_buffer(self) -> EncoderBuffer<'a> {
93 match self {
94 Self::PacketNumberTruncationError(buffer) => buffer,
95 Self::InsufficientSpace(buffer) => buffer,
96 Self::EmptyPayload(buffer) => buffer,
97 Self::AeadLimitReached(buffer) => buffer,
98 }
99 }
100}
101
102pub trait PacketEncoder<K: CryptoKey, H: HeaderKey, Payload: PacketPayloadEncoder>: Sized {
103 type PayloadLenCursor: PacketPayloadLenCursor;
104
105 /// Encodes the current packet's header into the provided encoder
106 fn encode_header<E: Encoder>(&self, packet_number_len: PacketNumberLen, encoder: &mut E);
107
108 /// Returns the payload for the current packet
109 fn payload(&mut self) -> &mut Payload;
110
111 /// Returns the packet number for the current packet
112 fn packet_number(&self) -> PacketNumber;
113
114 // Encodes, encrypts, and header-protects a packet into a buffer
115 fn encode_packet<'a>(
116 mut self,
117 key: &mut K,
118 header_key: &H,
119 largest_acknowledged_packet_number: PacketNumber,
120 min_packet_len: Option<usize>,
121 mut buffer: EncoderBuffer<'a>,
122 ) -> Result<(ProtectedPayload<'a>, EncoderBuffer<'a>), PacketEncodingError<'a>> {
123 let packet_number = self.packet_number();
124
125 // Truncate the packet number from the largest_acknowledged_packet_number.
126 let truncated_packet_number =
127 if let Some(tpn) = packet_number.truncate(largest_acknowledged_packet_number) {
128 tpn
129 } else {
130 return Err(PacketEncodingError::PacketNumberTruncationError(buffer));
131 };
132
133 let packet_number_len = truncated_packet_number.len();
134
135 // We need to build an estimate of how large this packet is before writing it
136 let mut estimator = EncoderLenEstimator::new(buffer.remaining_capacity());
137
138 // Start by measuring the header len
139 self.encode_header(packet_number_len, &mut estimator);
140
141 // Create a payload_len cursor so we can update it with the actual value
142 // after writing the payload
143 let mut payload_len_cursor = Self::PayloadLenCursor::new();
144
145 // `encode_mut` is called here to initialize the len based on the
146 // remaining buffer capacity
147 payload_len_cursor.encode_mut(&mut estimator);
148
149 // Save the header_len for later use
150 let header_len = estimator.len();
151
152 // Record the truncated_packet_number encoding size
153 truncated_packet_number.encode(&mut estimator);
154
155 // Make sure the crypto tag can be written.
156 // We want to write this before the payload in the
157 // estimator so the payload size hint has an accurate
158 // view of remaining capacity.
159 estimator.write_repeated(key.tag_len(), 0);
160
161 //= https://www.rfc-editor.org/rfc/rfc9000#section-10.3
162 //# To achieve that end,
163 //# the endpoint SHOULD ensure that all packets it sends are at least 22
164 //# bytes longer than the minimum connection ID length that it requests
165 //# the peer to include in its packets, adding PADDING frames as
166 //# necessary.
167 // One additional byte is added so that a stateless reset sent in response to this packet
168 // (which is required to be smaller than this packet) is large enough to be
169 // indistinguishable from a valid packet.
170 let minimum_packet_len = min_packet_len
171 .unwrap_or(0)
172 .max(stateless_reset::min_indistinguishable_packet_len(key.tag_len()) + 1);
173
174 // Compute how much the payload will need to write to satisfy the
175 // minimum_packet_len
176 let minimum_payload_len = minimum_packet_len.saturating_sub(estimator.len());
177
178 //= https://www.rfc-editor.org/rfc/rfc9001#section-5.4.2
179 //# in sampling packet ciphertext for header protection,
180 //# the Packet Number field is assumed to be 4 bytes long
181
182 // Header protection sampling assumes a packet number length of 4 bytes,
183 // but the actual packet number may be smaller than that, so we need to ensure
184 // there is still enough payload to sample from given the actual packet number length.
185 let minimum_payload_len = minimum_payload_len.max(
186 PacketNumberLen::MAX_LEN - truncated_packet_number.len().bytesize()
187 + header_key.sealing_sample_len(),
188 );
189
190 // Try to estimate the payload size - it may be inaccurate
191 // but this provides some checks to save writing the packet
192 // header
193 let estimated_payload_len = self
194 .payload()
195 .encoding_size_hint(&estimator, minimum_payload_len);
196
197 // The payload is not interested in writing to this packet
198 if estimated_payload_len == 0 {
199 return Err(PacketEncodingError::EmptyPayload(buffer));
200 }
201
202 // Use the estimated_payload_len to check if we're
203 // going to have enough room for it.
204 estimator.write_repeated(estimated_payload_len, 0);
205
206 // We don't have enough room to write this packet
207 if estimator.overflowed() {
208 return Err(PacketEncodingError::InsufficientSpace(buffer));
209 }
210
211 // After computing the minimum length we actually start writing to the buffer
212
213 // Now we actually encode the header
214 self.encode_header(packet_number_len, &mut buffer);
215
216 // Write the estimated payload len. This will be updated
217 // with the accurate value after everything is written.
218 payload_len_cursor.encode(&mut buffer);
219
220 // Write the packet number
221 truncated_packet_number.encode(&mut buffer);
222
223 let (payload_len, inline_len, extra) = {
224 // Create a temporary buffer for writing the payload
225 let (header_buffer, payload_buffer) = buffer.split_mut();
226
227 // Payloads should not be able to write into the crypto tag space
228 let payload_len = payload_buffer.len() - key.tag_len();
229 let payload_buffer = EncoderBuffer::new(&mut payload_buffer[..payload_len]);
230 let mut payload_buffer = scatter::Buffer::new(payload_buffer);
231
232 // Try to encode the payload into the buffer
233 self.payload().encode(
234 &mut payload_buffer,
235 minimum_payload_len,
236 header_buffer.len(),
237 key.tag_len(),
238 );
239
240 // read how much was written
241 let payload_len = payload_buffer.len();
242
243 let (inline_buffer, extra) = payload_buffer.into_inner();
244
245 // record the number of bytes that were written to the inline buffer
246 let inline_len = inline_buffer.len();
247
248 (payload_len, inline_len, extra)
249 };
250
251 // The payload didn't have anything to write so rewind the cursor
252 if payload_len == 0 {
253 buffer.set_position(0);
254 return Err(PacketEncodingError::EmptyPayload(buffer));
255 }
256
257 // Ideally we would check that the `payload_len >= minimum_payload_len`. However, the packet
258 // interceptor may rewrite the packet into something smaller. Instead of preventing that
259 // here, we will rely on the `crate::transmission::Transmission` logic to ensure the
260 // padding is initially written to ensure the minimum is met before interception is applied.
261
262 // Update the payload_len cursor with the actual payload len
263 let actual_payload_len = buffer.len() + payload_len + key.tag_len() - header_len;
264 payload_len_cursor.update(&mut buffer, actual_payload_len);
265
266 // Advance the buffer cursor by what the payload wrote inline. We'll recreate the scatter
267 // buffer with the option extra bytes at the end.
268 buffer.advance_position(inline_len);
269 let buffer = scatter::Buffer::new_with_extra(buffer, extra);
270
271 // Encrypt the written payload. Note that the tag is appended to the
272 // buffer in the `encrypt` function.
273 let (encrypted_payload, remaining) =
274 crate::crypto::encrypt(key, packet_number, packet_number_len, header_len, buffer)
275 .expect("encryption should always work");
276
277 // Protect the packet
278 let protected_payload = crate::crypto::protect(header_key, encrypted_payload)
279 .expect("header protection should always work");
280
281 // SUCCESS!!!
282
283 Ok((protected_payload, remaining))
284 }
285}