Skip to main content

tls_codec/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![warn(
5    clippy::mod_module_files,
6    clippy::unwrap_used,
7    rust_2018_idioms,
8    unused_lifetimes
9)]
10
11//! ## Usage
12//!
13//! ```
14//! # #[cfg(feature = "std")]
15//! # {
16//! use tls_codec::{TlsVecU8, Serialize, Deserialize};
17//! let mut b = &[1u8, 4, 77, 88, 1, 99] as &[u8];
18//!
19//! let a = u8::tls_deserialize(&mut b).expect("Unable to tls_deserialize");
20//! assert_eq!(1, a);
21//! println!("b: {:?}", b);
22//! let v = TlsVecU8::<u8>::tls_deserialize(&mut b).expect("Unable to tls_deserialize");
23//! assert_eq!(&[77, 88, 1, 99], v.as_slice());
24//! # }
25//! ```
26
27#[macro_use]
28extern crate alloc;
29
30#[cfg(feature = "std")]
31extern crate std;
32
33use alloc::{string::String, vec::Vec};
34use core::fmt::{self, Display};
35#[cfg(feature = "std")]
36use std::io::{Read, Write};
37
38mod arrays;
39mod primitives;
40mod quic_vec;
41mod string;
42mod tls_vec;
43mod varint;
44
45pub use tls_vec::{
46    SecretTlsVecU8, SecretTlsVecU16, SecretTlsVecU24, SecretTlsVecU32, TlsByteSliceU8,
47    TlsByteSliceU16, TlsByteSliceU24, TlsByteSliceU32, TlsByteVecU8, TlsByteVecU16, TlsByteVecU24,
48    TlsByteVecU32, TlsSliceU8, TlsSliceU16, TlsSliceU24, TlsSliceU32, TlsVecU8, TlsVecU16,
49    TlsVecU24, TlsVecU32,
50};
51
52#[cfg(feature = "std")]
53#[cfg_attr(feature = "future_deprecations", allow(deprecated))]
54pub use quic_vec::SecretVLBytes;
55#[cfg_attr(feature = "future_deprecations", allow(deprecated))]
56pub use quic_vec::VLBytes;
57#[cfg(feature = "std")]
58pub use quic_vec::{SecretVLByteVec, rw as vlen};
59pub use quic_vec::{VLByteSlice, VLByteVec};
60
61#[cfg(feature = "derive")]
62pub use tls_codec_derive::{
63    TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSerializeBytes, TlsSize,
64};
65
66#[cfg(feature = "conditional_deserialization")]
67pub use tls_codec_derive::conditionally_deserializable;
68
69pub use varint::TlsVarInt;
70
71/// Errors that are thrown by this crate.
72#[derive(Debug, Eq, PartialEq, Clone)]
73pub enum Error {
74    /// An error occurred during encoding.
75    EncodingError(String),
76
77    /// The length of a vector is invalid.
78    InvalidVectorLength,
79
80    /// Error writing everything out.
81    ///
82    /// **Deprecated:** This error variant is not returned anymore and only kept to avoid breaking
83    /// existing code.
84    InvalidWriteLength(String),
85
86    /// Invalid input when trying to decode a primitive integer.
87    InvalidInput,
88
89    /// An error occurred during decoding.
90    DecodingError(String),
91
92    /// Reached the end of a byte stream.
93    EndOfStream,
94
95    /// Found unexpected data after deserializing.
96    TrailingData,
97
98    /// An unknown value in an enum.
99    /// The application might not want to treat this as an error because it is
100    /// only an unknown value, not an invalid value.
101    UnknownValue(u64),
102
103    /// An internal library error that indicates a bug.
104    LibraryError,
105}
106
107#[cfg(feature = "std")]
108impl std::error::Error for Error {}
109
110impl Display for Error {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        f.write_fmt(format_args!("{self:?}"))
113    }
114}
115
116#[cfg(feature = "std")]
117impl From<std::io::Error> for Error {
118    fn from(e: std::io::Error) -> Self {
119        match e.kind() {
120            std::io::ErrorKind::UnexpectedEof => Self::EndOfStream,
121            _ => Self::DecodingError(format!("io error: {e:?}")),
122        }
123    }
124}
125
126/// Upper bound on an up-front allocation sized from an untrusted length hint, so
127/// that a bogus (large) length field can't trigger a huge allocation before any
128/// data is read. Callers cap the initial capacity at this value and let the
129/// vector grow as data actually arrives.
130#[cfg(any(feature = "std", feature = "serde"))]
131pub(crate) const MAX_PREALLOC: usize = 4096;
132
133/// Read exactly `len` bytes from `reader` into a freshly allocated vector.
134///
135/// Unlike `vec![0u8; len]` followed by `read_exact`, this does **not** eagerly
136/// allocate `len` bytes up front: the initial allocation is capped so that a
137/// bogus (large) length field in untrusted input can't trigger a huge
138/// allocation before any bytes are read. The vector grows as data actually
139/// arrives.
140///
141/// Returns [`Error::EndOfStream`] if the reader is exhausted before `len` bytes
142/// are read.
143#[cfg(feature = "std")]
144fn read_bytes_bounded<R: std::io::Read>(reader: &mut R, len: usize) -> Result<Vec<u8>, Error> {
145    if len > isize::MAX as usize {
146        return Err(Error::InvalidVectorLength);
147    }
148
149    // Cap the initial allocation. `read_to_end` grows the vector as data
150    // actually arrives, and `Take` bounds the reader to `len` so growth can
151    // never exceed the request.
152    let mut result = Vec::with_capacity(core::cmp::min(len, MAX_PREALLOC));
153
154    // `read_to_end` reads directly into the vector's spare capacity and retries
155    // `ErrorKind::Interrupted` internally, unlike a bare `read` loop.
156    reader.take(len as u64).read_to_end(&mut result)?;
157
158    // `Take` caps output at `len`, so a short read means the stream was
159    // exhausted early.
160    if result.len() != len {
161        return Err(Error::EndOfStream);
162    }
163    Ok(result)
164}
165
166/// Adds two serialized-length components, guarding against `usize` overflow
167/// only on platforms where it can actually occur.
168///
169/// On 64-bit targets every length is bounded by the amount of addressable
170/// memory (`isize::MAX`), so a sum of serialized lengths can never overflow
171/// `usize`. There this compiles down to a plain addition with no branch,
172/// keeping the serialization hot path free of overflow checks.
173///
174/// On narrower targets (32-bit, 16-bit is not officially supported)
175/// the serialized form of a large in-memory structure can carry enough
176/// length-prefix / discriminant overhead to exceed `usize::MAX`.
177/// There we saturate at `usize::MAX` rather than silently wrapping to a small
178/// value, so the oversized length is subsequently rejected by the
179/// length-encoding bounds checks instead of producing a truncated, mismatched
180/// length prefix on the wire.
181///
182/// `tls_codec_derive` emits the equivalent logic inline, so this helper does not
183/// need to be part of the public API.
184#[inline(always)]
185#[cfg(target_pointer_width = "64")]
186pub(crate) const fn len_add(a: usize, b: usize) -> usize {
187    a + b
188}
189
190#[inline(always)]
191#[cfg(not(target_pointer_width = "64"))]
192pub(crate) const fn len_add(a: usize, b: usize) -> usize {
193    a.saturating_add(b)
194}
195
196/// Like [`len_add`], but for contexts that can surface an error.
197///
198/// On 64-bit targets this is a plain, branch-free addition (see [`len_add`] for
199/// why it can't overflow). On narrower targets an overflow becomes
200/// [`Error::InvalidVectorLength`] so a wrapped, too-small length is never
201/// written to the wire.
202#[inline(always)]
203#[cfg(target_pointer_width = "64")]
204pub(crate) fn checked_len_add(a: usize, b: usize) -> Result<usize, Error> {
205    Ok(a + b)
206}
207
208#[inline(always)]
209#[cfg(not(target_pointer_width = "64"))]
210pub(crate) fn checked_len_add(a: usize, b: usize) -> Result<usize, Error> {
211    a.checked_add(b).ok_or(Error::InvalidVectorLength)
212}
213
214/// Validates `len` as a [`Vec`] capacity.
215///
216/// [`Vec::with_capacity`] *panics* when the requested capacity exceeds
217/// `isize::MAX`, so anything larger becomes [`Error::InvalidVectorLength`]
218/// instead of a panic. Call this at allocation sites — not in the per-element
219/// length folds, which use the cheaper [`checked_len_add`].
220#[inline(always)]
221pub(crate) fn checked_capacity(len: usize) -> Result<usize, Error> {
222    if len > isize::MAX as usize {
223        return Err(Error::InvalidVectorLength);
224    }
225    Ok(len)
226}
227
228/// Adds two length components and validates the result as a [`Vec`] capacity
229/// (see [`checked_capacity`]).
230///
231/// The `saturating_add` collapses a `usize` wrap (possible on narrow targets)
232/// into a value the `isize::MAX` check then rejects, so both failure modes are
233/// covered by a single check.
234#[inline(always)]
235pub(crate) fn checked_alloc_len(a: usize, b: usize) -> Result<usize, Error> {
236    checked_capacity(a.saturating_add(b))
237}
238
239/// The `Size` trait needs to be implemented by any struct that should be
240/// efficiently serialized.
241/// This allows to collect the length of a serialized structure before allocating
242/// memory.
243pub trait Size {
244    fn tls_serialized_len(&self) -> usize;
245}
246
247/// The `Serialize` trait provides functions to serialize a struct or enum.
248///
249/// The trait provides two functions:
250/// * `tls_serialize` that takes a buffer to write the serialization to
251/// * `tls_serialize_detached` that returns a byte vector
252pub trait Serialize: Size {
253    /// Serialize `self` and write it to the `writer`.
254    /// The function returns the number of bytes written to `writer`.
255    #[cfg(feature = "std")]
256    fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, Error>;
257
258    /// Serialize `self` and return it as a byte vector.
259    #[cfg(feature = "std")]
260    fn tls_serialize_detached(&self) -> Result<Vec<u8>, Error> {
261        let mut buffer = Vec::with_capacity(checked_capacity(self.tls_serialized_len())?);
262        let written = self.tls_serialize(&mut buffer)?;
263        debug_assert_eq!(
264            written,
265            buffer.len(),
266            "Expected that {} bytes were written but the output holds {} bytes",
267            written,
268            buffer.len()
269        );
270        if written != buffer.len() {
271            Err(Error::EncodingError(format!(
272                "Expected that {} bytes were written but the output holds {} bytes",
273                written,
274                buffer.len()
275            )))
276        } else {
277            Ok(buffer)
278        }
279    }
280}
281
282/// The `SerializeBytes` trait provides a function to serialize a struct or enum.
283///
284/// The trait provides one function:
285/// * `tls_serialize_bytes` that returns a byte vector
286pub trait SerializeBytes: Size {
287    /// Serialize `self` and return it as a byte vector.
288    fn tls_serialize_bytes(&self) -> Result<Vec<u8>, Error>;
289}
290
291/// The `Deserialize` trait defines functions to deserialize a byte slice to a
292/// struct or enum.
293pub trait Deserialize: Size {
294    /// This function deserializes the `bytes` from the provided a [`std::io::Read`]
295    /// and returns the populated struct.
296    ///
297    /// In order to get the amount of bytes read, use [`Size::tls_serialized_len`].
298    ///
299    /// Returns an error if one occurs during deserialization.
300    #[cfg(feature = "std")]
301    fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, Error>
302    where
303        Self: Sized;
304
305    /// This function deserializes the provided `bytes` and returns the populated
306    /// struct. All bytes must be consumed.
307    ///
308    /// Returns an error if not all bytes are read from the input, or if an error
309    /// occurs during deserialization.
310    #[cfg(feature = "std")]
311    fn tls_deserialize_exact(bytes: impl AsRef<[u8]>) -> Result<Self, Error>
312    where
313        Self: Sized,
314    {
315        let mut bytes = bytes.as_ref();
316        let out = Self::tls_deserialize(&mut bytes)?;
317
318        if !bytes.is_empty() {
319            return Err(Error::TrailingData);
320        }
321
322        Ok(out)
323    }
324}
325
326/// The `DeserializeBytes` trait defines functions to deserialize a byte slice
327/// to a struct or enum. In contrast to [`Deserialize`], this trait operates
328/// directly on byte slices and can return any remaining bytes.
329pub trait DeserializeBytes: Size {
330    /// This function deserializes the `bytes` from the provided a `&[u8]`
331    /// and returns the populated struct, as well as the remaining slice.
332    ///
333    /// In order to get the amount of bytes read, use [`Size::tls_serialized_len`].
334    ///
335    /// Returns an error if one occurs during deserialization.
336    fn tls_deserialize_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error>
337    where
338        Self: Sized;
339
340    /// This function deserializes the provided `bytes` and returns the populated
341    /// struct. All bytes must be consumed.
342    ///
343    /// Returns an error if not all bytes are read from the input, or if an error
344    /// occurs during deserialization.
345    fn tls_deserialize_exact_bytes(bytes: &[u8]) -> Result<Self, Error>
346    where
347        Self: Sized,
348    {
349        let (out, remainder) = Self::tls_deserialize_bytes(bytes)?;
350
351        if !remainder.is_empty() {
352            return Err(Error::TrailingData);
353        }
354
355        Ok(out)
356    }
357}
358
359/// A 3 byte wide unsigned integer type as defined in [RFC 5246].
360///
361/// [RFC 5246]: https://datatracker.ietf.org/doc/html/rfc5246#section-4.4
362#[derive(Copy, Clone, Debug, Default, PartialEq)]
363pub struct U24([u8; 3]);
364
365impl U24 {
366    pub const MAX: Self = Self([255u8; 3]);
367    pub const MIN: Self = Self([0u8; 3]);
368
369    pub fn from_be_bytes(bytes: [u8; 3]) -> Self {
370        U24(bytes)
371    }
372
373    pub fn to_be_bytes(self) -> [u8; 3] {
374        self.0
375    }
376}
377
378impl From<U24> for usize {
379    fn from(value: U24) -> usize {
380        const LEN: usize = core::mem::size_of::<usize>();
381        let mut usize_bytes = [0u8; LEN];
382        usize_bytes[LEN - 3..].copy_from_slice(&value.0);
383        usize::from_be_bytes(usize_bytes)
384    }
385}
386
387impl TryFrom<usize> for U24 {
388    type Error = Error;
389
390    fn try_from(value: usize) -> Result<Self, Self::Error> {
391        const LEN: usize = core::mem::size_of::<usize>();
392        // In practice, our usages of this conversion should never be invalid, as the values
393        // have to come from `TryFrom<U24> for usize`.
394        if value > (1 << 24) - 1 {
395            Err(Error::LibraryError)
396        } else {
397            Ok(U24(value.to_be_bytes()[LEN - 3..].try_into()?))
398        }
399    }
400}