From 4602c5d046f02cc53910fde12b512d62f7e2b2af Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 12 Apr 2024 22:23:18 -0300 Subject: [PATCH] add framing_sv2 docs --- protocols/v2/framing-sv2/src/framing2.rs | 105 ++++++++++++++--------- protocols/v2/framing-sv2/src/header.rs | 8 ++ protocols/v2/framing-sv2/src/lib.rs | 40 +++++---- 3 files changed, 96 insertions(+), 57 deletions(-) diff --git a/protocols/v2/framing-sv2/src/framing2.rs b/protocols/v2/framing-sv2/src/framing2.rs index 95afc617dd..2609b9f7c0 100644 --- a/protocols/v2/framing-sv2/src/framing2.rs +++ b/protocols/v2/framing-sv2/src/framing2.rs @@ -15,6 +15,7 @@ type Slice = Vec; type Slice = buffer_sv2::Slice; impl Sv2Frame { + /// todo pub fn map(self, fun: fn(A) -> C) -> Sv2Frame { let serialized = self.serialized; let header = self.header; @@ -31,31 +32,37 @@ pub trait Frame<'a, T: Serialize + GetSize>: Sized { type Buffer: AsMut<[u8]>; type Deserialized; - /// Serialize the frame into dst if the frame is already serialized it just swap dst with - /// itself + /// Write the serialized `Frame` into `dst`. fn serialize(self, dst: &mut [u8]) -> Result<(), Error>; //fn deserialize(&'a mut self) -> Result; + /// Get the payload fn payload(&'a mut self) -> &'a mut [u8]; - /// If is an Sv2 frame return the Some(header) if it is a noise frame return None + /// `Sv2Frame` returns `Some(self.header)`, `NoiseFrame` return `None`. fn get_header(&self) -> Option; - /// Try to build an Frame frame from raw bytes. - /// It return the frame or the number of the bytes needed to complete the frame - /// The resulting frame is just a header plus a payload with the right number of bytes nothing - /// is said about the correctness of the payload + /// Try to build a `Frame` from raw bytes. + /// Returns `Self` on success, or the number of the bytes needed to complete the frame + /// as an error. Nothing is assumed or checked about the correctness of the payload. fn from_bytes(bytes: Self::Buffer) -> Result; fn from_bytes_unchecked(bytes: Self::Buffer) -> Self; + /// Helps to determine if the frame size encoded in a byte array correctly representing the size of the frame. + /// - Returns `0` if the byte slice is of the expected size according to the header. + /// - Returns a negative value if the byte slice is smaller than a Noise Frame header; this value + /// represents how many bytes are missing. + /// - Returns a positive value if the byte slice is longer than expected; this value + /// indicates the surplus of bytes beyond the expected size. fn size_hint(bytes: &[u8]) -> isize; + /// Returns the size of the `Frame` payload. fn encoded_length(&self) -> usize; - /// Try to build an Frame frame from a serializable payload. - /// It return a Frame if the size of the payload fit in the frame, if not it return None + /// Try to build a `Frame` from a serializable payload. + /// Returns `Some(Self)` if the size of the payload fits in the frame, `None` otherwise. fn from_message( message: T, message_type: u8, @@ -64,11 +71,12 @@ pub trait Frame<'a, T: Serialize + GetSize>: Sized { ) -> Option; } +/// Abstraction for a SV2 Frame. #[derive(Debug, Clone)] pub struct Sv2Frame { header: Header, payload: Option, - /// Serializsed header + payload + /// Serialized header + payload serialized: Option, } @@ -82,13 +90,16 @@ impl Default for Sv2Frame { } } +/// Abstraction for a Noise Frame #[derive(Debug)] pub struct NoiseFrame { payload: Slice, } +/// todo pub type HandShakeFrame = NoiseFrame; +/// todo (why unreachable?) #[cfg(feature = "with_buffer_pool")] impl From>> for Sv2Frame { fn from(_: EitherFrame>) -> Self { @@ -105,8 +116,7 @@ impl<'a, T: Serialize + GetSize, B: AsMut<[u8]> + AsRef<[u8]>> Frame<'a, T> for type Buffer = B; type Deserialized = B; - /// Serialize the frame into dst if the frame is already serialized it just swap dst with - /// itself + /// Write the serialized `Sv2Frame` into `dst`. #[inline] fn serialize(self, dst: &mut [u8]) -> Result<(), Error> { if let Some(mut serialized) = self.serialized { @@ -129,30 +139,28 @@ impl<'a, T: Serialize + GetSize, B: AsMut<[u8]> + AsRef<[u8]>> Frame<'a, T> for } } - // self can be either serialized (it cointain an AsMut<[u8]> with the serialized data or - // deserialized it contain the rust type that represant the Sv2 message. If the type is - // deserialized self.paylos.is_some() is true. To get the serialized payload the inner type - // should be serialized and this function should never be used, cause is intended as a fast - // function that return a reference to an already serialized payload. For that the function - // panic. + /// `self` can be either serialized (`self.serialized` is `Some()`) or + /// deserialized (`self.serialized` is `None`, `self.payload` is `Some()`). + /// This function is only intended as a fast way to get a reference to an + /// already serialized payload. If the frame has not yet been + /// serialized, this function should never be used (it will panic). fn payload(&'a mut self) -> &'a mut [u8] { if let Some(serialized) = self.serialized.as_mut() { &mut serialized.as_mut()[Header::SIZE..] } else { // panic here is the expected behaviour - panic!() + panic!("Sv2Frame is not yet serialized.") } } - /// If is an Sv2 frame return the Some(header) if it is a noise frame return None + /// `Sv2Frame` always returns `Some(self.header)`. fn get_header(&self) -> Option { Some(self.header) } - /// Try to build a Frame frame from raw bytes. - /// It return the frame or the number of the bytes needed to complete the frame - /// The resulting frame is just a header plus a payload with the right number of bytes nothing - /// is said about the correctness of the payload + /// Tries to build a `Sv2Frame` from raw bytes, assuming they represent a serialized `Sv2Frame` frame (`Self.serialized`). + /// Returns a `Sv2Frame` on success, or the number of the bytes needed to complete the frame + /// as an error. `Self.serialized` is `Some`, but nothing is assumed or checked about the correctness of the payload. #[inline] fn from_bytes(mut bytes: Self::Buffer) -> Result { let hint = Self::size_hint(bytes.as_mut()); @@ -175,23 +183,34 @@ impl<'a, T: Serialize + GetSize, B: AsMut<[u8]> + AsRef<[u8]>> Frame<'a, T> for } } + /// After parsing `bytes` into a `Header`, this function helps to determine if the `msg_length` + /// field is correctly representing the size of the frame. + /// - Returns `0` if the byte slice is of the expected size according to the header. + /// - Returns a negative value if the byte slice is shorter than expected; this value + /// represents how many bytes are missing. + /// - Returns a positive value if the byte slice is longer than expected; this value + /// indicates the surplus of bytes beyond the expected size. #[inline] fn size_hint(bytes: &[u8]) -> isize { match Header::from_bytes(bytes) { Err(_) => { - // Return incorrect header length + // Returns how many bytes are missing from the expected frame size (Header::SIZE - bytes.len()) as isize } Ok(header) => { if bytes.len() - Header::SIZE == header.len() { + // expected frame size confirmed 0 } else { + // Returns how many excess bytes are beyond the expected frame size (bytes.len() - Header::SIZE) as isize + header.len() as isize } } } } + /// If `Sv2Frame` is serialized, returns the length of `self.serialized`, + /// otherwise, returns the length of `self.pauload`. #[inline] fn encoded_length(&self) -> usize { if let Some(serialized) = self.serialized.as_ref() { @@ -204,8 +223,8 @@ impl<'a, T: Serialize + GetSize, B: AsMut<[u8]> + AsRef<[u8]>> Frame<'a, T> for } } - /// Try to build an Frame frame from a serializable payload. - /// It returns a Frame if the size of the payload fits in the frame, if not it returns None + /// Tries to build a `Sv2Frame` from a non-serialized payload. + /// Returns a `Sv2Frame` if the size of the payload fits in the frame, `None` otherwise. fn from_message( message: T, message_type: u8, @@ -232,25 +251,25 @@ impl<'a> Frame<'a, Slice> for NoiseFrame { type Buffer = Slice; type Deserialized = &'a mut [u8]; - /// Serialize the frame into dst if the frame is already serialized it just swap dst with - /// itself + /// Put the Noise Frame payload into `dst` #[inline] fn serialize(mut self, dst: &mut [u8]) -> Result<(), Error> { dst.swap_with_slice(self.payload.as_mut()); Ok(()) } + /// Get the Noise Frame payload #[inline] fn payload(&'a mut self) -> &'a mut [u8] { &mut self.payload[NoiseHeader::HEADER_SIZE..] } - /// If is an Sv2 frame return the Some(header) if it is a noise frame return None + /// `NoiseFrame` always returns `None`. fn get_header(&self) -> Option { None } - // For a NoiseFrame from_bytes is the same of from_bytes_unchecked + /// Builds a `NoiseFrame` from raw bytes. Nothing is assumed or checked about the correctness of the payload. fn from_bytes(bytes: Self::Buffer) -> Result { Ok(Self::from_bytes_unchecked(bytes)) } @@ -260,6 +279,13 @@ impl<'a> Frame<'a, Slice> for NoiseFrame { Self { payload: bytes } } + /// After parsing the expected `NoiseFrame` size from `bytes`, this function helps to determine if this value + /// correctly representing the size of the frame. + /// - Returns `0` if the byte slice is of the expected size according to the header. + /// - Returns a negative value if the byte slice is smaller than a Noise Frame header; this value + /// represents how many bytes are missing. + /// - Returns a positive value if the byte slice is longer than expected; this value + /// indicates the surplus of bytes beyond the expected size. #[inline] fn size_hint(bytes: &[u8]) -> isize { if bytes.len() < NoiseHeader::HEADER_SIZE { @@ -276,15 +302,16 @@ impl<'a> Frame<'a, Slice> for NoiseFrame { } } + /// Returns the size of the `NoiseFrame` payload. #[inline] fn encoded_length(&self) -> usize { self.payload.len() } - /// Try to build a `Frame` frame from a serializable payload. - /// It returns a Frame if the size of the payload fits in the frame, if not it returns None - /// Inneficient should be used only to build `HandShakeFrames` - /// TODO check if is used only to build `HandShakeFrames` + /// Tries to build a `NoiseFrame` frame from a byte slice. + /// Returns a `NoiseFrame` if the size of the payload fits in the frame, `None` otherwise. + /// This is quite inefficient, and should be used only to build `HandShakeFrames` + // TODO check if is used only to build `HandShakeFrames` #[allow(clippy::useless_conversion)] fn from_message( message: Slice, @@ -302,6 +329,7 @@ impl<'a> Frame<'a, Slice> for NoiseFrame { } } +/// todo #[allow(clippy::useless_conversion)] pub fn handshake_message_to_frame>(message: T) -> HandShakeFrame { let mut payload = Vec::new(); @@ -311,6 +339,7 @@ pub fn handshake_message_to_frame>(message: T) -> HandShakeFrame } } +/// todo fn update_extension_type(extension_type: u16, channel_msg: bool) -> u16 { if channel_msg { let mask = 0b1000_0000_0000_0000; @@ -321,11 +350,7 @@ fn update_extension_type(extension_type: u16, channel_msg: bool) -> u16 { } } -/// A frame can be either -/// 1: Sv2Frame -/// 2: NoiseFrame -/// 3: HandashakeFrame -/// +/// todo #[derive(Debug)] pub enum EitherFrame { HandShake(HandShakeFrame), diff --git a/protocols/v2/framing-sv2/src/header.rs b/protocols/v2/framing-sv2/src/header.rs index 4262e90ad8..74254c31c4 100644 --- a/protocols/v2/framing-sv2/src/header.rs +++ b/protocols/v2/framing-sv2/src/header.rs @@ -7,6 +7,7 @@ use binary_sv2::{Deserialize, Serialize, U24}; use const_sv2::{AEAD_MAC_LEN, SV2_FRAME_CHUNK_SIZE}; use core::convert::TryInto; +/// Abstraction for a SV2 Frame Header. #[derive(Debug, Serialize, Deserialize, Copy, Clone)] pub struct Header { extension_type: u16, // TODO use specific type? @@ -32,6 +33,7 @@ impl Header { pub const SIZE: usize = const_sv2::SV2_FRAME_HEADER_SIZE; + /// Construct a `Header` from ray bytes #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() < Self::SIZE { @@ -52,6 +54,7 @@ impl Header { }) } + /// Get the message length #[allow(clippy::len_without_is_empty)] #[inline] pub fn len(&self) -> usize { @@ -59,6 +62,7 @@ impl Header { inner as usize } + /// Construct a `Header` from message length, type and extension type. #[inline] pub fn from_len(len: u32, message_type: u8, extension_type: u16) -> Option
{ Some(Self { @@ -68,19 +72,23 @@ impl Header { }) } + /// Get the `Header` message type. pub fn msg_type(&self) -> u8 { self.msg_type } + /// Get the `Header` extension type. pub fn ext_type(&self) -> u16 { self.extension_type } + /// Check if `Header` represents a channel message pub fn channel_msg(&self) -> bool { let mask = 0b0000_0000_0000_0001; self.extension_type & mask == self.extension_type } + /// Calculate the length of the encrypted `Header` pub fn encrypted_len(&self) -> usize { let len = self.len(); let mut chunks = len / (SV2_FRAME_CHUNK_SIZE - AEAD_MAC_LEN); diff --git a/protocols/v2/framing-sv2/src/lib.rs b/protocols/v2/framing-sv2/src/lib.rs index 64fa5a7fda..6d51cb4e4f 100644 --- a/protocols/v2/framing-sv2/src/lib.rs +++ b/protocols/v2/framing-sv2/src/lib.rs @@ -1,25 +1,31 @@ +//! The SV2 protocol is binary, with fixed message framing. +//! Each message begins with the extension type, message type, and message length (six bytes in total), followed by a variable length message. +//! +//! This crate provides primitives for framing of SV2 binary messages. +//! +//! The message framing is outlined below: +//! +//! | Protocol Type | Byte Length | Description | +//! |----------------|-------------|-------------| +//! | `extension_type` | `U16` | Unique identifier of the extension describing this protocol message.

Most significant bit (i.e.bit `15`, `0`-indexed, aka `channel_msg`) indicates a message which is specific to a channel, whereas if the most significant bit is unset, the message is to be interpreted by the immediate receiving device.

Note that the `channel_msg` bit is ignored in the extension lookup, i.e.an `extension_type` of `0x8ABC` is for the same "extension" as `0x0ABC`.

If the `channel_msg` bit is set, the first four bytes of the payload field is a `U32` representing the `channel_id` this message is destined for (these bytes are repeated in the message framing descriptions below).

Note that for the Job Declaration and Template Distribution Protocols the `channel_msg` bit is always unset. | +//! | `msg_type` | `U8` | Unique identifier of the extension describing this protocol message. | +//! | `msg_length` | `U24` | Length of the protocol message, not including this header. | +//! | `payload` | `BYTES` | Message-specific payload of length `msg_length`. If the MSB in `extension_type` (the `channel_msg` bit) is set the first four bytes are defined as a `U32` `"channel_id"`, though this definition is repeated in the message definitions below and these 4 bytes are included in `msg_length`. | +//! +//! # Features +//! This crate can be built with the following features: +//! - `with_serde`: builds `binary_sv2` and `buffer_sv2` crates with `serde`-based encoding and decoding. +//! - `with_buffer_pool`: uses `buffer_sv2` to provide a more efficient allocation method for `non_std` environments. Please refer to `buffer_sv2` crate docs for more context. + #![no_std] extern crate alloc; -/// -/// Sv2 messages are framed as -/// ```txt -/// extension type: u16 -/// msg type: u8 -/// msg length: u24 -/// payload: [u8; msg length] -/// ``` -/// -/// Sv2 messages can be encapsulated in noise messages, noise messages are framed as: -/// -/// ```txt -/// msg length: u16 -/// payload: [u8; msg length] -/// ``` -/// -/// +/// SV2 framing types pub mod framing2; +/// SV2 framing errors pub mod error; + +/// SV2 framing header pub mod header; pub use error::Error;