enigma-packet 0.1.0

Canonical message packet format + serialization for Enigma secure messaging
Documentation
use crate::error::{EnigmaPacketError, Result};

pub const MAGIC: [u8; 4] = *b"ENP1";
pub const VERSION: u8 = 1;
pub const HEADER_LEN: usize = 12;
pub const MIN_PACKET_SIZE: usize = HEADER_LEN;
pub const MAX_PACKET_SIZE: usize = 16 * 1024 * 1024;
pub const MAX_BODY_SIZE: usize = MAX_PACKET_SIZE - HEADER_LEN;

#[derive(Debug, Clone, Copy)]
pub struct PacketView<'a> {
    pub version: u8,
    pub flags: u8,
    pub reserved: u16,
    pub body_len: u32,
    pub body: &'a [u8],
}

pub fn peek_packet(packet: &[u8]) -> Result<PacketView<'_>> {
    parse_frame(packet)
}

pub(crate) fn encode_frame(body: &[u8], flags: u8) -> Result<Vec<u8>> {
    if body.len() > MAX_BODY_SIZE || body.len() > u32::MAX as usize {
        return Err(EnigmaPacketError::SizeLimitExceeded);
    }
    let total = HEADER_LEN
        .checked_add(body.len())
        .ok_or(EnigmaPacketError::SizeLimitExceeded)?;
    if total > MAX_PACKET_SIZE {
        return Err(EnigmaPacketError::SizeLimitExceeded);
    }
    let mut buf = Vec::with_capacity(total);
    buf.extend_from_slice(&MAGIC);
    buf.push(VERSION);
    buf.push(flags);
    buf.extend_from_slice(&0u16.to_be_bytes());
    buf.extend_from_slice(&(body.len() as u32).to_be_bytes());
    buf.extend_from_slice(body);
    Ok(buf)
}

pub(crate) fn parse_frame(packet: &[u8]) -> Result<PacketView<'_>> {
    if packet.len() < HEADER_LEN {
        return Err(EnigmaPacketError::InvalidPacket("packet too small".into()));
    }
    if packet.len() > MAX_PACKET_SIZE {
        return Err(EnigmaPacketError::SizeLimitExceeded);
    }
    if packet[..4] != MAGIC {
        return Err(EnigmaPacketError::InvalidPacket("invalid magic".into()));
    }
    let version = packet[4];
    if version != VERSION {
        return Err(EnigmaPacketError::UnsupportedVersion(version));
    }
    let flags = packet[5];
    let reserved = u16::from_be_bytes([packet[6], packet[7]]);
    let body_len = u32::from_be_bytes([packet[8], packet[9], packet[10], packet[11]]) as usize;
    let total = HEADER_LEN
        .checked_add(body_len)
        .ok_or(EnigmaPacketError::SizeLimitExceeded)?;
    if total > MAX_PACKET_SIZE {
        return Err(EnigmaPacketError::SizeLimitExceeded);
    }
    if packet.len() != total {
        return Err(EnigmaPacketError::InvalidPacket(
            "body length mismatch".into(),
        ));
    }
    let body = &packet[HEADER_LEN..];
    Ok(PacketView {
        version,
        flags,
        reserved,
        body_len: body_len as u32,
        body,
    })
}