Skip to main content

gbp_transport/
lib.rs

1//! TCP transport adapter for the Group Protocol Stack.
2//!
3//! Each on-the-wire message is framed as `u32-LE length || bytes`. This is a
4//! pragmatic stand-in for a QUIC stream; switching to a real QUIC binding
5//! later only requires replacing this crate and the same upper layers
6//! continue to work.
7
8#![deny(missing_docs)]
9
10use gbp::{CodecError, GbpFrame};
11use tokio::io::{AsyncReadExt, AsyncWriteExt};
12use tokio::net::TcpStream;
13
14/// Maximum size of a single on-the-wire message (1 MiB).
15pub const MAX_FRAME: usize = 1 << 20;
16
17/// Errors raised by the transport layer.
18#[derive(Debug, thiserror::Error)]
19pub enum WireError {
20    /// Underlying I/O error.
21    #[error("io: {0}")]
22    Io(#[from] std::io::Error),
23    /// GBP base-layer codec error.
24    #[error("codec: {0}")]
25    Codec(#[from] CodecError),
26    /// Message exceeds [`MAX_FRAME`].
27    #[error("frame too large: {size} bytes (max {max})")]
28    TooLarge {
29        /// Observed size.
30        size: usize,
31        /// Configured limit.
32        max: usize,
33    },
34}
35
36impl WireError {
37    fn too_large(n: usize) -> Self {
38        Self::TooLarge {
39            size: n,
40            max: MAX_FRAME,
41        }
42    }
43}
44
45/// Writes a [`GbpFrame`] using `CBOR + length-prefix` framing.
46pub async fn write_frame(stream: &mut TcpStream, frame: &GbpFrame) -> Result<(), WireError> {
47    let bytes = frame.to_cbor();
48    write_blob(stream, &bytes).await
49}
50
51/// Reads a [`GbpFrame`] using `length-prefix + CBOR` framing.
52pub async fn read_frame(stream: &mut TcpStream) -> Result<GbpFrame, WireError> {
53    let buf = read_blob(stream).await?;
54    Ok(GbpFrame::from_cbor(&buf)?)
55}
56
57/// Writes an opaque length-prefixed blob (e.g. a serialised MLS Welcome or
58/// KeyPackage).
59pub async fn write_blob(stream: &mut TcpStream, data: &[u8]) -> Result<(), WireError> {
60    if data.len() > MAX_FRAME {
61        return Err(WireError::too_large(data.len()));
62    }
63    let len = (data.len() as u32).to_le_bytes();
64    stream.write_all(&len).await?;
65    stream.write_all(data).await?;
66    stream.flush().await?;
67    Ok(())
68}
69
70/// Reads an opaque length-prefixed blob.
71pub async fn read_blob(stream: &mut TcpStream) -> Result<Vec<u8>, WireError> {
72    let mut len_buf = [0u8; 4];
73    stream.read_exact(&mut len_buf).await?;
74    let len = u32::from_le_bytes(len_buf) as usize;
75    if len > MAX_FRAME {
76        return Err(WireError::too_large(len));
77    }
78    let mut buf = vec![0u8; len];
79    stream.read_exact(&mut buf).await?;
80    Ok(buf)
81}