Skip to content

Commit

Permalink
codec doc cmts
Browse files Browse the repository at this point in the history
  • Loading branch information
rrybarczyk committed Oct 14, 2024
1 parent 4c22e82 commit 7444d0d
Show file tree
Hide file tree
Showing 5 changed files with 594 additions and 102 deletions.
235 changes: 210 additions & 25 deletions protocols/v2/codec-sv2/src/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
// # Decoder
//
// Provides utilities for decoding messages held by Sv2 frames, with or without Noise protocol
// support.
//
// It includes primitives to both decode encoded standard Sv2 frames and to decrypt and decode
// Noise-encrypted encoded Sv2 frames, ensuring secure communication when required.
//
// ## Usage
// All messages passed between Sv2 roles are encoded as Sv2 frames. These frames are decoded using
// primitives in this module. There are two types of decoders for reading these frames: one for
// regular Sv2 frames [`StandardDecoder`], and another for Noise-encrypted frames
// [`StandardNoiseDecoder`]. Both decoders manage the deserialization of incoming data and, when
// applicable, the decryption of the data upon receiving the transmitted message.
//
// ### Buffer Management
//
// The decoders rely on buffers to hold intermediate data during the decoding process.
//
// - When the `with_buffer_pool` feature is enabled, the internal `Buffer` type is backed by a
// pool-allocated buffer [`binary_sv2::BufferPool`], providing more efficient memory usage,
// particularly in high-throughput scenarios.
// - If this feature is not enabled, a system memory buffer [`binary_sv2::BufferFromSystemMemory`]
// is used for simpler applications where memory efficiency is less critical.

#[cfg(feature = "noise_sv2")]
use binary_sv2::Deserialize;
#[cfg(feature = "noise_sv2")]
Expand All @@ -18,38 +43,101 @@ use framing_sv2::{
#[cfg(feature = "noise_sv2")]
use noise_sv2::NoiseCodec;

#[cfg(feature = "noise_sv2")]
use crate::error::Error;
use crate::error::Result;

use crate::Error::MissingBytes;
#[cfg(feature = "noise_sv2")]
use crate::State;

#[cfg(not(feature = "with_buffer_pool"))]
use buffer_sv2::{Buffer as IsBuffer, BufferFromSystemMemory as Buffer};

#[cfg(feature = "with_buffer_pool")]
use buffer_sv2::{Buffer as IsBuffer, BufferFromSystemMemory, BufferPool};

// The buffer type for holding intermediate data during decoding.
//
// When the `with_buffer_pool` feature is enabled, `Buffer` is a pool-allocated buffer type
// [`BufferPool`], which allows for more efficient memory management. Otherwise, it defaults to
// [`BufferFromSystemMemory`].
//
// `Buffer` is used for storing both serialized Sv2 frames and encrypted Noise data.
#[cfg(feature = "with_buffer_pool")]
type Buffer = BufferPool<BufferFromSystemMemory>;

#[cfg(feature = "noise_sv2")]
use crate::error::Error;
use crate::error::Result;
/// An encoded or decoded Sv2 frame containing either a regular or Noise-protected message.
///
/// A wrapper around the [`Frame`] enum that represents either a regular or Noise-protected Sv2
/// frame containing the generic message type (`T`).
pub type StandardEitherFrame<T> = Frame<T, <Buffer as IsBuffer>::Slice>;

use crate::Error::MissingBytes;
#[cfg(feature = "noise_sv2")]
use crate::State;
/// An encoded or decoded Sv2 frame.
///
/// A wrapper around the [`Sv2Frame`] that represents a regular Sv2 frame containing the generic
/// message type (`T`).
pub type StandardSv2Frame<T> = Sv2Frame<T, <Buffer as IsBuffer>::Slice>;

/// Standard Sv2 decoder with Noise protocol support.
///
/// Used for decoding and decrypting generic message types (`T`) encoded in Sv2 frames and
/// encrypted via the Noise protocol.
#[cfg(feature = "noise_sv2")]
pub type StandardNoiseDecoder<T> = WithNoise<Buffer, T>;
pub type StandardEitherFrame<T> = Frame<T, <Buffer as IsBuffer>::Slice>;
pub type StandardSv2Frame<T> = Sv2Frame<T, <Buffer as IsBuffer>::Slice>;

/// Standard Sv2 decoder without Noise protocol support.
///
/// Used for decoding generic message types (`T`) encoded in Sv2 frames.
pub type StandardDecoder<T> = WithoutNoise<Buffer, T>;

/// Decoder for Sv2 frames with Noise protocol support.
///
/// Accumulates the encrypted data into a dedicated buffer until the entire encrypted frame is
/// received. The Noise protocol is then used to decrypt the accumulated data into another
/// dedicated buffer, converting it back into its original serialized form. This decrypted data is
/// then deserialized into the original Sv2 frame and message format.
#[cfg(feature = "noise_sv2")]
#[derive(Debug)]
pub struct WithNoise<B: IsBuffer, T: Serialize + binary_sv2::GetSize> {
// Marker for the type of frame being decoded.
//
// Used to maintain the generic type (`T`) information of the message payload held by the
// frame. `T` refers to a type that implements the necessary traits for serialization
// [`binary_sv2::Serialize`] and size calculation [`binary_sv2::GetSize`].
frame: PhantomData<T>,

// Tracks the number of bytes remaining until the full frame is received.
//
// Ensures that the full encrypted Noise frame has been received by keeping track of the
// remaining bytes. Once the complete frame is received, decoding can proceed.
missing_noise_b: usize,

// Buffer for holding incoming encrypted Noise data to be decrypted.
//
// Stores the incoming encrypted data, allowing the decoder to accumulate the necessary bytes
// for full decryption. Once the entire encrypted frame is received, the decoder processes the
// buffer to extract the underlying frame.
noise_buffer: B,

// Buffer for holding decrypted data to be decoded.
//
// Stores the decrypted data until it is ready to be processed and converted into a Sv2 frame.
sv2_buffer: B,
}

#[cfg(feature = "noise_sv2")]
impl<'a, T: Serialize + GetSize + Deserialize<'a>, B: IsBuffer + AeadBuffer> WithNoise<B, T> {
/// Attempts to decode the next Noise encrypted frame.
///
/// On success, the decoded and decrypted frame is returned. Otherwise, an error indicating the
/// number of missing bytes required to complete the encoded frame, an error on a badly
/// formatted message header, or an error on decryption failure is returned.
///
/// In this case of the `Error::MissingBytes`, the user should resize the decoder buffer using
/// `writable`, read another chunk from the incoming message stream, and then call `next_frame`
/// again. This process should be repeated until `next_frame` returns `Ok`, indicating that the
/// full message has been received, and the decoding and decryption of the frame can proceed.
#[inline]
pub fn next_frame(&mut self, state: &mut State) -> Result<Frame<T, B::Slice>> {
match state {
Expand Down Expand Up @@ -96,6 +184,57 @@ impl<'a, T: Serialize + GetSize + Deserialize<'a>, B: IsBuffer + AeadBuffer> Wit
}
}

/// Provides a writable buffer for receiving incoming Noise-encrypted Sv2 data.
///
/// This buffer is used to store incoming data, and its size is adjusted based on the number
/// of missing bytes. As new data is read, it is written into this buffer until enough data has
/// been received to fully decode a frame. The buffer must have the correct number of bytes
/// available to progress to the decoding process.
#[inline]
pub fn writable(&mut self) -> &mut [u8] {
self.noise_buffer.get_writable(self.missing_noise_b)
}

/// Determines whether the decoder's internal buffers can be safely dropped.
///
/// For more information, refer to the [`buffer_sv2`
/// crate](https://docs.rs/buffer_sv2/latest/buffer_sv2/).
pub fn droppable(&self) -> bool {
self.noise_buffer.is_droppable() && self.sv2_buffer.is_droppable()
}

// Processes and decodes a Sv2 frame during the Noise protocol handshake phase.
//
// Handles the decoding of a handshake frame from the `noise_buffer`. It converts the received
// data into a `HandShakeFrame` and encapsulates it into a `Frame` for further processing by
// the codec.
//
// This is used exclusively during the initial handshake phase of the Noise protocol, before
// transitioning to regular frame encryption and decryption.
fn while_handshaking(&mut self) -> Frame<T, B::Slice> {
let src = self.noise_buffer.get_data_owned().as_mut().to_vec();

// Since the frame length is already validated during the handshake process, this
// operation is infallible
let frame = HandShakeFrame::from_bytes_unchecked(src.into());

frame.into()
}

// Decodes a Noise-encrypted Sv2 frame, handling both the message header and payload
// decryption.
//
// Processes Noise-encrypted Sv2 frames by first decrypting the header, followed by the
// payload. If the frame's data is received in chunks, it ensures that decryption occurs
// incrementally as more encrypted data becomes available. The decrypted data is then stored in
// the `sv2_buffer`, from which the resulting Sv2 frame is extracted and returned.
//
// On success, the decoded frame is returned. Otherwise, an error indicating the number of
// missing bytes required to complete the encoded frame, an error on a badly formatted message
// header, or a decryption failure error is returned. If there are still bytes missing to
// complete the frame, the function will return an `Error::MissingBytes` with the number of
// additional bytes required to fully decrypt the frame. Once all bytes are available, the
// decryption process completes and the frame can be successfully decoded.
#[inline]
fn decode_noise_frame(&mut self, noise_codec: &mut NoiseCodec) -> Result<Frame<T, B::Slice>> {
match (
Expand Down Expand Up @@ -144,27 +283,14 @@ impl<'a, T: Serialize + GetSize + Deserialize<'a>, B: IsBuffer + AeadBuffer> Wit
}
}
}

fn while_handshaking(&mut self) -> Frame<T, B::Slice> {
let src = self.noise_buffer.get_data_owned().as_mut().to_vec();

// below is inffalible as noise frame length has been already checked
let frame = HandShakeFrame::from_bytes_unchecked(src.into());

frame.into()
}

#[inline]
pub fn writable(&mut self) -> &mut [u8] {
self.noise_buffer.get_writable(self.missing_noise_b)
}
pub fn droppable(&self) -> bool {
self.noise_buffer.is_droppable() && self.sv2_buffer.is_droppable()
}
}

#[cfg(feature = "noise_sv2")]
impl<T: Serialize + binary_sv2::GetSize> WithNoise<Buffer, T> {
/// Crates a new [`WithNoise`] decoder with default buffer sizes.
///
/// Initializes the decoder with default buffer sizes and sets the number of missing bytes to
/// 0.
pub fn new() -> Self {
Self {
frame: PhantomData,
Expand All @@ -182,14 +308,46 @@ impl<T: Serialize + binary_sv2::GetSize> Default for WithNoise<Buffer, T> {
}
}

/// Decoder for standard Sv2 frames.
///
/// Accumulates the data into a dedicated buffer until the entire Sv2 frame is received. This data
/// is then deserialized into the original Sv2 frame and message format.
#[derive(Debug)]
pub struct WithoutNoise<B: IsBuffer, T: Serialize + binary_sv2::GetSize> {
// Marker for the type of frame being decoded.
//
// Used to maintain the generic type (`T`) information of the message payload held by the
// frame. `T` refers to a type that implements the necessary traits for serialization
// [`binary_sv2::Serialize`] and size calculation [`binary_sv2::GetSize`].
frame: PhantomData<T>,

// Tracks the number of bytes remaining until the full frame is received.
//
// Ensures that the full Sv2 frame has been received by keeping track of the remaining bytes.
// Once the complete frame is received, decoding can proceed.
missing_b: usize,

// Buffer for holding incoming data to be decoded into a Sv2 frame.
//
// This buffer stores incoming data as it is received, allowing the decoder to accumulate the
// necessary bytes until a full frame is available. Once the full encoded frame has been
// received, the buffer's contents are processed and decoded into an Sv2 frame.
buffer: B,
}

impl<T: Serialize + binary_sv2::GetSize, B: IsBuffer> WithoutNoise<B, T> {
/// Attempts to decode the next frame, returning either a frame or an error indicating how many
/// bytes are missing.
///
/// Attempts to decode the next Sv2 frame.
///
/// On success, the decoded frame is returned. Otherwise, an error indicating the number of
/// missing bytes required to complete the frame is returned.
///
/// In the case of `Error::MissingBytes`, the user should resize the decoder buffer using
/// `writable`, read another chunk from the incoming message stream, and then call `next_frame`
/// again. This process should be repeated until `next_frame` returns `Ok`, indicating that the
/// full message has been received, and the frame can be fully decoded.
#[inline]
pub fn next_frame(&mut self) -> Result<Sv2Frame<T, B::Slice>> {
let len = self.buffer.len();
Expand All @@ -210,12 +368,22 @@ impl<T: Serialize + binary_sv2::GetSize, B: IsBuffer> WithoutNoise<B, T> {
}
}

/// Provides a writable buffer for receiving incoming Sv2 data.
///
/// This buffer is used to store incoming data, and its size is adjusted based on the number of
/// missing bytes. As new data is read, it is written into this buffer until enough data has
/// been received to fully decode a frame. The buffer must have the correct number of bytes
/// available to progress to the decoding process.
pub fn writable(&mut self) -> &mut [u8] {
self.buffer.get_writable(self.missing_b)
}
}

impl<T: Serialize + binary_sv2::GetSize> WithoutNoise<Buffer, T> {
/// Creates a new [`WithoutNoise`] with a buffer of default size.
///
/// Initializes the decoder with a default buffer size and sets the number of missing bytes to
/// the size of the header.
pub fn new() -> Self {
Self {
frame: PhantomData,
Expand All @@ -230,3 +398,20 @@ impl<T: Serialize + binary_sv2::GetSize> Default for WithoutNoise<Buffer, T> {
Self::new()
}
}

#[cfg(test)]
mod tests {
use super::*;
use binary_sv2::{binary_codec_sv2, Serialize};

#[derive(Serialize)]
pub struct TestMessage {}

#[test]
fn unencrypted_writable_with_missing_b_initialized_as_header_size() {
let mut decoder = StandardDecoder::<TestMessage>::new();
let actual = decoder.writable();
let expect = [0u8; Header::SIZE];
assert_eq!(actual, expect);
}
}
Loading

0 comments on commit 7444d0d

Please sign in to comment.