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}