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}