use crate::crypto_suite::CryptoSuite;
use crate::envelope::{Flags, HEADER_SIZE, MAC_SIZE};
use crate::error::Error;
use crate::msg_type::MsgType;
use crate::residency::ResidencyTag;
use crate::version::Version;
pub struct EnvelopeFields<'a> {
pub version: Version,
pub msg_type: MsgType,
pub crypto_suite: CryptoSuite,
pub flags: Flags,
pub sender_device_id: &'a [u8; 32],
pub residency_tag: ResidencyTag,
pub payload: &'a [u8],
pub mac: &'a [u8; 16],
}
pub fn encode_to_slice(fields: &EnvelopeFields<'_>, dst: &mut [u8]) -> Result<usize, Error> {
let payload_len = fields.payload.len();
if payload_len > u32::MAX as usize {
return Err(Error::PayloadTooLarge(payload_len));
}
let total = HEADER_SIZE + payload_len + MAC_SIZE;
if dst.len() < total {
return Err(Error::BufferTooShort {
need: total,
have: dst.len(),
});
}
dst[0] = fields.version.as_byte();
dst[1] = fields.msg_type.as_byte();
dst[2] = fields.crypto_suite.as_byte();
dst[3] = fields.flags.as_byte();
dst[4..36].copy_from_slice(fields.sender_device_id);
let res_bytes = fields.residency_tag.to_be_bytes();
dst[36] = res_bytes[0];
dst[37] = res_bytes[1];
let pl_bytes = (payload_len as u32).to_be_bytes();
dst[38] = pl_bytes[0];
dst[39] = pl_bytes[1];
dst[40] = pl_bytes[2];
dst[41] = pl_bytes[3];
dst[HEADER_SIZE..HEADER_SIZE + payload_len].copy_from_slice(fields.payload);
let mac_start = HEADER_SIZE + payload_len;
dst[mac_start..mac_start + MAC_SIZE].copy_from_slice(fields.mac);
Ok(total)
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn encode_to_vec(envelope: &crate::envelope::Envelope) -> alloc::vec::Vec<u8> {
let total = HEADER_SIZE + envelope.payload.len() + MAC_SIZE;
let mut buf = alloc::vec![0u8; total];
let fields = EnvelopeFields {
version: envelope.version,
msg_type: envelope.msg_type,
crypto_suite: envelope.crypto_suite,
flags: envelope.flags,
sender_device_id: &envelope.sender_device_id,
residency_tag: envelope.residency_tag,
payload: &envelope.payload,
mac: &envelope.mac,
};
encode_to_slice(&fields, &mut buf).expect("pre-allocated buffer is exactly the right size");
buf
}
#[cfg(test)]
mod tests {
use super::*;
fn test_id() -> [u8; 32] {
[0xAB; 32]
}
fn test_mac() -> [u8; 16] {
[0xCD; 16]
}
fn test_fields<'a>(
payload: &'a [u8],
id: &'a [u8; 32],
mac: &'a [u8; 16],
) -> EnvelopeFields<'a> {
EnvelopeFields {
version: Version::CURRENT,
msg_type: MsgType::TaskRoute,
crypto_suite: CryptoSuite::PqHybrid,
flags: Flags::NONE,
sender_device_id: id,
residency_tag: ResidencyTag::INDONESIA,
payload,
mac,
}
}
#[test]
fn encode_empty_payload() {
let id = test_id();
let mac = test_mac();
let fields = test_fields(&[], &id, &mac);
let mut buf = [0u8; 58];
let n = encode_to_slice(&fields, &mut buf).unwrap();
assert_eq!(n, 58); }
#[test]
#[cfg(feature = "alloc")]
fn encode_exact_size_buffer() {
use alloc::vec;
let id = test_id();
let mac = test_mac();
let payload = [0x42u8; 10];
let fields = test_fields(&payload, &id, &mac);
let total = HEADER_SIZE + 10 + MAC_SIZE;
let mut buf = vec![0u8; total];
let n = encode_to_slice(&fields, &mut buf).unwrap();
assert_eq!(n, total);
}
#[test]
fn encode_oversized_buffer_ok() {
let id = test_id();
let mac = test_mac();
let fields = test_fields(&[1, 2, 3], &id, &mac);
let mut buf = [0u8; 1024];
let n = encode_to_slice(&fields, &mut buf).unwrap();
assert_eq!(n, HEADER_SIZE + 3 + MAC_SIZE);
}
#[test]
fn encode_undersized_buffer_err() {
let id = test_id();
let mac = test_mac();
let fields = test_fields(&[0u8; 100], &id, &mac);
let mut buf = [0u8; 50]; assert!(matches!(
encode_to_slice(&fields, &mut buf),
Err(crate::error::Error::BufferTooShort { .. })
));
}
#[test]
fn encode_header_fields_correct() {
let id = test_id();
let mac = test_mac();
let fields = EnvelopeFields {
version: Version::V1,
msg_type: MsgType::SyncCrdt,
crypto_suite: CryptoSuite::Classical,
flags: Flags::from_byte(Flags::COMPRESSED),
sender_device_id: &id,
residency_tag: ResidencyTag::INDONESIA,
payload: &[],
mac: &mac,
};
let mut buf = [0u8; 58];
encode_to_slice(&fields, &mut buf).unwrap();
assert_eq!(buf[0], 0x01); assert_eq!(buf[1], 0x02); assert_eq!(buf[2], 0x02); assert_eq!(buf[3], 0x01); assert_eq!(&buf[4..36], &[0xAB; 32]); assert_eq!(&buf[36..38], &[0x01, 0x68]); assert_eq!(&buf[38..42], &[0, 0, 0, 0]); assert_eq!(&buf[42..58], &[0xCD; 16]); }
#[test]
fn encode_payload_in_correct_position() {
let id = test_id();
let mac = test_mac();
let payload = [0x11, 0x22, 0x33, 0x44];
let fields = test_fields(&payload, &id, &mac);
let mut buf = [0u8; 128];
let n = encode_to_slice(&fields, &mut buf).unwrap();
assert_eq!(&buf[42..46], &[0x11, 0x22, 0x33, 0x44]);
assert_eq!(&buf[46..62], &[0xCD; 16]);
assert_eq!(n, 62);
}
}